From 5e0b8d508ed51004bd836384293be00950ee62c9 Mon Sep 17 00:00:00 2001 From: Pasha Date: Tue, 20 Feb 2024 18:49:50 +0000 Subject: init gnumach copy --- =announce-1.0 | 40 + =announce-1.1 | 34 + =announce-1.1.1 | 36 + =announce-1.2 | 27 + =announce-1.3 | 47 + AUTHORS | 4 + COPYING | 340 + COPYING3 | 674 + ChangeLog | 8 + DEVELOPMENT | 84 + Makefile.am | 265 + Makefile.in.dep.patch | 19 + Makefrag.am | 611 + Makerules.am | 54 + Makerules.mig.am | 127 + NEWS | 161 + README | 56 + chips/busses.c | 232 + chips/busses.h | 154 + config.status.dep.patch | 18 + configfrag-first.ac | 29 + configfrag.ac | 183 + configure.ac | 270 + ddb/db_access.c | 136 + ddb/db_access.h | 79 + ddb/db_break.c | 746 ++ ddb/db_break.h | 111 + ddb/db_command.c | 594 + ddb/db_command.h | 80 + ddb/db_cond.c | 185 + ddb/db_cond.h | 39 + ddb/db_elf.c | 232 + ddb/db_elf.h | 52 + ddb/db_examine.c | 664 + ddb/db_examine.h | 92 + ddb/db_expr.c | 382 + ddb/db_expr.h | 26 + ddb/db_ext_symtab.c | 123 + ddb/db_input.c | 414 + ddb/db_input.h | 33 + ddb/db_lex.c | 454 + ddb/db_lex.h | 99 + ddb/db_macro.c | 197 + ddb/db_macro.h | 53 + ddb/db_mp.c | 339 + ddb/db_mp.h | 35 + ddb/db_output.c | 217 + ddb/db_output.h | 46 + ddb/db_print.c | 573 + ddb/db_print.h | 68 + ddb/db_run.c | 430 + ddb/db_run.h | 94 + ddb/db_sym.c | 532 + ddb/db_sym.h | 264 + ddb/db_task_thread.c | 326 + ddb/db_task_thread.h | 73 + ddb/db_trap.c | 115 + ddb/db_trap.h | 34 + ddb/db_variables.c | 224 + ddb/db_variables.h | 88 + ddb/db_watch.c | 329 + ddb/db_watch.h | 80 + ddb/db_write_cmd.c | 111 + ddb/db_write_cmd.h | 34 + ddb/nlist.h | 63 + ddb/stab.h | 73 + ddb/tr.h | 117 + device/blkio.c | 66 + device/blkio.h | 26 + device/buf.h | 96 + device/chario.c | 1060 ++ device/chario.h | 37 + device/cirbuf.c | 277 + device/cirbuf.h | 61 + device/conf.h | 127 + device/cons.c | 183 + device/cons.h | 68 + device/dev_hdr.h | 160 + device/dev_lookup.c | 364 + device/dev_master.h | 65 + device/dev_name.c | 242 + device/dev_pager.c | 662 + device/dev_pager.h | 26 + device/device.srv | 27 + device/device_emul.h | 64 + device/device_init.c | 67 + device/device_init.h | 24 + device/device_pager.srv | 43 + device/device_port.h | 41 + device/device_reply.cli | 27 + device/device_types_kernel.h | 43 + device/ds_routines.c | 1901 +++ device/ds_routines.h | 86 + device/if_ether.h | 52 + device/if_hdr.h | 165 + device/intr.c | 372 + device/intr.h | 62 + device/io_req.h | 145 + device/kmsg.c | 254 + device/kmsg.h | 18 + device/memory_object_reply.cli | 27 + device/net_io.c | 2153 ++++ device/net_io.h | 164 + device/param.h | 49 + device/subrs.c | 86 + device/subrs.h | 37 + device/tty.h | 237 + doc/.gitignore | 4 + doc/Makefrag.am | 119 + doc/fdl.texi | 452 + doc/gpl.texi | 383 + doc/mach.texi | 7417 +++++++++++ gensym.awk | 78 + gitlog-to-changelog | 432 + i386/Makefrag.am | 215 + i386/Makefrag_x86.am | 84 + i386/README-Drivers | 122 + i386/configfrag.ac | 124 + i386/i386/.gitignore | 1 + i386/i386/_setjmp.S | 63 + i386/i386/apic.c | 453 + i386/i386/apic.h | 337 + i386/i386/ast.h | 47 + i386/i386/ast_check.c | 56 + i386/i386/ast_types.h | 36 + i386/i386/copy_user.h | 100 + i386/i386/cpu.h | 110 + i386/i386/cpu_number.h | 119 + i386/i386/cpuboot.S | 245 + i386/i386/cswitch.S | 139 + i386/i386/db_disasm.c | 1437 +++ i386/i386/db_interface.c | 865 ++ i386/i386/db_interface.h | 149 + i386/i386/db_machdep.h | 105 + i386/i386/db_trace.c | 586 + i386/i386/db_trace.h | 33 + i386/i386/debug.h | 73 + i386/i386/debug_i386.c | 178 + i386/i386/debug_trace.S | 56 + i386/i386/eflags.h | 35 + i386/i386/fpu.c | 948 ++ i386/i386/fpu.h | 250 + i386/i386/gdt.c | 166 + i386/i386/gdt.h | 121 + i386/i386/hardclock.c | 81 + i386/i386/hardclock.h | 28 + i386/i386/i386asm.sym | 194 + i386/i386/idt-gen.h | 47 + i386/i386/idt.c | 87 + i386/i386/idt_inittab.S | 140 + i386/i386/io_perm.c | 329 + i386/i386/io_perm.h | 63 + i386/i386/ipl.h | 83 + i386/i386/irq.c | 73 + i386/i386/irq.h | 31 + i386/i386/ktss.c | 92 + i386/i386/ktss.h | 33 + i386/i386/kttd_interface.c | 574 + i386/i386/kttd_machdep.h | 59 + i386/i386/ldt.c | 117 + i386/i386/ldt.h | 77 + i386/i386/lock.h | 132 + i386/i386/locore.S | 1603 +++ i386/i386/locore.h | 98 + i386/i386/loose_ends.c | 49 + i386/i386/loose_ends.h | 33 + i386/i386/mach_i386.srv | 27 + i386/i386/mach_param.h | 31 + i386/i386/machine_routines.h | 38 + i386/i386/machine_task.c | 80 + i386/i386/machspl.h | 29 + i386/i386/model_dep.h | 68 + i386/i386/mp_desc.c | 357 + i386/i386/mp_desc.h | 98 + i386/i386/msr.h | 56 + i386/i386/pcb.c | 958 ++ i386/i386/pcb.h | 90 + i386/i386/percpu.c | 33 + i386/i386/percpu.h | 98 + i386/i386/phys.c | 187 + i386/i386/pic.c | 262 + i386/i386/pic.h | 191 + i386/i386/pio.h | 61 + i386/i386/pit.c | 140 + i386/i386/pit.h | 98 + i386/i386/pmap.h | 27 + i386/i386/proc_reg.h | 407 + i386/i386/sched_param.h | 40 + i386/i386/seg.h | 264 + i386/i386/setjmp.h | 44 + i386/i386/smp.c | 199 + i386/i386/smp.h | 34 + i386/i386/spl.S | 264 + i386/i386/spl.h | 78 + i386/i386/strings.c | 96 + i386/i386/task.h | 61 + i386/i386/thread.h | 276 + i386/i386/time_stamp.h | 30 + i386/i386/trap.c | 675 + i386/i386/trap.h | 71 + i386/i386/tss.h | 109 + i386/i386/user_ldt.c | 451 + i386/i386/user_ldt.h | 50 + i386/i386/vm_param.h | 200 + i386/i386/xen.h | 412 + i386/i386/xpr.h | 32 + i386/i386at/acpi_parse_apic.c | 650 + i386/i386at/acpi_parse_apic.h | 201 + i386/i386at/autoconf.c | 149 + i386/i386at/autoconf.h | 43 + i386/i386at/biosmem.c | 1070 ++ i386/i386at/biosmem.h | 109 + i386/i386at/boothdr.S | 179 + i386/i386at/com.c | 900 ++ i386/i386at/com.h | 86 + i386/i386at/comreg.h | 139 + i386/i386at/conf.c | 172 + i386/i386at/cons_conf.c | 63 + i386/i386at/cram.h | 86 + i386/i386at/disk.h | 89 + i386/i386at/elf.h | 61 + i386/i386at/i8250.h | 134 + i386/i386at/idt.h | 53 + i386/i386at/immc.c | 134 + i386/i386at/immc.h | 31 + i386/i386at/int_init.c | 82 + i386/i386at/int_init.h | 35 + i386/i386at/interrupt.S | 142 + i386/i386at/ioapic.c | 463 + i386/i386at/kd.c | 3059 +++++ i386/i386at/kd.h | 744 ++ i386/i386at/kd_event.c | 392 + i386/i386at/kd_event.h | 62 + i386/i386at/kd_mouse.c | 800 ++ i386/i386at/kd_mouse.h | 72 + i386/i386at/kd_queue.c | 109 + i386/i386at/kd_queue.h | 86 + i386/i386at/kdasm.S | 145 + i386/i386at/kdsoft.h | 209 + i386/i386at/lpr.c | 285 + i386/i386at/lpr.h | 66 + i386/i386at/mem.c | 42 + i386/i386at/mem.h | 24 + i386/i386at/model_dep.c | 674 + i386/i386at/model_dep.h | 39 + i386/i386at/pic_isa.c | 56 + i386/i386at/rtc.c | 242 + i386/i386at/rtc.h | 143 + i386/include/mach/i386/asm.h | 146 + i386/include/mach/i386/boolean.h | 37 + i386/include/mach/i386/eflags.h | 53 + i386/include/mach/i386/exception.h | 85 + i386/include/mach/i386/exec/elf.h | 53 + i386/include/mach/i386/fp_reg.h | 140 + i386/include/mach/i386/ioccom.h | 32 + i386/include/mach/i386/kern_return.h | 40 + i386/include/mach/i386/mach_i386.defs | 113 + i386/include/mach/i386/mach_i386_types.h | 57 + i386/include/mach/i386/machine_types.defs | 107 + i386/include/mach/i386/multiboot.h | 208 + i386/include/mach/i386/syscall_sw.h | 39 + i386/include/mach/i386/thread_status.h | 190 + i386/include/mach/i386/trap.h | 60 + i386/include/mach/i386/vm_param.h | 90 + i386/include/mach/i386/vm_types.h | 173 + i386/include/mach/sa/stdarg.h | 58 + i386/intel/pmap.c | 3325 +++++ i386/intel/pmap.h | 574 + i386/intel/read_fault.c | 178 + i386/intel/read_fault.h | 35 + i386/ldscript | 201 + i386/linux/Makefrag.am | 25 + i386/linux/dev/include/linux/autoconf.h | 284 + i386/xen/Makefrag.am | 34 + i386/xen/xen.c | 69 + i386/xen/xen_boothdr.S | 208 + i386/xen/xen_locore.S | 110 + include/alloca.h | 25 + include/cache.h | 25 + include/device/audio_status.h | 164 + include/device/bpf.h | 244 + include/device/device.defs | 183 + include/device/device_reply.defs | 110 + include/device/device_request.defs | 95 + include/device/device_types.defs | 92 + include/device/device_types.h | 148 + include/device/disk_status.h | 318 + include/device/input.h | 106 + include/device/net_status.h | 201 + include/device/notify.defs | 36 + include/device/notify.h | 34 + include/device/tape_status.h | 140 + include/device/tty_status.h | 134 + include/inttypes.h | 64 + include/mach/alert.h | 37 + include/mach/boolean.h | 63 + include/mach/boot.h | 93 + include/mach/default_pager.defs | 65 + include/mach/default_pager_types.defs | 53 + include/mach/default_pager_types.h | 59 + include/mach/error.h | 93 + include/mach/exc.defs | 47 + include/mach/exception.h | 58 + include/mach/exec/a.out.h | 68 + include/mach/exec/elf.h | 364 + include/mach/exec/exec.h | 130 + include/mach/experimental.defs | 15 + include/mach/gnumach.defs | 217 + include/mach/host_info.h | 90 + include/mach/inline.h | 27 + include/mach/kern_return.h | 166 + include/mach/mach.defs | 724 ++ include/mach/mach4.defs | 131 + include/mach/mach_host.defs | 388 + include/mach/mach_param.h | 39 + include/mach/mach_port.defs | 360 + include/mach/mach_traps.h | 43 + include/mach/mach_types.defs | 299 + include/mach/mach_types.h | 90 + include/mach/machine.h | 268 + include/mach/macro_help.h | 18 + include/mach/memory_object.defs | 333 + include/mach/memory_object.h | 90 + include/mach/memory_object_default.defs | 118 + include/mach/message.h | 540 + include/mach/mig_errors.h | 89 + include/mach/mig_support.h | 57 + include/mach/notify.defs | 112 + include/mach/notify.h | 92 + include/mach/pc_sample.h | 66 + include/mach/policy.h | 45 + include/mach/port.h | 159 + include/mach/processor_info.h | 104 + include/mach/profil.h | 212 + include/mach/profilparam.h | 62 + include/mach/std_types.defs | 101 + include/mach/std_types.h | 44 + include/mach/syscall_sw.h | 121 + include/mach/task_info.h | 126 + include/mach/task_notify.defs | 59 + include/mach/task_special_ports.h | 66 + include/mach/thread_info.h | 124 + include/mach/thread_special_ports.h | 59 + include/mach/thread_status.h | 55 + include/mach/thread_switch.h | 40 + include/mach/time_value.h | 201 + include/mach/version.h | 73 + include/mach/vm_attributes.h | 63 + include/mach/vm_cache_statistics.h | 41 + include/mach/vm_inherit.h | 55 + include/mach/vm_param.h | 102 + include/mach/vm_prot.h | 79 + include/mach/vm_statistics.h | 75 + include/mach/vm_sync.h | 45 + include/mach/vm_wire.h | 30 + include/mach/xen.h | 95 + include/mach_debug/hash_info.h | 41 + include/mach_debug/mach_debug.defs | 228 + include/mach_debug/mach_debug_types.defs | 121 + include/mach_debug/mach_debug_types.h | 52 + include/mach_debug/slab_info.h | 56 + include/mach_debug/vm_info.h | 143 + include/string.h | 55 + include/sys/reboot.h | 135 + include/sys/types.h | 88 + ipc/.gitignore | 2 + ipc/ipc_entry.c | 214 + ipc/ipc_entry.h | 110 + ipc/ipc_init.c | 117 + ipc/ipc_init.h | 50 + ipc/ipc_kmsg.c | 2904 +++++ ipc/ipc_kmsg.h | 345 + ipc/ipc_kmsg_queue.h | 31 + ipc/ipc_machdep.h | 39 + ipc/ipc_marequest.c | 437 + ipc/ipc_marequest.h | 99 + ipc/ipc_mqueue.c | 695 ++ ipc/ipc_mqueue.h | 112 + ipc/ipc_notify.c | 449 + ipc/ipc_notify.h | 58 + ipc/ipc_object.c | 969 ++ ipc/ipc_object.h | 169 + ipc/ipc_port.c | 1290 ++ ipc/ipc_port.h | 354 + ipc/ipc_print.h | 39 + ipc/ipc_pset.c | 350 + ipc/ipc_pset.h | 92 + ipc/ipc_right.c | 2115 ++++ ipc/ipc_right.h | 112 + ipc/ipc_space.c | 215 + ipc/ipc_space.h | 324 + ipc/ipc_table.c | 135 + ipc/ipc_table.h | 101 + ipc/ipc_target.c | 78 + ipc/ipc_target.h | 67 + ipc/ipc_thread.c | 107 + ipc/ipc_thread.h | 129 + ipc/ipc_types.h | 31 + ipc/mach_debug.c | 288 + ipc/mach_msg.c | 1709 +++ ipc/mach_msg.h | 60 + ipc/mach_port.c | 1578 +++ ipc/mach_port.h | 37 + ipc/mach_port.srv | 27 + ipc/notify.defs | 22 + ipc/port.h | 106 + kern/.gitignore | 2 + kern/act.c | 1118 ++ kern/act.h | 192 + kern/assert.h | 54 + kern/ast.c | 235 + kern/ast.h | 139 + kern/atomic.h | 54 + kern/boot_script.c | 791 ++ kern/boot_script.h | 111 + kern/bootstrap.c | 918 ++ kern/bootstrap.h | 26 + kern/counters.c | 82 + kern/counters.h | 107 + kern/cpu_number.h | 47 + kern/debug.c | 207 + kern/debug.h | 72 + kern/elf-load.c | 97 + kern/eventcount.c | 361 + kern/eventcount.h | 66 + kern/exc.defs | 22 + kern/exception.c | 1023 ++ kern/exception.h | 66 + kern/experimental.srv | 3 + kern/gnumach.srv | 23 + kern/gsync.c | 517 + kern/gsync.h | 41 + kern/host.c | 389 + kern/host.h | 48 + kern/ipc_host.c | 451 + kern/ipc_host.h | 72 + kern/ipc_kobject.c | 365 + kern/ipc_kobject.h | 123 + kern/ipc_mig.c | 984 ++ kern/ipc_mig.h | 143 + kern/ipc_sched.c | 283 + kern/ipc_sched.h | 32 + kern/ipc_tt.c | 1113 ++ kern/ipc_tt.h | 92 + kern/kalloc.h | 38 + kern/kern_types.h | 70 + kern/kmutex.c | 76 + kern/kmutex.h | 52 + kern/list.h | 357 + kern/lock.c | 689 ++ kern/lock.h | 316 + kern/lock_mon.c | 364 + kern/log2.h | 50 + kern/mach.srv | 40 + kern/mach4.srv | 32 + kern/mach_clock.c | 657 + kern/mach_clock.h | 112 + kern/mach_debug.srv | 26 + kern/mach_factor.c | 150 + kern/mach_factor.h | 31 + kern/mach_host.srv | 37 + kern/machine.c | 672 + kern/machine.h | 59 + kern/macros.h | 93 + kern/pc_sample.c | 306 + kern/pc_sample.h | 94 + kern/printf.c | 656 + kern/printf.h | 68 + kern/priority.c | 223 + kern/priority.h | 28 + kern/processor.c | 1034 ++ kern/processor.h | 326 + kern/profile.c | 408 + kern/queue.c | 121 + kern/queue.h | 391 + kern/rbtree.c | 483 + kern/rbtree.h | 306 + kern/rbtree_i.h | 186 + kern/rdxtree.c | 830 ++ kern/rdxtree.h | 209 + kern/rdxtree_i.h | 74 + kern/refcount.h | 68 + kern/sched.h | 186 + kern/sched_prim.c | 2059 ++++ kern/sched_prim.h | 189 + kern/shuttle.h | 71 + kern/slab.c | 1686 +++ kern/slab.h | 243 + kern/smp.c | 49 + kern/smp.h | 24 + kern/startup.c | 316 + kern/startup.h | 28 + kern/strings.c | 275 + kern/syscall_emulation.c | 453 + kern/syscall_emulation.h | 67 + kern/syscall_subr.c | 386 + kern/syscall_subr.h | 42 + kern/syscall_sw.c | 224 + kern/syscall_sw.h | 57 + kern/task.c | 1351 ++ kern/task.h | 197 + kern/task_notify.cli | 7 + kern/thread.c | 2646 ++++ kern/thread.h | 437 + kern/thread_swap.c | 200 + kern/thread_swap.h | 43 + kern/timer.c | 501 + kern/timer.h | 195 + kern/xpr.c | 197 + kern/xpr.h | 97 + linux/Makefrag.am | 788 ++ linux/configfrag.ac | 664 + linux/dev/README | 8 + linux/dev/arch/i386/kernel/irq.c | 775 ++ linux/dev/arch/i386/kernel/setup.c | 13 + linux/dev/drivers/block/ahci.c | 1038 ++ linux/dev/drivers/block/floppy.c | 4288 +++++++ linux/dev/drivers/block/genhd.c | 1080 ++ linux/dev/drivers/net/Space.c | 582 + linux/dev/drivers/net/auto_irq.c | 123 + linux/dev/drivers/net/net_init.c | 446 + linux/dev/drivers/net/wavelan.p.h | 639 + linux/dev/drivers/scsi/eata_dma.c | 1607 +++ linux/dev/drivers/scsi/g_NCR5380.c | 735 ++ linux/dev/glue/block.c | 1770 +++ linux/dev/glue/glue.h | 42 + linux/dev/glue/kmem.c | 589 + linux/dev/glue/misc.c | 248 + linux/dev/glue/net.c | 670 + linux/dev/include/ahci.h | 268 + linux/dev/include/asm-i386/page.h | 59 + linux/dev/include/asm-i386/smp.h | 8 + linux/dev/include/asm-i386/string.h | 487 + linux/dev/include/asm-i386/system.h | 356 + linux/dev/include/asm-i386/uaccess.h | 1 + linux/dev/include/linux/blk.h | 471 + linux/dev/include/linux/blkdev.h | 73 + linux/dev/include/linux/compile.h | 6 + linux/dev/include/linux/etherdevice.h | 62 + linux/dev/include/linux/fs.h | 803 ++ linux/dev/include/linux/genhd.h | 208 + linux/dev/include/linux/if.h | 184 + linux/dev/include/linux/kernel.h | 107 + linux/dev/include/linux/locks.h | 66 + linux/dev/include/linux/malloc.h | 18 + linux/dev/include/linux/mm.h | 378 + linux/dev/include/linux/modversions.h | 1 + linux/dev/include/linux/netdevice.h | 339 + linux/dev/include/linux/notifier.h | 96 + linux/dev/include/linux/pagemap.h | 150 + linux/dev/include/linux/pm.h | 1 + linux/dev/include/linux/proc_fs.h | 292 + linux/dev/include/linux/sched.h | 521 + linux/dev/include/linux/skbuff.h | 466 + linux/dev/include/linux/threads.h | 1 + linux/dev/include/linux/types.h | 117 + linux/dev/init/main.c | 261 + linux/dev/init/version.c | 32 + linux/dev/kernel/dma.c | 109 + linux/dev/kernel/printk.c | 83 + linux/dev/kernel/resource.c | 145 + linux/dev/kernel/sched.c | 630 + linux/dev/kernel/softirq.c | 48 + linux/dev/lib/vsprintf.c | 354 + linux/dev/net/core/dev.c | 1648 +++ linux/pcmcia-cs/clients/3c574_cs.c | 1349 ++ linux/pcmcia-cs/clients/3c589_cs.c | 1107 ++ linux/pcmcia-cs/clients/ax8390.h | 165 + linux/pcmcia-cs/clients/axnet_cs.c | 1936 +++ linux/pcmcia-cs/clients/fmvj18x_cs.c | 1322 ++ linux/pcmcia-cs/clients/nmclan_cs.c | 1744 +++ linux/pcmcia-cs/clients/ositech.h | 358 + linux/pcmcia-cs/clients/pcnet_cs.c | 1702 +++ linux/pcmcia-cs/clients/smc91c92_cs.c | 2135 ++++ linux/pcmcia-cs/clients/xirc2ps_cs.c | 2091 ++++ linux/pcmcia-cs/glue/ds.c | 454 + linux/pcmcia-cs/glue/pcmcia.c | 121 + linux/pcmcia-cs/glue/pcmcia_glue.h | 264 + linux/pcmcia-cs/glue/wireless_glue.h | 158 + linux/pcmcia-cs/include/linux/crc32.h | 49 + linux/pcmcia-cs/include/linux/slab.h | 12 + linux/pcmcia-cs/include/pcmcia/bulkmem.h | 195 + linux/pcmcia-cs/include/pcmcia/bus_ops.h | 157 + linux/pcmcia-cs/include/pcmcia/ciscode.h | 138 + linux/pcmcia-cs/include/pcmcia/cisreg.h | 135 + linux/pcmcia-cs/include/pcmcia/cistpl.h | 604 + linux/pcmcia-cs/include/pcmcia/cs.h | 441 + linux/pcmcia-cs/include/pcmcia/cs_types.h | 70 + linux/pcmcia-cs/include/pcmcia/driver_ops.h | 73 + linux/pcmcia-cs/include/pcmcia/ds.h | 148 + linux/pcmcia-cs/include/pcmcia/mem_op.h | 133 + linux/pcmcia-cs/include/pcmcia/ss.h | 133 + linux/pcmcia-cs/include/pcmcia/version.h | 9 + linux/pcmcia-cs/modules/bulkmem.c | 626 + linux/pcmcia-cs/modules/cirrus.h | 188 + linux/pcmcia-cs/modules/cistpl.c | 1502 +++ linux/pcmcia-cs/modules/cs.c | 2399 ++++ linux/pcmcia-cs/modules/cs_internal.h | 300 + linux/pcmcia-cs/modules/ds.c | 1039 ++ linux/pcmcia-cs/modules/ene.h | 59 + linux/pcmcia-cs/modules/i82365.c | 2588 ++++ linux/pcmcia-cs/modules/i82365.h | 154 + linux/pcmcia-cs/modules/o2micro.h | 160 + linux/pcmcia-cs/modules/pci_fixup.c | 677 ++ linux/pcmcia-cs/modules/ricoh.h | 161 + linux/pcmcia-cs/modules/rsrc_mgr.c | 877 ++ linux/pcmcia-cs/modules/smc34c90.h | 58 + linux/pcmcia-cs/modules/ti113x.h | 264 + linux/pcmcia-cs/modules/topic.h | 123 + linux/pcmcia-cs/modules/vg468.h | 112 + linux/pcmcia-cs/modules/yenta.h | 156 + linux/pcmcia-cs/wireless/hermes.c | 552 + linux/pcmcia-cs/wireless/hermes.h | 456 + linux/pcmcia-cs/wireless/hermes_rid.h | 153 + linux/pcmcia-cs/wireless/ieee802_11.h | 79 + linux/pcmcia-cs/wireless/orinoco.c | 4230 +++++++ linux/pcmcia-cs/wireless/orinoco.h | 166 + linux/pcmcia-cs/wireless/orinoco_cs.c | 705 ++ linux/src/COPYING | 351 + linux/src/arch/i386/kernel/bios32.c | 916 ++ linux/src/arch/i386/kernel/irq.c | 582 + linux/src/arch/i386/lib/delay.c | 45 + linux/src/arch/i386/lib/semaphore.S | 35 + linux/src/drivers/block/cmd640.c | 850 ++ linux/src/drivers/block/floppy.c | 4284 +++++++ linux/src/drivers/block/genhd.c | 761 ++ linux/src/drivers/block/ide-cd.c | 2802 +++++ linux/src/drivers/block/ide.c | 3926 ++++++ linux/src/drivers/block/ide.h | 750 ++ linux/src/drivers/block/ide_modes.h | 226 + linux/src/drivers/block/rz1000.c | 59 + linux/src/drivers/block/triton.c | 996 ++ linux/src/drivers/net/3c501.c | 856 ++ linux/src/drivers/net/3c503.c | 690 ++ linux/src/drivers/net/3c503.h | 91 + linux/src/drivers/net/3c505.c | 1732 +++ linux/src/drivers/net/3c505.h | 245 + linux/src/drivers/net/3c507.c | 924 ++ linux/src/drivers/net/3c509.c | 842 ++ linux/src/drivers/net/3c515.c | 1501 +++ linux/src/drivers/net/3c59x.c | 2648 ++++ linux/src/drivers/net/8390.c | 829 ++ linux/src/drivers/net/8390.h | 175 + linux/src/drivers/net/Space.c | 541 + linux/src/drivers/net/ac3200.c | 385 + linux/src/drivers/net/apricot.c | 1046 ++ linux/src/drivers/net/at1700.c | 756 ++ linux/src/drivers/net/atp.c | 977 ++ linux/src/drivers/net/atp.h | 274 + linux/src/drivers/net/auto_irq.c | 123 + linux/src/drivers/net/cb_shim.c | 296 + linux/src/drivers/net/de4x5.c | 5942 +++++++++ linux/src/drivers/net/de4x5.h | 1028 ++ linux/src/drivers/net/de600.c | 853 ++ linux/src/drivers/net/de620.c | 1045 ++ linux/src/drivers/net/de620.h | 117 + linux/src/drivers/net/depca.c | 1890 +++ linux/src/drivers/net/depca.h | 185 + linux/src/drivers/net/e2100.c | 456 + linux/src/drivers/net/eepro.c | 1407 +++ linux/src/drivers/net/eepro100.c | 2155 ++++ linux/src/drivers/net/eexpress.c | 1285 ++ linux/src/drivers/net/epic100.c | 1560 +++ linux/src/drivers/net/eth16i.c | 1604 +++ linux/src/drivers/net/eth82586.h | 172 + linux/src/drivers/net/ewrk3.c | 1920 +++ linux/src/drivers/net/ewrk3.h | 322 + linux/src/drivers/net/fmv18x.c | 664 + linux/src/drivers/net/hamachi.c | 1315 ++ linux/src/drivers/net/hp-plus.c | 483 + linux/src/drivers/net/hp.c | 451 + linux/src/drivers/net/hp100.c | 3121 +++++ linux/src/drivers/net/hp100.h | 626 + linux/src/drivers/net/i82586.h | 413 + linux/src/drivers/net/intel-gige.c | 1450 +++ linux/src/drivers/net/iow.h | 6 + linux/src/drivers/net/kern_compat.h | 285 + linux/src/drivers/net/lance.c | 1293 ++ linux/src/drivers/net/myson803.c | 1650 +++ linux/src/drivers/net/natsemi.c | 1448 +++ linux/src/drivers/net/ne.c | 812 ++ linux/src/drivers/net/ne2k-pci.c | 647 + linux/src/drivers/net/net_init.c | 439 + linux/src/drivers/net/ni52.c | 1387 +++ linux/src/drivers/net/ni52.h | 310 + linux/src/drivers/net/ni65.c | 1228 ++ linux/src/drivers/net/ni65.h | 130 + linux/src/drivers/net/ns820.c | 1547 +++ linux/src/drivers/net/pci-scan.c | 659 + linux/src/drivers/net/pci-scan.h | 90 + linux/src/drivers/net/pcnet32.c | 970 ++ linux/src/drivers/net/rtl8139.c | 1737 +++ linux/src/drivers/net/seeq8005.c | 760 ++ linux/src/drivers/net/seeq8005.h | 156 + linux/src/drivers/net/sis900.c | 1803 +++ linux/src/drivers/net/sis900.h | 284 + linux/src/drivers/net/sk_g16.c | 2110 ++++ linux/src/drivers/net/sk_g16.h | 164 + linux/src/drivers/net/smc-ultra.c | 496 + linux/src/drivers/net/smc-ultra32.c | 413 + linux/src/drivers/net/smc9194.c | 1779 +++ linux/src/drivers/net/smc9194.h | 240 + linux/src/drivers/net/starfire.c | 1535 +++ linux/src/drivers/net/sundance.c | 1556 +++ linux/src/drivers/net/tlan.c | 2863 +++++ linux/src/drivers/net/tlan.h | 525 + linux/src/drivers/net/tulip.c | 3685 ++++++ linux/src/drivers/net/via-rhine.c | 1427 +++ linux/src/drivers/net/wavelan.c | 4373 +++++++ linux/src/drivers/net/wavelan.h | 346 + linux/src/drivers/net/wavelan.p.h | 635 + linux/src/drivers/net/wd.c | 513 + linux/src/drivers/net/winbond-840.c | 1558 +++ linux/src/drivers/net/yellowfin.c | 1482 +++ linux/src/drivers/net/znet.c | 746 ++ linux/src/drivers/pci/pci.c | 1322 ++ linux/src/drivers/scsi/53c7,8xx.h | 1584 +++ linux/src/drivers/scsi/53c78xx.c | 6401 ++++++++++ linux/src/drivers/scsi/53c8xx_d.h | 2677 ++++ linux/src/drivers/scsi/53c8xx_u.h | 97 + linux/src/drivers/scsi/AM53C974.c | 2270 ++++ linux/src/drivers/scsi/AM53C974.h | 409 + linux/src/drivers/scsi/BusLogic.c | 5003 ++++++++ linux/src/drivers/scsi/BusLogic.h | 1775 +++ linux/src/drivers/scsi/FlashPoint.c | 12156 ++++++++++++++++++ linux/src/drivers/scsi/NCR5380.c | 3246 +++++ linux/src/drivers/scsi/NCR5380.h | 369 + linux/src/drivers/scsi/NCR53c406a.c | 1079 ++ linux/src/drivers/scsi/NCR53c406a.h | 83 + linux/src/drivers/scsi/advansys.c | 15554 ++++++++++++++++++++++++ linux/src/drivers/scsi/advansys.h | 174 + linux/src/drivers/scsi/aha152x.c | 3280 +++++ linux/src/drivers/scsi/aha152x.h | 357 + linux/src/drivers/scsi/aha1542.c | 1325 ++ linux/src/drivers/scsi/aha1542.h | 170 + linux/src/drivers/scsi/aha1740.c | 614 + linux/src/drivers/scsi/aha1740.h | 196 + linux/src/drivers/scsi/aic7xxx.c | 11404 +++++++++++++++++ linux/src/drivers/scsi/aic7xxx.h | 114 + linux/src/drivers/scsi/aic7xxx/scsi_message.h | 41 + linux/src/drivers/scsi/aic7xxx/sequencer.h | 135 + linux/src/drivers/scsi/aic7xxx_proc.c | 384 + linux/src/drivers/scsi/aic7xxx_reg.h | 587 + linux/src/drivers/scsi/aic7xxx_seq.c | 769 ++ linux/src/drivers/scsi/constants.c | 683 ++ linux/src/drivers/scsi/constants.h | 6 + linux/src/drivers/scsi/dc390.h | 147 + linux/src/drivers/scsi/dtc.c | 400 + linux/src/drivers/scsi/dtc.h | 169 + linux/src/drivers/scsi/eata.c | 2331 ++++ linux/src/drivers/scsi/eata.h | 60 + linux/src/drivers/scsi/eata_dma.c | 1603 +++ linux/src/drivers/scsi/eata_dma.h | 128 + linux/src/drivers/scsi/eata_dma_proc.c | 493 + linux/src/drivers/scsi/eata_dma_proc.h | 260 + linux/src/drivers/scsi/eata_generic.h | 414 + linux/src/drivers/scsi/eata_pio.c | 1042 ++ linux/src/drivers/scsi/eata_pio.h | 116 + linux/src/drivers/scsi/eata_pio_proc.c | 135 + linux/src/drivers/scsi/fdomain.c | 2082 ++++ linux/src/drivers/scsi/fdomain.h | 61 + linux/src/drivers/scsi/g_NCR5380.c | 729 ++ linux/src/drivers/scsi/g_NCR5380.h | 162 + linux/src/drivers/scsi/gdth.c | 3598 ++++++ linux/src/drivers/scsi/gdth.h | 819 ++ linux/src/drivers/scsi/gdth_ioctl.h | 86 + linux/src/drivers/scsi/gdth_proc.c | 656 + linux/src/drivers/scsi/gdth_proc.h | 24 + linux/src/drivers/scsi/hosts.c | 554 + linux/src/drivers/scsi/hosts.h | 405 + linux/src/drivers/scsi/in2000.c | 2379 ++++ linux/src/drivers/scsi/in2000.h | 465 + linux/src/drivers/scsi/ncr53c8xx.c | 10795 ++++++++++++++++ linux/src/drivers/scsi/ncr53c8xx.h | 1220 ++ linux/src/drivers/scsi/pas16.c | 576 + linux/src/drivers/scsi/pas16.h | 196 + linux/src/drivers/scsi/ppa.c | 1464 +++ linux/src/drivers/scsi/ppa.h | 176 + linux/src/drivers/scsi/qlogicfas.c | 679 ++ linux/src/drivers/scsi/qlogicfas.h | 43 + linux/src/drivers/scsi/qlogicisp.c | 1767 +++ linux/src/drivers/scsi/qlogicisp.h | 98 + linux/src/drivers/scsi/scripts.h | 1357 +++ linux/src/drivers/scsi/scsi.c | 3585 ++++++ linux/src/drivers/scsi/scsi.h | 650 + linux/src/drivers/scsi/scsi_ioctl.c | 452 + linux/src/drivers/scsi/scsi_proc.c | 302 + linux/src/drivers/scsi/scsicam.c | 229 + linux/src/drivers/scsi/scsiio.c | 1537 +++ linux/src/drivers/scsi/scsiiom.c | 1540 +++ linux/src/drivers/scsi/sd.c | 1691 +++ linux/src/drivers/scsi/sd.h | 65 + linux/src/drivers/scsi/sd_ioctl.c | 128 + linux/src/drivers/scsi/seagate.c | 1679 +++ linux/src/drivers/scsi/seagate.h | 139 + linux/src/drivers/scsi/sr.c | 1290 ++ linux/src/drivers/scsi/sr.h | 40 + linux/src/drivers/scsi/sr_ioctl.c | 607 + linux/src/drivers/scsi/sym53c8xx.c | 14696 ++++++++++++++++++++++ linux/src/drivers/scsi/sym53c8xx.h | 116 + linux/src/drivers/scsi/sym53c8xx_comm.h | 2717 +++++ linux/src/drivers/scsi/sym53c8xx_defs.h | 1767 +++ linux/src/drivers/scsi/t128.c | 400 + linux/src/drivers/scsi/t128.h | 169 + linux/src/drivers/scsi/tmscsim.c | 1930 +++ linux/src/drivers/scsi/tmscsim.h | 680 ++ linux/src/drivers/scsi/u14-34f.c | 1996 +++ linux/src/drivers/scsi/u14-34f.h | 60 + linux/src/drivers/scsi/ultrastor.c | 1165 ++ linux/src/drivers/scsi/ultrastor.h | 102 + linux/src/drivers/scsi/wd7000.c | 1452 +++ linux/src/drivers/scsi/wd7000.h | 446 + linux/src/include/asm-i386/atomic.h | 69 + linux/src/include/asm-i386/bitops.h | 201 + linux/src/include/asm-i386/byteorder.h | 90 + linux/src/include/asm-i386/cache.h | 18 + linux/src/include/asm-i386/checksum.h | 121 + linux/src/include/asm-i386/delay.h | 18 + linux/src/include/asm-i386/dma.h | 271 + linux/src/include/asm-i386/errno.h | 132 + linux/src/include/asm-i386/fcntl.h | 59 + linux/src/include/asm-i386/floppy.h | 289 + linux/src/include/asm-i386/hardirq.h | 66 + linux/src/include/asm-i386/io.h | 216 + linux/src/include/asm-i386/ioctl.h | 75 + linux/src/include/asm-i386/ioctls.h | 74 + linux/src/include/asm-i386/irq.h | 421 + linux/src/include/asm-i386/math_emu.h | 57 + linux/src/include/asm-i386/page.h | 62 + linux/src/include/asm-i386/param.h | 20 + linux/src/include/asm-i386/posix_types.h | 63 + linux/src/include/asm-i386/processor.h | 204 + linux/src/include/asm-i386/ptrace.h | 60 + linux/src/include/asm-i386/resource.h | 39 + linux/src/include/asm-i386/segment.h | 380 + linux/src/include/asm-i386/semaphore.h | 133 + linux/src/include/asm-i386/sigcontext.h | 54 + linux/src/include/asm-i386/signal.h | 97 + linux/src/include/asm-i386/socket.h | 27 + linux/src/include/asm-i386/sockios.h | 12 + linux/src/include/asm-i386/spinlock.h | 262 + linux/src/include/asm-i386/stat.h | 41 + linux/src/include/asm-i386/statfs.h | 25 + linux/src/include/asm-i386/string.h | 487 + linux/src/include/asm-i386/system.h | 334 + linux/src/include/asm-i386/termbits.h | 160 + linux/src/include/asm-i386/termios.h | 92 + linux/src/include/asm-i386/types.h | 46 + linux/src/include/asm-i386/unaligned.h | 16 + linux/src/include/asm-i386/unistd.h | 328 + linux/src/include/asm-i386/vm86.h | 175 + linux/src/include/linux/affs_hardblocks.h | 66 + linux/src/include/linux/atalk.h | 157 + linux/src/include/linux/ax25.h | 96 + linux/src/include/linux/binfmts.h | 65 + linux/src/include/linux/bios32.h | 61 + linux/src/include/linux/blk.h | 454 + linux/src/include/linux/blkdev.h | 66 + linux/src/include/linux/cdrom.h | 453 + linux/src/include/linux/compatmac.h | 153 + linux/src/include/linux/compiler-gcc.h | 112 + linux/src/include/linux/compiler-gcc3.h | 23 + linux/src/include/linux/compiler-gcc4.h | 57 + linux/src/include/linux/compiler-gcc5.h | 67 + linux/src/include/linux/compiler.h | 315 + linux/src/include/linux/config.h | 43 + linux/src/include/linux/ctype.h | 64 + linux/src/include/linux/delay.h | 14 + linux/src/include/linux/errno.h | 16 + linux/src/include/linux/etherdevice.h | 46 + linux/src/include/linux/fcntl.h | 6 + linux/src/include/linux/fd.h | 377 + linux/src/include/linux/fddidevice.h | 42 + linux/src/include/linux/fdreg.h | 143 + linux/src/include/linux/fs.h | 728 ++ linux/src/include/linux/genhd.h | 136 + linux/src/include/linux/hdreg.h | 240 + linux/src/include/linux/head.h | 20 + linux/src/include/linux/icmp.h | 85 + linux/src/include/linux/if.h | 155 + linux/src/include/linux/if_arp.h | 130 + linux/src/include/linux/if_ether.h | 119 + linux/src/include/linux/if_fddi.h | 202 + linux/src/include/linux/if_tr.h | 102 + linux/src/include/linux/igmp.h | 119 + linux/src/include/linux/in.h | 149 + linux/src/include/linux/inet.h | 52 + linux/src/include/linux/init.h | 30 + linux/src/include/linux/interrupt.h | 120 + linux/src/include/linux/ioctl.h | 7 + linux/src/include/linux/ioport.h | 31 + linux/src/include/linux/ip.h | 112 + linux/src/include/linux/ipc.h | 67 + linux/src/include/linux/ipx.h | 80 + linux/src/include/linux/kcomp.h | 52 + linux/src/include/linux/kdev_t.h | 114 + linux/src/include/linux/kernel.h | 97 + linux/src/include/linux/kernel_stat.h | 32 + linux/src/include/linux/limits.h | 17 + linux/src/include/linux/linkage.h | 59 + linux/src/include/linux/list.h | 112 + linux/src/include/linux/locks.h | 65 + linux/src/include/linux/major.h | 88 + linux/src/include/linux/malloc.h | 11 + linux/src/include/linux/mc146818rtc.h | 149 + linux/src/include/linux/md.h | 275 + linux/src/include/linux/mm.h | 375 + linux/src/include/linux/module.h | 116 + linux/src/include/linux/mount.h | 30 + linux/src/include/linux/net.h | 130 + linux/src/include/linux/netdevice.h | 313 + linux/src/include/linux/netrom.h | 34 + linux/src/include/linux/notifier.h | 96 + linux/src/include/linux/pagemap.h | 146 + linux/src/include/linux/param.h | 6 + linux/src/include/linux/pci.h | 1116 ++ linux/src/include/linux/personality.h | 55 + linux/src/include/linux/posix_types.h | 50 + linux/src/include/linux/proc_fs.h | 292 + linux/src/include/linux/ptrace.h | 26 + linux/src/include/linux/quota.h | 221 + linux/src/include/linux/random.h | 70 + linux/src/include/linux/resource.h | 60 + linux/src/include/linux/rose.h | 88 + linux/src/include/linux/route.h | 79 + linux/src/include/linux/sched.h | 496 + linux/src/include/linux/sem.h | 112 + linux/src/include/linux/signal.h | 6 + linux/src/include/linux/skbuff.h | 467 + linux/src/include/linux/smp.h | 54 + linux/src/include/linux/socket.h | 147 + linux/src/include/linux/sockios.h | 98 + linux/src/include/linux/spinlock.h | 4 + linux/src/include/linux/stat.h | 53 + linux/src/include/linux/stddef.h | 15 + linux/src/include/linux/string.h | 53 + linux/src/include/linux/symtab_begin.h | 45 + linux/src/include/linux/symtab_end.h | 15 + linux/src/include/linux/tasks.h | 17 + linux/src/include/linux/tcp.h | 71 + linux/src/include/linux/termios.h | 7 + linux/src/include/linux/time.h | 53 + linux/src/include/linux/timer.h | 100 + linux/src/include/linux/tqueue.h | 143 + linux/src/include/linux/trdevice.h | 40 + linux/src/include/linux/tty.h | 351 + linux/src/include/linux/tty_driver.h | 189 + linux/src/include/linux/tty_ldisc.h | 46 + linux/src/include/linux/types.h | 96 + linux/src/include/linux/ucdrom.h | 96 + linux/src/include/linux/udp.h | 29 + linux/src/include/linux/uio.h | 26 + linux/src/include/linux/unistd.h | 11 + linux/src/include/linux/utsname.h | 35 + linux/src/include/linux/version.h | 2 + linux/src/include/linux/vfs.h | 6 + linux/src/include/linux/wait.h | 53 + linux/src/include/linux/wireless.h | 479 + linux/src/include/net/af_unix.h | 14 + linux/src/include/net/arp.h | 17 + linux/src/include/net/atalkcall.h | 2 + linux/src/include/net/ax25.h | 292 + linux/src/include/net/ax25call.h | 2 + linux/src/include/net/br.h | 270 + linux/src/include/net/checksum.h | 25 + linux/src/include/net/datalink.h | 16 + linux/src/include/net/gc.h | 46 + linux/src/include/net/icmp.h | 43 + linux/src/include/net/ip.h | 159 + linux/src/include/net/ip_alias.h | 23 + linux/src/include/net/ip_forward.h | 11 + linux/src/include/net/ip_masq.h | 205 + linux/src/include/net/ipip.h | 4 + linux/src/include/net/ipx.h | 88 + linux/src/include/net/ipxcall.h | 2 + linux/src/include/net/netlink.h | 32 + linux/src/include/net/netrom.h | 166 + linux/src/include/net/nrcall.h | 2 + linux/src/include/net/p8022.h | 7 + linux/src/include/net/p8022call.h | 2 + linux/src/include/net/p8022tr.h | 8 + linux/src/include/net/p8022trcall.h | 3 + linux/src/include/net/protocol.h | 55 + linux/src/include/net/psnap.h | 7 + linux/src/include/net/psnapcall.h | 2 + linux/src/include/net/rarp.h | 12 + linux/src/include/net/raw.h | 44 + linux/src/include/net/rose.h | 233 + linux/src/include/net/rosecall.h | 2 + linux/src/include/net/route.h | 189 + linux/src/include/net/slhc.h | 6 + linux/src/include/net/slhc_vj.h | 187 + linux/src/include/net/snmp.h | 107 + linux/src/include/net/sock.h | 613 + linux/src/include/net/spx.h | 38 + linux/src/include/net/tcp.h | 374 + linux/src/include/net/udp.h | 63 + linux/src/include/scsi/scsi.h | 205 + linux/src/include/scsi/scsi_ioctl.h | 28 + linux/src/include/scsi/scsicam.h | 17 + linux/src/init/main.c | 1135 ++ linux/src/init/version.c | 30 + linux/src/kernel/dma.c | 99 + linux/src/kernel/printk.c | 253 + linux/src/kernel/resource.c | 129 + linux/src/kernel/sched.c | 1747 +++ linux/src/kernel/softirq.c | 54 + linux/src/lib/ctype.c | 36 + linux/src/lib/vsprintf.c | 306 + linux/src/net/core/dev.c | 1629 +++ tests/.gitignore | 1 + tests/Makefrag.am | 34 + tests/README | 37 + tests/configfrag.ac | 43 + tests/grub.cfg.single.template | 4 + tests/include/device/cons.h | 27 + tests/include/kern/printf.h | 1 + tests/include/mach/mig_support.h | 71 + tests/include/syscalls.h | 83 + tests/include/testlib.h | 75 + tests/include/util/atoi.h | 1 + tests/run-qemu.sh.template | 38 + tests/start.S | 28 + tests/syscalls.S | 4 + tests/test-gsync.c | 122 + tests/test-hello.c | 26 + tests/test-mach_host.c | 81 + tests/test-mach_port.c | 121 + tests/test-machmsg.c | 405 + tests/test-multiboot.in | 30 + tests/test-syscalls.c | 166 + tests/test-task.c | 171 + tests/test-threads.c | 104 + tests/test-vm.c | 85 + tests/testlib.c | 114 + tests/testlib_thread_start.c | 86 + tests/user-qemu.mk | 221 + util/atoi.c | 106 + util/atoi.h | 67 + util/byteorder.c | 53 + util/byteorder.h | 32 + version.c.in | 2 + version.m4 | 4 + vm/memory_object.c | 1090 ++ vm/memory_object.h | 39 + vm/memory_object_default.cli | 28 + vm/memory_object_proxy.c | 228 + vm/memory_object_proxy.h | 39 + vm/memory_object_user.cli | 28 + vm/pmap.h | 241 + vm/vm_debug.c | 548 + vm/vm_external.c | 151 + vm/vm_external.h | 95 + vm/vm_fault.c | 2136 ++++ vm/vm_fault.h | 81 + vm/vm_init.c | 88 + vm/vm_init.h | 25 + vm/vm_kern.c | 1099 ++ vm/vm_kern.h | 100 + vm/vm_map.c | 5237 ++++++++ vm/vm_map.h | 585 + vm/vm_object.c | 2994 +++++ vm/vm_object.h | 415 + vm/vm_page.c | 2164 ++++ vm/vm_page.h | 567 + vm/vm_pageout.c | 515 + vm/vm_pageout.h | 53 + vm/vm_print.h | 41 + vm/vm_resident.c | 1116 ++ vm/vm_resident.h | 45 + vm/vm_types.h | 42 + vm/vm_user.c | 803 ++ vm/vm_user.h | 60 + x86_64/Makefrag.am | 245 + x86_64/_setjmp.S | 65 + x86_64/boothdr.S | 222 + x86_64/configfrag.ac | 63 + x86_64/copy_user.c | 613 + x86_64/cswitch.S | 148 + x86_64/debug_trace.S | 56 + x86_64/idt_inittab.S | 148 + x86_64/include/mach/x86_64 | 1 + x86_64/include/syscall_sw.h | 40 + x86_64/interrupt.S | 140 + x86_64/kdasm.S | 133 + x86_64/ldscript | 227 + x86_64/locore.S | 1640 +++ x86_64/spl.S | 265 + x86_64/x86_64 | 1 + x86_64/xen_boothdr.S | 190 + x86_64/xen_locore.S | 146 + xen/Makefrag.am | 83 + xen/block.c | 730 ++ xen/block.h | 24 + xen/configfrag.ac | 76 + xen/console.c | 230 + xen/console.h | 53 + xen/evt.c | 119 + xen/evt.h | 29 + xen/grant.c | 143 + xen/grant.h | 33 + xen/net.c | 767 ++ xen/net.h | 24 + xen/public/COPYING | 38 + xen/public/arch-x86/xen-mca.h | 279 + xen/public/arch-x86/xen-x86_32.h | 180 + xen/public/arch-x86/xen-x86_64.h | 212 + xen/public/arch-x86/xen.h | 204 + xen/public/arch-x86_32.h | 27 + xen/public/arch-x86_64.h | 27 + xen/public/callback.h | 121 + xen/public/dom0_ops.h | 120 + xen/public/domctl.h | 680 ++ xen/public/elfnote.h | 233 + xen/public/elfstructs.h | 526 + xen/public/event_channel.h | 264 + xen/public/features.h | 83 + xen/public/grant_table.h | 438 + xen/public/io/blkif.h | 141 + xen/public/io/console.h | 51 + xen/public/io/fbif.h | 176 + xen/public/io/fsif.h | 191 + xen/public/io/kbdif.h | 132 + xen/public/io/netif.h | 205 + xen/public/io/pciif.h | 101 + xen/public/io/protocols.h | 40 + xen/public/io/ring.h | 313 + xen/public/io/tpmif.h | 77 + xen/public/io/xenbus.h | 80 + xen/public/io/xs_wire.h | 132 + xen/public/kexec.h | 189 + xen/public/libelf.h | 265 + xen/public/memory.h | 312 + xen/public/nmi.h | 78 + xen/public/physdev.h | 219 + xen/public/platform.h | 346 + xen/public/sched.h | 121 + xen/public/sysctl.h | 308 + xen/public/trace.h | 206 + xen/public/vcpu.h | 213 + xen/public/version.h | 91 + xen/public/xen-compat.h | 44 + xen/public/xen.h | 657 + xen/public/xencomm.h | 41 + xen/public/xenoprof.h | 138 + xen/ring.c | 61 + xen/ring.h | 34 + xen/store.c | 337 + xen/store.h | 54 + xen/time.c | 144 + xen/time.h | 25 + xen/xen.c | 73 + xen/xen.h | 41 + 1153 files changed, 513598 insertions(+) create mode 100644 =announce-1.0 create mode 100644 =announce-1.1 create mode 100644 =announce-1.1.1 create mode 100644 =announce-1.2 create mode 100644 =announce-1.3 create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 COPYING3 create mode 100644 ChangeLog create mode 100644 DEVELOPMENT create mode 100644 Makefile.am create mode 100644 Makefile.in.dep.patch create mode 100644 Makefrag.am create mode 100644 Makerules.am create mode 100644 Makerules.mig.am create mode 100644 NEWS create mode 100644 README create mode 100644 chips/busses.c create mode 100644 chips/busses.h create mode 100644 config.status.dep.patch create mode 100644 configfrag-first.ac create mode 100644 configfrag.ac create mode 100644 configure.ac create mode 100644 ddb/db_access.c create mode 100644 ddb/db_access.h create mode 100644 ddb/db_break.c create mode 100644 ddb/db_break.h create mode 100644 ddb/db_command.c create mode 100644 ddb/db_command.h create mode 100644 ddb/db_cond.c create mode 100644 ddb/db_cond.h create mode 100644 ddb/db_elf.c create mode 100644 ddb/db_elf.h create mode 100644 ddb/db_examine.c create mode 100644 ddb/db_examine.h create mode 100644 ddb/db_expr.c create mode 100644 ddb/db_expr.h create mode 100644 ddb/db_ext_symtab.c create mode 100644 ddb/db_input.c create mode 100644 ddb/db_input.h create mode 100644 ddb/db_lex.c create mode 100644 ddb/db_lex.h create mode 100644 ddb/db_macro.c create mode 100644 ddb/db_macro.h create mode 100644 ddb/db_mp.c create mode 100644 ddb/db_mp.h create mode 100644 ddb/db_output.c create mode 100644 ddb/db_output.h create mode 100644 ddb/db_print.c create mode 100644 ddb/db_print.h create mode 100644 ddb/db_run.c create mode 100644 ddb/db_run.h create mode 100644 ddb/db_sym.c create mode 100644 ddb/db_sym.h create mode 100644 ddb/db_task_thread.c create mode 100644 ddb/db_task_thread.h create mode 100644 ddb/db_trap.c create mode 100644 ddb/db_trap.h create mode 100644 ddb/db_variables.c create mode 100644 ddb/db_variables.h create mode 100644 ddb/db_watch.c create mode 100644 ddb/db_watch.h create mode 100644 ddb/db_write_cmd.c create mode 100644 ddb/db_write_cmd.h create mode 100644 ddb/nlist.h create mode 100644 ddb/stab.h create mode 100644 ddb/tr.h create mode 100644 device/blkio.c create mode 100644 device/blkio.h create mode 100644 device/buf.h create mode 100644 device/chario.c create mode 100644 device/chario.h create mode 100644 device/cirbuf.c create mode 100644 device/cirbuf.h create mode 100644 device/conf.h create mode 100644 device/cons.c create mode 100644 device/cons.h create mode 100644 device/dev_hdr.h create mode 100644 device/dev_lookup.c create mode 100644 device/dev_master.h create mode 100644 device/dev_name.c create mode 100644 device/dev_pager.c create mode 100644 device/dev_pager.h create mode 100644 device/device.srv create mode 100644 device/device_emul.h create mode 100644 device/device_init.c create mode 100644 device/device_init.h create mode 100644 device/device_pager.srv create mode 100644 device/device_port.h create mode 100644 device/device_reply.cli create mode 100644 device/device_types_kernel.h create mode 100644 device/ds_routines.c create mode 100644 device/ds_routines.h create mode 100644 device/if_ether.h create mode 100644 device/if_hdr.h create mode 100644 device/intr.c create mode 100644 device/intr.h create mode 100644 device/io_req.h create mode 100644 device/kmsg.c create mode 100644 device/kmsg.h create mode 100644 device/memory_object_reply.cli create mode 100644 device/net_io.c create mode 100644 device/net_io.h create mode 100644 device/param.h create mode 100644 device/subrs.c create mode 100644 device/subrs.h create mode 100644 device/tty.h create mode 100644 doc/.gitignore create mode 100644 doc/Makefrag.am create mode 100644 doc/fdl.texi create mode 100644 doc/gpl.texi create mode 100644 doc/mach.texi create mode 100644 gensym.awk create mode 100755 gitlog-to-changelog create mode 100644 i386/Makefrag.am create mode 100644 i386/Makefrag_x86.am create mode 100644 i386/README-Drivers create mode 100644 i386/configfrag.ac create mode 100644 i386/i386/.gitignore create mode 100644 i386/i386/_setjmp.S create mode 100644 i386/i386/apic.c create mode 100644 i386/i386/apic.h create mode 100644 i386/i386/ast.h create mode 100644 i386/i386/ast_check.c create mode 100644 i386/i386/ast_types.h create mode 100644 i386/i386/copy_user.h create mode 100644 i386/i386/cpu.h create mode 100644 i386/i386/cpu_number.h create mode 100644 i386/i386/cpuboot.S create mode 100644 i386/i386/cswitch.S create mode 100644 i386/i386/db_disasm.c create mode 100644 i386/i386/db_interface.c create mode 100644 i386/i386/db_interface.h create mode 100644 i386/i386/db_machdep.h create mode 100644 i386/i386/db_trace.c create mode 100644 i386/i386/db_trace.h create mode 100644 i386/i386/debug.h create mode 100644 i386/i386/debug_i386.c create mode 100644 i386/i386/debug_trace.S create mode 100644 i386/i386/eflags.h create mode 100644 i386/i386/fpu.c create mode 100644 i386/i386/fpu.h create mode 100644 i386/i386/gdt.c create mode 100644 i386/i386/gdt.h create mode 100644 i386/i386/hardclock.c create mode 100644 i386/i386/hardclock.h create mode 100644 i386/i386/i386asm.sym create mode 100644 i386/i386/idt-gen.h create mode 100644 i386/i386/idt.c create mode 100644 i386/i386/idt_inittab.S create mode 100644 i386/i386/io_perm.c create mode 100644 i386/i386/io_perm.h create mode 100644 i386/i386/ipl.h create mode 100644 i386/i386/irq.c create mode 100644 i386/i386/irq.h create mode 100644 i386/i386/ktss.c create mode 100644 i386/i386/ktss.h create mode 100644 i386/i386/kttd_interface.c create mode 100644 i386/i386/kttd_machdep.h create mode 100644 i386/i386/ldt.c create mode 100644 i386/i386/ldt.h create mode 100644 i386/i386/lock.h create mode 100644 i386/i386/locore.S create mode 100644 i386/i386/locore.h create mode 100644 i386/i386/loose_ends.c create mode 100644 i386/i386/loose_ends.h create mode 100644 i386/i386/mach_i386.srv create mode 100644 i386/i386/mach_param.h create mode 100644 i386/i386/machine_routines.h create mode 100644 i386/i386/machine_task.c create mode 100644 i386/i386/machspl.h create mode 100644 i386/i386/model_dep.h create mode 100644 i386/i386/mp_desc.c create mode 100644 i386/i386/mp_desc.h create mode 100644 i386/i386/msr.h create mode 100644 i386/i386/pcb.c create mode 100644 i386/i386/pcb.h create mode 100644 i386/i386/percpu.c create mode 100644 i386/i386/percpu.h create mode 100644 i386/i386/phys.c create mode 100644 i386/i386/pic.c create mode 100644 i386/i386/pic.h create mode 100644 i386/i386/pio.h create mode 100644 i386/i386/pit.c create mode 100644 i386/i386/pit.h create mode 100644 i386/i386/pmap.h create mode 100644 i386/i386/proc_reg.h create mode 100644 i386/i386/sched_param.h create mode 100644 i386/i386/seg.h create mode 100644 i386/i386/setjmp.h create mode 100644 i386/i386/smp.c create mode 100644 i386/i386/smp.h create mode 100644 i386/i386/spl.S create mode 100644 i386/i386/spl.h create mode 100644 i386/i386/strings.c create mode 100644 i386/i386/task.h create mode 100644 i386/i386/thread.h create mode 100644 i386/i386/time_stamp.h create mode 100644 i386/i386/trap.c create mode 100644 i386/i386/trap.h create mode 100644 i386/i386/tss.h create mode 100644 i386/i386/user_ldt.c create mode 100644 i386/i386/user_ldt.h create mode 100644 i386/i386/vm_param.h create mode 100644 i386/i386/xen.h create mode 100644 i386/i386/xpr.h create mode 100644 i386/i386at/acpi_parse_apic.c create mode 100644 i386/i386at/acpi_parse_apic.h create mode 100644 i386/i386at/autoconf.c create mode 100644 i386/i386at/autoconf.h create mode 100644 i386/i386at/biosmem.c create mode 100644 i386/i386at/biosmem.h create mode 100644 i386/i386at/boothdr.S create mode 100644 i386/i386at/com.c create mode 100644 i386/i386at/com.h create mode 100644 i386/i386at/comreg.h create mode 100644 i386/i386at/conf.c create mode 100644 i386/i386at/cons_conf.c create mode 100644 i386/i386at/cram.h create mode 100644 i386/i386at/disk.h create mode 100644 i386/i386at/elf.h create mode 100644 i386/i386at/i8250.h create mode 100644 i386/i386at/idt.h create mode 100644 i386/i386at/immc.c create mode 100644 i386/i386at/immc.h create mode 100644 i386/i386at/int_init.c create mode 100644 i386/i386at/int_init.h create mode 100644 i386/i386at/interrupt.S create mode 100644 i386/i386at/ioapic.c create mode 100644 i386/i386at/kd.c create mode 100644 i386/i386at/kd.h create mode 100644 i386/i386at/kd_event.c create mode 100644 i386/i386at/kd_event.h create mode 100644 i386/i386at/kd_mouse.c create mode 100644 i386/i386at/kd_mouse.h create mode 100644 i386/i386at/kd_queue.c create mode 100644 i386/i386at/kd_queue.h create mode 100644 i386/i386at/kdasm.S create mode 100644 i386/i386at/kdsoft.h create mode 100644 i386/i386at/lpr.c create mode 100644 i386/i386at/lpr.h create mode 100644 i386/i386at/mem.c create mode 100644 i386/i386at/mem.h create mode 100644 i386/i386at/model_dep.c create mode 100644 i386/i386at/model_dep.h create mode 100644 i386/i386at/pic_isa.c create mode 100644 i386/i386at/rtc.c create mode 100644 i386/i386at/rtc.h create mode 100644 i386/include/mach/i386/asm.h create mode 100644 i386/include/mach/i386/boolean.h create mode 100644 i386/include/mach/i386/eflags.h create mode 100644 i386/include/mach/i386/exception.h create mode 100644 i386/include/mach/i386/exec/elf.h create mode 100644 i386/include/mach/i386/fp_reg.h create mode 100644 i386/include/mach/i386/ioccom.h create mode 100644 i386/include/mach/i386/kern_return.h create mode 100644 i386/include/mach/i386/mach_i386.defs create mode 100644 i386/include/mach/i386/mach_i386_types.h create mode 100755 i386/include/mach/i386/machine_types.defs create mode 100644 i386/include/mach/i386/multiboot.h create mode 100644 i386/include/mach/i386/syscall_sw.h create mode 100644 i386/include/mach/i386/thread_status.h create mode 100644 i386/include/mach/i386/trap.h create mode 100644 i386/include/mach/i386/vm_param.h create mode 100644 i386/include/mach/i386/vm_types.h create mode 100644 i386/include/mach/sa/stdarg.h create mode 100644 i386/intel/pmap.c create mode 100644 i386/intel/pmap.h create mode 100644 i386/intel/read_fault.c create mode 100644 i386/intel/read_fault.h create mode 100644 i386/ldscript create mode 100644 i386/linux/Makefrag.am create mode 100644 i386/linux/dev/include/linux/autoconf.h create mode 100644 i386/xen/Makefrag.am create mode 100644 i386/xen/xen.c create mode 100644 i386/xen/xen_boothdr.S create mode 100644 i386/xen/xen_locore.S create mode 100644 include/alloca.h create mode 100644 include/cache.h create mode 100644 include/device/audio_status.h create mode 100644 include/device/bpf.h create mode 100644 include/device/device.defs create mode 100644 include/device/device_reply.defs create mode 100644 include/device/device_request.defs create mode 100644 include/device/device_types.defs create mode 100644 include/device/device_types.h create mode 100644 include/device/disk_status.h create mode 100644 include/device/input.h create mode 100644 include/device/net_status.h create mode 100644 include/device/notify.defs create mode 100644 include/device/notify.h create mode 100644 include/device/tape_status.h create mode 100644 include/device/tty_status.h create mode 100644 include/inttypes.h create mode 100644 include/mach/alert.h create mode 100644 include/mach/boolean.h create mode 100644 include/mach/boot.h create mode 100644 include/mach/default_pager.defs create mode 100644 include/mach/default_pager_types.defs create mode 100644 include/mach/default_pager_types.h create mode 100644 include/mach/error.h create mode 100644 include/mach/exc.defs create mode 100644 include/mach/exception.h create mode 100644 include/mach/exec/a.out.h create mode 100644 include/mach/exec/elf.h create mode 100644 include/mach/exec/exec.h create mode 100644 include/mach/experimental.defs create mode 100644 include/mach/gnumach.defs create mode 100644 include/mach/host_info.h create mode 100644 include/mach/inline.h create mode 100644 include/mach/kern_return.h create mode 100644 include/mach/mach.defs create mode 100644 include/mach/mach4.defs create mode 100644 include/mach/mach_host.defs create mode 100644 include/mach/mach_param.h create mode 100644 include/mach/mach_port.defs create mode 100644 include/mach/mach_traps.h create mode 100644 include/mach/mach_types.defs create mode 100644 include/mach/mach_types.h create mode 100644 include/mach/machine.h create mode 100644 include/mach/macro_help.h create mode 100644 include/mach/memory_object.defs create mode 100644 include/mach/memory_object.h create mode 100644 include/mach/memory_object_default.defs create mode 100644 include/mach/message.h create mode 100644 include/mach/mig_errors.h create mode 100644 include/mach/mig_support.h create mode 100644 include/mach/notify.defs create mode 100644 include/mach/notify.h create mode 100644 include/mach/pc_sample.h create mode 100644 include/mach/policy.h create mode 100644 include/mach/port.h create mode 100644 include/mach/processor_info.h create mode 100644 include/mach/profil.h create mode 100644 include/mach/profilparam.h create mode 100644 include/mach/std_types.defs create mode 100644 include/mach/std_types.h create mode 100644 include/mach/syscall_sw.h create mode 100644 include/mach/task_info.h create mode 100644 include/mach/task_notify.defs create mode 100644 include/mach/task_special_ports.h create mode 100644 include/mach/thread_info.h create mode 100644 include/mach/thread_special_ports.h create mode 100644 include/mach/thread_status.h create mode 100644 include/mach/thread_switch.h create mode 100644 include/mach/time_value.h create mode 100644 include/mach/version.h create mode 100644 include/mach/vm_attributes.h create mode 100644 include/mach/vm_cache_statistics.h create mode 100644 include/mach/vm_inherit.h create mode 100644 include/mach/vm_param.h create mode 100644 include/mach/vm_prot.h create mode 100644 include/mach/vm_statistics.h create mode 100644 include/mach/vm_sync.h create mode 100644 include/mach/vm_wire.h create mode 100644 include/mach/xen.h create mode 100644 include/mach_debug/hash_info.h create mode 100644 include/mach_debug/mach_debug.defs create mode 100644 include/mach_debug/mach_debug_types.defs create mode 100644 include/mach_debug/mach_debug_types.h create mode 100644 include/mach_debug/slab_info.h create mode 100644 include/mach_debug/vm_info.h create mode 100644 include/string.h create mode 100644 include/sys/reboot.h create mode 100644 include/sys/types.h create mode 100644 ipc/.gitignore create mode 100644 ipc/ipc_entry.c create mode 100644 ipc/ipc_entry.h create mode 100644 ipc/ipc_init.c create mode 100644 ipc/ipc_init.h create mode 100644 ipc/ipc_kmsg.c create mode 100644 ipc/ipc_kmsg.h create mode 100644 ipc/ipc_kmsg_queue.h create mode 100755 ipc/ipc_machdep.h create mode 100644 ipc/ipc_marequest.c create mode 100644 ipc/ipc_marequest.h create mode 100644 ipc/ipc_mqueue.c create mode 100644 ipc/ipc_mqueue.h create mode 100644 ipc/ipc_notify.c create mode 100644 ipc/ipc_notify.h create mode 100644 ipc/ipc_object.c create mode 100644 ipc/ipc_object.h create mode 100644 ipc/ipc_port.c create mode 100644 ipc/ipc_port.h create mode 100644 ipc/ipc_print.h create mode 100644 ipc/ipc_pset.c create mode 100644 ipc/ipc_pset.h create mode 100644 ipc/ipc_right.c create mode 100644 ipc/ipc_right.h create mode 100644 ipc/ipc_space.c create mode 100644 ipc/ipc_space.h create mode 100644 ipc/ipc_table.c create mode 100644 ipc/ipc_table.h create mode 100644 ipc/ipc_target.c create mode 100644 ipc/ipc_target.h create mode 100644 ipc/ipc_thread.c create mode 100644 ipc/ipc_thread.h create mode 100644 ipc/ipc_types.h create mode 100644 ipc/mach_debug.c create mode 100644 ipc/mach_msg.c create mode 100644 ipc/mach_msg.h create mode 100644 ipc/mach_port.c create mode 100644 ipc/mach_port.h create mode 100644 ipc/mach_port.srv create mode 100644 ipc/notify.defs create mode 100644 ipc/port.h create mode 100644 kern/.gitignore create mode 100644 kern/act.c create mode 100644 kern/act.h create mode 100644 kern/assert.h create mode 100644 kern/ast.c create mode 100644 kern/ast.h create mode 100644 kern/atomic.h create mode 100644 kern/boot_script.c create mode 100644 kern/boot_script.h create mode 100644 kern/bootstrap.c create mode 100644 kern/bootstrap.h create mode 100644 kern/counters.c create mode 100644 kern/counters.h create mode 100644 kern/cpu_number.h create mode 100644 kern/debug.c create mode 100644 kern/debug.h create mode 100644 kern/elf-load.c create mode 100644 kern/eventcount.c create mode 100644 kern/eventcount.h create mode 100644 kern/exc.defs create mode 100644 kern/exception.c create mode 100644 kern/exception.h create mode 100644 kern/experimental.srv create mode 100644 kern/gnumach.srv create mode 100644 kern/gsync.c create mode 100644 kern/gsync.h create mode 100644 kern/host.c create mode 100644 kern/host.h create mode 100644 kern/ipc_host.c create mode 100644 kern/ipc_host.h create mode 100644 kern/ipc_kobject.c create mode 100644 kern/ipc_kobject.h create mode 100644 kern/ipc_mig.c create mode 100644 kern/ipc_mig.h create mode 100644 kern/ipc_sched.c create mode 100644 kern/ipc_sched.h create mode 100644 kern/ipc_tt.c create mode 100644 kern/ipc_tt.h create mode 100644 kern/kalloc.h create mode 100644 kern/kern_types.h create mode 100644 kern/kmutex.c create mode 100644 kern/kmutex.h create mode 100644 kern/list.h create mode 100644 kern/lock.c create mode 100644 kern/lock.h create mode 100644 kern/lock_mon.c create mode 100644 kern/log2.h create mode 100644 kern/mach.srv create mode 100644 kern/mach4.srv create mode 100644 kern/mach_clock.c create mode 100644 kern/mach_clock.h create mode 100644 kern/mach_debug.srv create mode 100644 kern/mach_factor.c create mode 100644 kern/mach_factor.h create mode 100644 kern/mach_host.srv create mode 100644 kern/machine.c create mode 100644 kern/machine.h create mode 100644 kern/macros.h create mode 100644 kern/pc_sample.c create mode 100644 kern/pc_sample.h create mode 100644 kern/printf.c create mode 100644 kern/printf.h create mode 100644 kern/priority.c create mode 100644 kern/priority.h create mode 100644 kern/processor.c create mode 100644 kern/processor.h create mode 100644 kern/profile.c create mode 100644 kern/queue.c create mode 100644 kern/queue.h create mode 100644 kern/rbtree.c create mode 100644 kern/rbtree.h create mode 100644 kern/rbtree_i.h create mode 100644 kern/rdxtree.c create mode 100644 kern/rdxtree.h create mode 100644 kern/rdxtree_i.h create mode 100644 kern/refcount.h create mode 100644 kern/sched.h create mode 100644 kern/sched_prim.c create mode 100644 kern/sched_prim.h create mode 100644 kern/shuttle.h create mode 100644 kern/slab.c create mode 100644 kern/slab.h create mode 100644 kern/smp.c create mode 100644 kern/smp.h create mode 100644 kern/startup.c create mode 100644 kern/startup.h create mode 100644 kern/strings.c create mode 100644 kern/syscall_emulation.c create mode 100644 kern/syscall_emulation.h create mode 100644 kern/syscall_subr.c create mode 100644 kern/syscall_subr.h create mode 100644 kern/syscall_sw.c create mode 100644 kern/syscall_sw.h create mode 100644 kern/task.c create mode 100644 kern/task.h create mode 100644 kern/task_notify.cli create mode 100644 kern/thread.c create mode 100644 kern/thread.h create mode 100644 kern/thread_swap.c create mode 100644 kern/thread_swap.h create mode 100644 kern/timer.c create mode 100644 kern/timer.h create mode 100644 kern/xpr.c create mode 100644 kern/xpr.h create mode 100644 linux/Makefrag.am create mode 100644 linux/configfrag.ac create mode 100644 linux/dev/README create mode 100644 linux/dev/arch/i386/kernel/irq.c create mode 100644 linux/dev/arch/i386/kernel/setup.c create mode 100644 linux/dev/drivers/block/ahci.c create mode 100644 linux/dev/drivers/block/floppy.c create mode 100644 linux/dev/drivers/block/genhd.c create mode 100644 linux/dev/drivers/net/Space.c create mode 100644 linux/dev/drivers/net/auto_irq.c create mode 100644 linux/dev/drivers/net/net_init.c create mode 100644 linux/dev/drivers/net/wavelan.p.h create mode 100644 linux/dev/drivers/scsi/eata_dma.c create mode 100644 linux/dev/drivers/scsi/g_NCR5380.c create mode 100644 linux/dev/glue/block.c create mode 100644 linux/dev/glue/glue.h create mode 100644 linux/dev/glue/kmem.c create mode 100644 linux/dev/glue/misc.c create mode 100644 linux/dev/glue/net.c create mode 100644 linux/dev/include/ahci.h create mode 100644 linux/dev/include/asm-i386/page.h create mode 100644 linux/dev/include/asm-i386/smp.h create mode 100644 linux/dev/include/asm-i386/string.h create mode 100644 linux/dev/include/asm-i386/system.h create mode 100644 linux/dev/include/asm-i386/uaccess.h create mode 100644 linux/dev/include/linux/blk.h create mode 100644 linux/dev/include/linux/blkdev.h create mode 100644 linux/dev/include/linux/compile.h create mode 100644 linux/dev/include/linux/etherdevice.h create mode 100644 linux/dev/include/linux/fs.h create mode 100644 linux/dev/include/linux/genhd.h create mode 100644 linux/dev/include/linux/if.h create mode 100644 linux/dev/include/linux/kernel.h create mode 100644 linux/dev/include/linux/locks.h create mode 100644 linux/dev/include/linux/malloc.h create mode 100644 linux/dev/include/linux/mm.h create mode 100644 linux/dev/include/linux/modversions.h create mode 100644 linux/dev/include/linux/netdevice.h create mode 100644 linux/dev/include/linux/notifier.h create mode 100644 linux/dev/include/linux/pagemap.h create mode 100644 linux/dev/include/linux/pm.h create mode 100644 linux/dev/include/linux/proc_fs.h create mode 100644 linux/dev/include/linux/sched.h create mode 100644 linux/dev/include/linux/skbuff.h create mode 100644 linux/dev/include/linux/threads.h create mode 100644 linux/dev/include/linux/types.h create mode 100644 linux/dev/init/main.c create mode 100644 linux/dev/init/version.c create mode 100644 linux/dev/kernel/dma.c create mode 100644 linux/dev/kernel/printk.c create mode 100644 linux/dev/kernel/resource.c create mode 100644 linux/dev/kernel/sched.c create mode 100644 linux/dev/kernel/softirq.c create mode 100644 linux/dev/lib/vsprintf.c create mode 100644 linux/dev/net/core/dev.c create mode 100644 linux/pcmcia-cs/clients/3c574_cs.c create mode 100644 linux/pcmcia-cs/clients/3c589_cs.c create mode 100644 linux/pcmcia-cs/clients/ax8390.h create mode 100644 linux/pcmcia-cs/clients/axnet_cs.c create mode 100644 linux/pcmcia-cs/clients/fmvj18x_cs.c create mode 100644 linux/pcmcia-cs/clients/nmclan_cs.c create mode 100644 linux/pcmcia-cs/clients/ositech.h create mode 100644 linux/pcmcia-cs/clients/pcnet_cs.c create mode 100644 linux/pcmcia-cs/clients/smc91c92_cs.c create mode 100644 linux/pcmcia-cs/clients/xirc2ps_cs.c create mode 100644 linux/pcmcia-cs/glue/ds.c create mode 100644 linux/pcmcia-cs/glue/pcmcia.c create mode 100644 linux/pcmcia-cs/glue/pcmcia_glue.h create mode 100644 linux/pcmcia-cs/glue/wireless_glue.h create mode 100644 linux/pcmcia-cs/include/linux/crc32.h create mode 100644 linux/pcmcia-cs/include/linux/slab.h create mode 100644 linux/pcmcia-cs/include/pcmcia/bulkmem.h create mode 100644 linux/pcmcia-cs/include/pcmcia/bus_ops.h create mode 100644 linux/pcmcia-cs/include/pcmcia/ciscode.h create mode 100644 linux/pcmcia-cs/include/pcmcia/cisreg.h create mode 100644 linux/pcmcia-cs/include/pcmcia/cistpl.h create mode 100644 linux/pcmcia-cs/include/pcmcia/cs.h create mode 100644 linux/pcmcia-cs/include/pcmcia/cs_types.h create mode 100644 linux/pcmcia-cs/include/pcmcia/driver_ops.h create mode 100644 linux/pcmcia-cs/include/pcmcia/ds.h create mode 100644 linux/pcmcia-cs/include/pcmcia/mem_op.h create mode 100644 linux/pcmcia-cs/include/pcmcia/ss.h create mode 100644 linux/pcmcia-cs/include/pcmcia/version.h create mode 100644 linux/pcmcia-cs/modules/bulkmem.c create mode 100644 linux/pcmcia-cs/modules/cirrus.h create mode 100644 linux/pcmcia-cs/modules/cistpl.c create mode 100644 linux/pcmcia-cs/modules/cs.c create mode 100644 linux/pcmcia-cs/modules/cs_internal.h create mode 100644 linux/pcmcia-cs/modules/ds.c create mode 100644 linux/pcmcia-cs/modules/ene.h create mode 100644 linux/pcmcia-cs/modules/i82365.c create mode 100644 linux/pcmcia-cs/modules/i82365.h create mode 100644 linux/pcmcia-cs/modules/o2micro.h create mode 100644 linux/pcmcia-cs/modules/pci_fixup.c create mode 100644 linux/pcmcia-cs/modules/ricoh.h create mode 100644 linux/pcmcia-cs/modules/rsrc_mgr.c create mode 100644 linux/pcmcia-cs/modules/smc34c90.h create mode 100644 linux/pcmcia-cs/modules/ti113x.h create mode 100644 linux/pcmcia-cs/modules/topic.h create mode 100644 linux/pcmcia-cs/modules/vg468.h create mode 100644 linux/pcmcia-cs/modules/yenta.h create mode 100644 linux/pcmcia-cs/wireless/hermes.c create mode 100644 linux/pcmcia-cs/wireless/hermes.h create mode 100644 linux/pcmcia-cs/wireless/hermes_rid.h create mode 100644 linux/pcmcia-cs/wireless/ieee802_11.h create mode 100644 linux/pcmcia-cs/wireless/orinoco.c create mode 100644 linux/pcmcia-cs/wireless/orinoco.h create mode 100644 linux/pcmcia-cs/wireless/orinoco_cs.c create mode 100644 linux/src/COPYING create mode 100644 linux/src/arch/i386/kernel/bios32.c create mode 100644 linux/src/arch/i386/kernel/irq.c create mode 100644 linux/src/arch/i386/lib/delay.c create mode 100644 linux/src/arch/i386/lib/semaphore.S create mode 100644 linux/src/drivers/block/cmd640.c create mode 100644 linux/src/drivers/block/floppy.c create mode 100644 linux/src/drivers/block/genhd.c create mode 100644 linux/src/drivers/block/ide-cd.c create mode 100644 linux/src/drivers/block/ide.c create mode 100644 linux/src/drivers/block/ide.h create mode 100644 linux/src/drivers/block/ide_modes.h create mode 100644 linux/src/drivers/block/rz1000.c create mode 100644 linux/src/drivers/block/triton.c create mode 100644 linux/src/drivers/net/3c501.c create mode 100644 linux/src/drivers/net/3c503.c create mode 100644 linux/src/drivers/net/3c503.h create mode 100644 linux/src/drivers/net/3c505.c create mode 100644 linux/src/drivers/net/3c505.h create mode 100644 linux/src/drivers/net/3c507.c create mode 100644 linux/src/drivers/net/3c509.c create mode 100644 linux/src/drivers/net/3c515.c create mode 100644 linux/src/drivers/net/3c59x.c create mode 100644 linux/src/drivers/net/8390.c create mode 100644 linux/src/drivers/net/8390.h create mode 100644 linux/src/drivers/net/Space.c create mode 100644 linux/src/drivers/net/ac3200.c create mode 100644 linux/src/drivers/net/apricot.c create mode 100644 linux/src/drivers/net/at1700.c create mode 100644 linux/src/drivers/net/atp.c create mode 100644 linux/src/drivers/net/atp.h create mode 100644 linux/src/drivers/net/auto_irq.c create mode 100644 linux/src/drivers/net/cb_shim.c create mode 100644 linux/src/drivers/net/de4x5.c create mode 100644 linux/src/drivers/net/de4x5.h create mode 100644 linux/src/drivers/net/de600.c create mode 100644 linux/src/drivers/net/de620.c create mode 100644 linux/src/drivers/net/de620.h create mode 100644 linux/src/drivers/net/depca.c create mode 100644 linux/src/drivers/net/depca.h create mode 100644 linux/src/drivers/net/e2100.c create mode 100644 linux/src/drivers/net/eepro.c create mode 100644 linux/src/drivers/net/eepro100.c create mode 100644 linux/src/drivers/net/eexpress.c create mode 100644 linux/src/drivers/net/epic100.c create mode 100644 linux/src/drivers/net/eth16i.c create mode 100644 linux/src/drivers/net/eth82586.h create mode 100644 linux/src/drivers/net/ewrk3.c create mode 100644 linux/src/drivers/net/ewrk3.h create mode 100644 linux/src/drivers/net/fmv18x.c create mode 100644 linux/src/drivers/net/hamachi.c create mode 100644 linux/src/drivers/net/hp-plus.c create mode 100644 linux/src/drivers/net/hp.c create mode 100644 linux/src/drivers/net/hp100.c create mode 100644 linux/src/drivers/net/hp100.h create mode 100644 linux/src/drivers/net/i82586.h create mode 100644 linux/src/drivers/net/intel-gige.c create mode 100644 linux/src/drivers/net/iow.h create mode 100644 linux/src/drivers/net/kern_compat.h create mode 100644 linux/src/drivers/net/lance.c create mode 100644 linux/src/drivers/net/myson803.c create mode 100644 linux/src/drivers/net/natsemi.c create mode 100644 linux/src/drivers/net/ne.c create mode 100644 linux/src/drivers/net/ne2k-pci.c create mode 100644 linux/src/drivers/net/net_init.c create mode 100644 linux/src/drivers/net/ni52.c create mode 100644 linux/src/drivers/net/ni52.h create mode 100644 linux/src/drivers/net/ni65.c create mode 100644 linux/src/drivers/net/ni65.h create mode 100644 linux/src/drivers/net/ns820.c create mode 100644 linux/src/drivers/net/pci-scan.c create mode 100644 linux/src/drivers/net/pci-scan.h create mode 100644 linux/src/drivers/net/pcnet32.c create mode 100644 linux/src/drivers/net/rtl8139.c create mode 100644 linux/src/drivers/net/seeq8005.c create mode 100644 linux/src/drivers/net/seeq8005.h create mode 100644 linux/src/drivers/net/sis900.c create mode 100644 linux/src/drivers/net/sis900.h create mode 100644 linux/src/drivers/net/sk_g16.c create mode 100644 linux/src/drivers/net/sk_g16.h create mode 100644 linux/src/drivers/net/smc-ultra.c create mode 100644 linux/src/drivers/net/smc-ultra32.c create mode 100644 linux/src/drivers/net/smc9194.c create mode 100644 linux/src/drivers/net/smc9194.h create mode 100644 linux/src/drivers/net/starfire.c create mode 100644 linux/src/drivers/net/sundance.c create mode 100644 linux/src/drivers/net/tlan.c create mode 100644 linux/src/drivers/net/tlan.h create mode 100644 linux/src/drivers/net/tulip.c create mode 100644 linux/src/drivers/net/via-rhine.c create mode 100644 linux/src/drivers/net/wavelan.c create mode 100644 linux/src/drivers/net/wavelan.h create mode 100644 linux/src/drivers/net/wavelan.p.h create mode 100644 linux/src/drivers/net/wd.c create mode 100644 linux/src/drivers/net/winbond-840.c create mode 100644 linux/src/drivers/net/yellowfin.c create mode 100644 linux/src/drivers/net/znet.c create mode 100644 linux/src/drivers/pci/pci.c create mode 100644 linux/src/drivers/scsi/53c7,8xx.h create mode 100644 linux/src/drivers/scsi/53c78xx.c create mode 100644 linux/src/drivers/scsi/53c8xx_d.h create mode 100644 linux/src/drivers/scsi/53c8xx_u.h create mode 100644 linux/src/drivers/scsi/AM53C974.c create mode 100644 linux/src/drivers/scsi/AM53C974.h create mode 100644 linux/src/drivers/scsi/BusLogic.c create mode 100644 linux/src/drivers/scsi/BusLogic.h create mode 100644 linux/src/drivers/scsi/FlashPoint.c create mode 100644 linux/src/drivers/scsi/NCR5380.c create mode 100644 linux/src/drivers/scsi/NCR5380.h create mode 100644 linux/src/drivers/scsi/NCR53c406a.c create mode 100644 linux/src/drivers/scsi/NCR53c406a.h create mode 100644 linux/src/drivers/scsi/advansys.c create mode 100644 linux/src/drivers/scsi/advansys.h create mode 100644 linux/src/drivers/scsi/aha152x.c create mode 100644 linux/src/drivers/scsi/aha152x.h create mode 100644 linux/src/drivers/scsi/aha1542.c create mode 100644 linux/src/drivers/scsi/aha1542.h create mode 100644 linux/src/drivers/scsi/aha1740.c create mode 100644 linux/src/drivers/scsi/aha1740.h create mode 100644 linux/src/drivers/scsi/aic7xxx.c create mode 100644 linux/src/drivers/scsi/aic7xxx.h create mode 100644 linux/src/drivers/scsi/aic7xxx/scsi_message.h create mode 100644 linux/src/drivers/scsi/aic7xxx/sequencer.h create mode 100644 linux/src/drivers/scsi/aic7xxx_proc.c create mode 100644 linux/src/drivers/scsi/aic7xxx_reg.h create mode 100644 linux/src/drivers/scsi/aic7xxx_seq.c create mode 100644 linux/src/drivers/scsi/constants.c create mode 100644 linux/src/drivers/scsi/constants.h create mode 100644 linux/src/drivers/scsi/dc390.h create mode 100644 linux/src/drivers/scsi/dtc.c create mode 100644 linux/src/drivers/scsi/dtc.h create mode 100644 linux/src/drivers/scsi/eata.c create mode 100644 linux/src/drivers/scsi/eata.h create mode 100644 linux/src/drivers/scsi/eata_dma.c create mode 100644 linux/src/drivers/scsi/eata_dma.h create mode 100644 linux/src/drivers/scsi/eata_dma_proc.c create mode 100644 linux/src/drivers/scsi/eata_dma_proc.h create mode 100644 linux/src/drivers/scsi/eata_generic.h create mode 100644 linux/src/drivers/scsi/eata_pio.c create mode 100644 linux/src/drivers/scsi/eata_pio.h create mode 100644 linux/src/drivers/scsi/eata_pio_proc.c create mode 100644 linux/src/drivers/scsi/fdomain.c create mode 100644 linux/src/drivers/scsi/fdomain.h create mode 100644 linux/src/drivers/scsi/g_NCR5380.c create mode 100644 linux/src/drivers/scsi/g_NCR5380.h create mode 100644 linux/src/drivers/scsi/gdth.c create mode 100644 linux/src/drivers/scsi/gdth.h create mode 100644 linux/src/drivers/scsi/gdth_ioctl.h create mode 100644 linux/src/drivers/scsi/gdth_proc.c create mode 100644 linux/src/drivers/scsi/gdth_proc.h create mode 100644 linux/src/drivers/scsi/hosts.c create mode 100644 linux/src/drivers/scsi/hosts.h create mode 100644 linux/src/drivers/scsi/in2000.c create mode 100644 linux/src/drivers/scsi/in2000.h create mode 100644 linux/src/drivers/scsi/ncr53c8xx.c create mode 100644 linux/src/drivers/scsi/ncr53c8xx.h create mode 100644 linux/src/drivers/scsi/pas16.c create mode 100644 linux/src/drivers/scsi/pas16.h create mode 100644 linux/src/drivers/scsi/ppa.c create mode 100644 linux/src/drivers/scsi/ppa.h create mode 100644 linux/src/drivers/scsi/qlogicfas.c create mode 100644 linux/src/drivers/scsi/qlogicfas.h create mode 100644 linux/src/drivers/scsi/qlogicisp.c create mode 100644 linux/src/drivers/scsi/qlogicisp.h create mode 100644 linux/src/drivers/scsi/scripts.h create mode 100644 linux/src/drivers/scsi/scsi.c create mode 100644 linux/src/drivers/scsi/scsi.h create mode 100644 linux/src/drivers/scsi/scsi_ioctl.c create mode 100644 linux/src/drivers/scsi/scsi_proc.c create mode 100644 linux/src/drivers/scsi/scsicam.c create mode 100644 linux/src/drivers/scsi/scsiio.c create mode 100644 linux/src/drivers/scsi/scsiiom.c create mode 100644 linux/src/drivers/scsi/sd.c create mode 100644 linux/src/drivers/scsi/sd.h create mode 100644 linux/src/drivers/scsi/sd_ioctl.c create mode 100644 linux/src/drivers/scsi/seagate.c create mode 100644 linux/src/drivers/scsi/seagate.h create mode 100644 linux/src/drivers/scsi/sr.c create mode 100644 linux/src/drivers/scsi/sr.h create mode 100644 linux/src/drivers/scsi/sr_ioctl.c create mode 100644 linux/src/drivers/scsi/sym53c8xx.c create mode 100644 linux/src/drivers/scsi/sym53c8xx.h create mode 100644 linux/src/drivers/scsi/sym53c8xx_comm.h create mode 100644 linux/src/drivers/scsi/sym53c8xx_defs.h create mode 100644 linux/src/drivers/scsi/t128.c create mode 100644 linux/src/drivers/scsi/t128.h create mode 100644 linux/src/drivers/scsi/tmscsim.c create mode 100644 linux/src/drivers/scsi/tmscsim.h create mode 100644 linux/src/drivers/scsi/u14-34f.c create mode 100644 linux/src/drivers/scsi/u14-34f.h create mode 100644 linux/src/drivers/scsi/ultrastor.c create mode 100644 linux/src/drivers/scsi/ultrastor.h create mode 100644 linux/src/drivers/scsi/wd7000.c create mode 100644 linux/src/drivers/scsi/wd7000.h create mode 100644 linux/src/include/asm-i386/atomic.h create mode 100644 linux/src/include/asm-i386/bitops.h create mode 100644 linux/src/include/asm-i386/byteorder.h create mode 100644 linux/src/include/asm-i386/cache.h create mode 100644 linux/src/include/asm-i386/checksum.h create mode 100644 linux/src/include/asm-i386/delay.h create mode 100644 linux/src/include/asm-i386/dma.h create mode 100644 linux/src/include/asm-i386/errno.h create mode 100644 linux/src/include/asm-i386/fcntl.h create mode 100644 linux/src/include/asm-i386/floppy.h create mode 100644 linux/src/include/asm-i386/hardirq.h create mode 100644 linux/src/include/asm-i386/io.h create mode 100644 linux/src/include/asm-i386/ioctl.h create mode 100644 linux/src/include/asm-i386/ioctls.h create mode 100644 linux/src/include/asm-i386/irq.h create mode 100644 linux/src/include/asm-i386/math_emu.h create mode 100644 linux/src/include/asm-i386/page.h create mode 100644 linux/src/include/asm-i386/param.h create mode 100644 linux/src/include/asm-i386/posix_types.h create mode 100644 linux/src/include/asm-i386/processor.h create mode 100644 linux/src/include/asm-i386/ptrace.h create mode 100644 linux/src/include/asm-i386/resource.h create mode 100644 linux/src/include/asm-i386/segment.h create mode 100644 linux/src/include/asm-i386/semaphore.h create mode 100644 linux/src/include/asm-i386/sigcontext.h create mode 100644 linux/src/include/asm-i386/signal.h create mode 100644 linux/src/include/asm-i386/socket.h create mode 100644 linux/src/include/asm-i386/sockios.h create mode 100644 linux/src/include/asm-i386/spinlock.h create mode 100644 linux/src/include/asm-i386/stat.h create mode 100644 linux/src/include/asm-i386/statfs.h create mode 100644 linux/src/include/asm-i386/string.h create mode 100644 linux/src/include/asm-i386/system.h create mode 100644 linux/src/include/asm-i386/termbits.h create mode 100644 linux/src/include/asm-i386/termios.h create mode 100644 linux/src/include/asm-i386/types.h create mode 100644 linux/src/include/asm-i386/unaligned.h create mode 100644 linux/src/include/asm-i386/unistd.h create mode 100644 linux/src/include/asm-i386/vm86.h create mode 100644 linux/src/include/linux/affs_hardblocks.h create mode 100644 linux/src/include/linux/atalk.h create mode 100644 linux/src/include/linux/ax25.h create mode 100644 linux/src/include/linux/binfmts.h create mode 100644 linux/src/include/linux/bios32.h create mode 100644 linux/src/include/linux/blk.h create mode 100644 linux/src/include/linux/blkdev.h create mode 100644 linux/src/include/linux/cdrom.h create mode 100644 linux/src/include/linux/compatmac.h create mode 100644 linux/src/include/linux/compiler-gcc.h create mode 100644 linux/src/include/linux/compiler-gcc3.h create mode 100644 linux/src/include/linux/compiler-gcc4.h create mode 100644 linux/src/include/linux/compiler-gcc5.h create mode 100644 linux/src/include/linux/compiler.h create mode 100644 linux/src/include/linux/config.h create mode 100644 linux/src/include/linux/ctype.h create mode 100644 linux/src/include/linux/delay.h create mode 100644 linux/src/include/linux/errno.h create mode 100644 linux/src/include/linux/etherdevice.h create mode 100644 linux/src/include/linux/fcntl.h create mode 100644 linux/src/include/linux/fd.h create mode 100644 linux/src/include/linux/fddidevice.h create mode 100644 linux/src/include/linux/fdreg.h create mode 100644 linux/src/include/linux/fs.h create mode 100644 linux/src/include/linux/genhd.h create mode 100644 linux/src/include/linux/hdreg.h create mode 100644 linux/src/include/linux/head.h create mode 100644 linux/src/include/linux/icmp.h create mode 100644 linux/src/include/linux/if.h create mode 100644 linux/src/include/linux/if_arp.h create mode 100644 linux/src/include/linux/if_ether.h create mode 100644 linux/src/include/linux/if_fddi.h create mode 100644 linux/src/include/linux/if_tr.h create mode 100644 linux/src/include/linux/igmp.h create mode 100644 linux/src/include/linux/in.h create mode 100644 linux/src/include/linux/inet.h create mode 100644 linux/src/include/linux/init.h create mode 100644 linux/src/include/linux/interrupt.h create mode 100644 linux/src/include/linux/ioctl.h create mode 100644 linux/src/include/linux/ioport.h create mode 100644 linux/src/include/linux/ip.h create mode 100644 linux/src/include/linux/ipc.h create mode 100644 linux/src/include/linux/ipx.h create mode 100644 linux/src/include/linux/kcomp.h create mode 100644 linux/src/include/linux/kdev_t.h create mode 100644 linux/src/include/linux/kernel.h create mode 100644 linux/src/include/linux/kernel_stat.h create mode 100644 linux/src/include/linux/limits.h create mode 100644 linux/src/include/linux/linkage.h create mode 100644 linux/src/include/linux/list.h create mode 100644 linux/src/include/linux/locks.h create mode 100644 linux/src/include/linux/major.h create mode 100644 linux/src/include/linux/malloc.h create mode 100644 linux/src/include/linux/mc146818rtc.h create mode 100644 linux/src/include/linux/md.h create mode 100644 linux/src/include/linux/mm.h create mode 100644 linux/src/include/linux/module.h create mode 100644 linux/src/include/linux/mount.h create mode 100644 linux/src/include/linux/net.h create mode 100644 linux/src/include/linux/netdevice.h create mode 100644 linux/src/include/linux/netrom.h create mode 100644 linux/src/include/linux/notifier.h create mode 100644 linux/src/include/linux/pagemap.h create mode 100644 linux/src/include/linux/param.h create mode 100644 linux/src/include/linux/pci.h create mode 100644 linux/src/include/linux/personality.h create mode 100644 linux/src/include/linux/posix_types.h create mode 100644 linux/src/include/linux/proc_fs.h create mode 100644 linux/src/include/linux/ptrace.h create mode 100644 linux/src/include/linux/quota.h create mode 100644 linux/src/include/linux/random.h create mode 100644 linux/src/include/linux/resource.h create mode 100644 linux/src/include/linux/rose.h create mode 100644 linux/src/include/linux/route.h create mode 100644 linux/src/include/linux/sched.h create mode 100644 linux/src/include/linux/sem.h create mode 100644 linux/src/include/linux/signal.h create mode 100644 linux/src/include/linux/skbuff.h create mode 100644 linux/src/include/linux/smp.h create mode 100644 linux/src/include/linux/socket.h create mode 100644 linux/src/include/linux/sockios.h create mode 100644 linux/src/include/linux/spinlock.h create mode 100644 linux/src/include/linux/stat.h create mode 100644 linux/src/include/linux/stddef.h create mode 100644 linux/src/include/linux/string.h create mode 100644 linux/src/include/linux/symtab_begin.h create mode 100644 linux/src/include/linux/symtab_end.h create mode 100644 linux/src/include/linux/tasks.h create mode 100644 linux/src/include/linux/tcp.h create mode 100644 linux/src/include/linux/termios.h create mode 100644 linux/src/include/linux/time.h create mode 100644 linux/src/include/linux/timer.h create mode 100644 linux/src/include/linux/tqueue.h create mode 100644 linux/src/include/linux/trdevice.h create mode 100644 linux/src/include/linux/tty.h create mode 100644 linux/src/include/linux/tty_driver.h create mode 100644 linux/src/include/linux/tty_ldisc.h create mode 100644 linux/src/include/linux/types.h create mode 100644 linux/src/include/linux/ucdrom.h create mode 100644 linux/src/include/linux/udp.h create mode 100644 linux/src/include/linux/uio.h create mode 100644 linux/src/include/linux/unistd.h create mode 100644 linux/src/include/linux/utsname.h create mode 100644 linux/src/include/linux/version.h create mode 100644 linux/src/include/linux/vfs.h create mode 100644 linux/src/include/linux/wait.h create mode 100644 linux/src/include/linux/wireless.h create mode 100644 linux/src/include/net/af_unix.h create mode 100644 linux/src/include/net/arp.h create mode 100644 linux/src/include/net/atalkcall.h create mode 100644 linux/src/include/net/ax25.h create mode 100644 linux/src/include/net/ax25call.h create mode 100644 linux/src/include/net/br.h create mode 100644 linux/src/include/net/checksum.h create mode 100644 linux/src/include/net/datalink.h create mode 100644 linux/src/include/net/gc.h create mode 100644 linux/src/include/net/icmp.h create mode 100644 linux/src/include/net/ip.h create mode 100644 linux/src/include/net/ip_alias.h create mode 100644 linux/src/include/net/ip_forward.h create mode 100644 linux/src/include/net/ip_masq.h create mode 100644 linux/src/include/net/ipip.h create mode 100644 linux/src/include/net/ipx.h create mode 100644 linux/src/include/net/ipxcall.h create mode 100644 linux/src/include/net/netlink.h create mode 100644 linux/src/include/net/netrom.h create mode 100644 linux/src/include/net/nrcall.h create mode 100644 linux/src/include/net/p8022.h create mode 100644 linux/src/include/net/p8022call.h create mode 100644 linux/src/include/net/p8022tr.h create mode 100644 linux/src/include/net/p8022trcall.h create mode 100644 linux/src/include/net/protocol.h create mode 100644 linux/src/include/net/psnap.h create mode 100644 linux/src/include/net/psnapcall.h create mode 100644 linux/src/include/net/rarp.h create mode 100644 linux/src/include/net/raw.h create mode 100644 linux/src/include/net/rose.h create mode 100644 linux/src/include/net/rosecall.h create mode 100644 linux/src/include/net/route.h create mode 100644 linux/src/include/net/slhc.h create mode 100644 linux/src/include/net/slhc_vj.h create mode 100644 linux/src/include/net/snmp.h create mode 100644 linux/src/include/net/sock.h create mode 100644 linux/src/include/net/spx.h create mode 100644 linux/src/include/net/tcp.h create mode 100644 linux/src/include/net/udp.h create mode 100644 linux/src/include/scsi/scsi.h create mode 100644 linux/src/include/scsi/scsi_ioctl.h create mode 100644 linux/src/include/scsi/scsicam.h create mode 100644 linux/src/init/main.c create mode 100644 linux/src/init/version.c create mode 100644 linux/src/kernel/dma.c create mode 100644 linux/src/kernel/printk.c create mode 100644 linux/src/kernel/resource.c create mode 100644 linux/src/kernel/sched.c create mode 100644 linux/src/kernel/softirq.c create mode 100644 linux/src/lib/ctype.c create mode 100644 linux/src/lib/vsprintf.c create mode 100644 linux/src/net/core/dev.c create mode 100644 tests/.gitignore create mode 100644 tests/Makefrag.am create mode 100644 tests/README create mode 100644 tests/configfrag.ac create mode 100644 tests/grub.cfg.single.template create mode 100644 tests/include/device/cons.h create mode 120000 tests/include/kern/printf.h create mode 100644 tests/include/mach/mig_support.h create mode 100644 tests/include/syscalls.h create mode 100644 tests/include/testlib.h create mode 120000 tests/include/util/atoi.h create mode 100644 tests/run-qemu.sh.template create mode 100644 tests/start.S create mode 100644 tests/syscalls.S create mode 100644 tests/test-gsync.c create mode 100644 tests/test-hello.c create mode 100644 tests/test-mach_host.c create mode 100644 tests/test-mach_port.c create mode 100644 tests/test-machmsg.c create mode 100644 tests/test-multiboot.in create mode 100644 tests/test-syscalls.c create mode 100644 tests/test-task.c create mode 100644 tests/test-threads.c create mode 100644 tests/test-vm.c create mode 100644 tests/testlib.c create mode 100644 tests/testlib_thread_start.c create mode 100644 tests/user-qemu.mk create mode 100644 util/atoi.c create mode 100644 util/atoi.h create mode 100644 util/byteorder.c create mode 100644 util/byteorder.h create mode 100644 version.c.in create mode 100644 version.m4 create mode 100644 vm/memory_object.c create mode 100644 vm/memory_object.h create mode 100644 vm/memory_object_default.cli create mode 100644 vm/memory_object_proxy.c create mode 100644 vm/memory_object_proxy.h create mode 100644 vm/memory_object_user.cli create mode 100644 vm/pmap.h create mode 100644 vm/vm_debug.c create mode 100644 vm/vm_external.c create mode 100644 vm/vm_external.h create mode 100644 vm/vm_fault.c create mode 100644 vm/vm_fault.h create mode 100644 vm/vm_init.c create mode 100644 vm/vm_init.h create mode 100644 vm/vm_kern.c create mode 100644 vm/vm_kern.h create mode 100644 vm/vm_map.c create mode 100644 vm/vm_map.h create mode 100644 vm/vm_object.c create mode 100644 vm/vm_object.h create mode 100644 vm/vm_page.c create mode 100644 vm/vm_page.h create mode 100644 vm/vm_pageout.c create mode 100644 vm/vm_pageout.h create mode 100644 vm/vm_print.h create mode 100644 vm/vm_resident.c create mode 100644 vm/vm_resident.h create mode 100644 vm/vm_types.h create mode 100644 vm/vm_user.c create mode 100644 vm/vm_user.h create mode 100644 x86_64/Makefrag.am create mode 100644 x86_64/_setjmp.S create mode 100644 x86_64/boothdr.S create mode 100644 x86_64/configfrag.ac create mode 100644 x86_64/copy_user.c create mode 100644 x86_64/cswitch.S create mode 100644 x86_64/debug_trace.S create mode 100644 x86_64/idt_inittab.S create mode 120000 x86_64/include/mach/x86_64 create mode 100644 x86_64/include/syscall_sw.h create mode 100644 x86_64/interrupt.S create mode 100644 x86_64/kdasm.S create mode 100644 x86_64/ldscript create mode 100644 x86_64/locore.S create mode 100644 x86_64/spl.S create mode 120000 x86_64/x86_64 create mode 100644 x86_64/xen_boothdr.S create mode 100644 x86_64/xen_locore.S create mode 100644 xen/Makefrag.am create mode 100644 xen/block.c create mode 100644 xen/block.h create mode 100644 xen/configfrag.ac create mode 100644 xen/console.c create mode 100644 xen/console.h create mode 100644 xen/evt.c create mode 100644 xen/evt.h create mode 100644 xen/grant.c create mode 100644 xen/grant.h create mode 100644 xen/net.c create mode 100644 xen/net.h create mode 100644 xen/public/COPYING create mode 100644 xen/public/arch-x86/xen-mca.h create mode 100644 xen/public/arch-x86/xen-x86_32.h create mode 100644 xen/public/arch-x86/xen-x86_64.h create mode 100644 xen/public/arch-x86/xen.h create mode 100644 xen/public/arch-x86_32.h create mode 100644 xen/public/arch-x86_64.h create mode 100644 xen/public/callback.h create mode 100644 xen/public/dom0_ops.h create mode 100644 xen/public/domctl.h create mode 100644 xen/public/elfnote.h create mode 100644 xen/public/elfstructs.h create mode 100644 xen/public/event_channel.h create mode 100644 xen/public/features.h create mode 100644 xen/public/grant_table.h create mode 100644 xen/public/io/blkif.h create mode 100644 xen/public/io/console.h create mode 100644 xen/public/io/fbif.h create mode 100644 xen/public/io/fsif.h create mode 100644 xen/public/io/kbdif.h create mode 100644 xen/public/io/netif.h create mode 100644 xen/public/io/pciif.h create mode 100644 xen/public/io/protocols.h create mode 100644 xen/public/io/ring.h create mode 100644 xen/public/io/tpmif.h create mode 100644 xen/public/io/xenbus.h create mode 100644 xen/public/io/xs_wire.h create mode 100644 xen/public/kexec.h create mode 100644 xen/public/libelf.h create mode 100644 xen/public/memory.h create mode 100644 xen/public/nmi.h create mode 100644 xen/public/physdev.h create mode 100644 xen/public/platform.h create mode 100644 xen/public/sched.h create mode 100644 xen/public/sysctl.h create mode 100644 xen/public/trace.h create mode 100644 xen/public/vcpu.h create mode 100644 xen/public/version.h create mode 100644 xen/public/xen-compat.h create mode 100644 xen/public/xen.h create mode 100644 xen/public/xencomm.h create mode 100644 xen/public/xenoprof.h create mode 100644 xen/ring.c create mode 100644 xen/ring.h create mode 100644 xen/store.c create mode 100644 xen/store.h create mode 100644 xen/time.c create mode 100644 xen/time.h create mode 100644 xen/xen.c create mode 100644 xen/xen.h diff --git a/=announce-1.0 b/=announce-1.0 new file mode 100644 index 0000000..673dc99 --- /dev/null +++ b/=announce-1.0 @@ -0,0 +1,40 @@ +I am pleased to announce version 1.0 of the GNU distribution of the +Mach kernel. It may be found in the file (about 2.54 MB compressed) +ftp://prep.ai.mit.edu/pub/gnu/gnumach-1.0.tar.gz. + +This distribution was prepared in order to install some bug fixes and +minor improvements (not worth noting here) to the Utah microkernel, +and to make the package conform to the GNU coding standards, at least +minimally, as regards configuration. This was based upon the UK22 +distribution, with some modifications made at Utah as well. + +This kernel will be in the forthcoming complete binary distribution of +GNU. The release is being made now, in the hopes that any serious +problems we didn't notice might be found in advance of that +distribution. Volunteers who are interested in compiling this release +should do so, and please report any problems asap. + +All the non-kernel pieces of the Utah distribution are not present in +this distribution, except for the MiG interface generator which is +still supplied. These other pieces were never used for GNU, and are +not really part of the kernel at all. We may separate MiG into a +separate distribution for convenience at some later date. + +This distribution is only for i386, i486, i586 (pentium), and i686 +(sexium [pentium pro]) processors on PC-AT compatible machines. +Volunteers interested in ports to other architectures are eagerly +sought. + +Most ethernet cards and disk controllers are supported by this kernel; +ones for which Linux drivers exist can be ported easily. Non-network +devices for which BSD drivers exist can be ported with a little +effort, but not much. + +Bug reports relating to this distribution should be sent to +bug-hurd@prep.ai.mit.edu. Requests for assistance should be made on +help-hurd@prep.ai.mit.edu. + +The md5sum checksum for gnumach-1.0.tar.gz is: + +62ac22cbe695a058243673427a264745 gnumach-1.0.tar.gz + diff --git a/=announce-1.1 b/=announce-1.1 new file mode 100644 index 0000000..ef5fa0b --- /dev/null +++ b/=announce-1.1 @@ -0,0 +1,34 @@ +I am pleased to announce version 1.1 of the GNU distribution of the +Mach kernel. It may be found in the file (about 254 MB compressed) +ftp://prep.ai.mit.edu/pub/gnu/gnumach-1.1.tar.gz. + +Diffs from version 1.0 of this distribution are avaiable at +ftp://prep.ai.mit.edu/pub/gnu/gnumach-1.0-1.1.diff (42 KB +uncompressed). + +This is a bug-fixing release; the only new feature over version 1.0 is +the --enable-kdb configure option which has been added to turn on +kernel debugging support. + +Several bugs have been fixed in this distribution, particularly +relating to cross-compilation support. + +This distribution is only for i386, i486, i586 (pentium), and i686 +(sexium [pentium pro]) processors on PC-AT compatible machines. +Volunteers interested in ports to other architectures are eagerly +sought. + +Most ethernet cards and disk controllers are supported by this kernel; +ones for which Linux drivers exist can be ported easily. Non-network +devices for which BSD drivers exist can be ported with a little +effort, but not much. + +Bug reports relating to this distribution should be sent to +bug-hurd@prep.ai.mit.edu. Requests for assistance should be made on +help-hurd@prep.ai.mit.edu. + +The md5sum checksums for the files mentioned in this message are: + +6bb809d81198fd28078c8ac9ccb55965 gnumach-1.1.tar.gz +089b95de887c69c9f1f28b1b1dcb00f7 gnumach-1.0-1.1.diff + diff --git a/=announce-1.1.1 b/=announce-1.1.1 new file mode 100644 index 0000000..244141d --- /dev/null +++ b/=announce-1.1.1 @@ -0,0 +1,36 @@ +I am pleased to announce version 1.1.1 of the GNU distribution of the +Mach kernel. It may be found in the file (about 254 MB compressed) +ftp://prep.ai.mit.edu/pub/gnu/gnumach-1.1.1.tar.gz. + +Diffs from version 1.0 of this distribution are avaiable at +ftp://prep.ai.mit.edu/pub/gnu/gnumach-1.0-1.1.1.diff.gz (about 169 MB +compressed). + +Version 1.1 had some minor accidental problems and is being removed in +favor of this minor update. + +This is a bug-fixing release; the only new feature over version 1.0 is +the --enable-kdb configure option which has been added to turn on +kernel debugging support. + +Several bugs have been fixed in this distribution, particularly +relating to cross-compilation support. + +This distribution is only for i386, i486, i586 (pentium), and i686 +(sexium [pentium pro]) processors on PC-AT compatible machines. +Volunteers interested in ports to other architectures are eagerly +sought. + +Most ethernet cards and disk controllers are supported by this kernel; +ones for which Linux drivers exist can be ported easily. Non-network +devices for which BSD drivers exist can be ported with a little +effort, but not much. + +Bug reports relating to this distribution should be sent to +bug-hurd@prep.ai.mit.edu. Requests for assistance should be made on +help-hurd@prep.ai.mit.edu. + +The md5sum checksums for the files mentioned in this message are: + +45147839691e40b2e67412579e0fcc5d gnumach-1.1.1.tar.gz +35e69cedb4d23b4058be26bd58345a02 gnumach-1.0-1.1.1.diff.Z diff --git a/=announce-1.2 b/=announce-1.2 new file mode 100644 index 0000000..6fc987f --- /dev/null +++ b/=announce-1.2 @@ -0,0 +1,27 @@ +I am pleased to announce version 1.2 of the GNU distribution of the +Mach kernel. It may be found in the file (about 3.64 MB compressed) +ftp://ftp.gnu.org/gnu/gnumach/gnumach-1.2.tar.gz. + +Because of extensive source reorganization, it is not profitable to +provide diffs against previous versions. + +Most notably, this release updates the Linux-derived device drivers to +those found in Linux version 2.0.36. + +This distribution is only for i386, i486, i586 (pentium), and i686 +(sexium [pentium pro]) processors on PC-AT compatible machines. +Volunteers interested in ports to other architectures are eagerly +sought. + +MiG (the Mach Interface Generator) is no longer in this distribution; +it is now distributed separately. You will need MiG in order to be +able to compile this release. + +Bug reports relating to this distribution should be sent to +bug-hurd@gnu.org. Requests for assistance should be made on +help-hurd@gnu.org. + +The md5sum checksums for this distibution is + +85e898a1753270e63a1cc69028043c68 gnumach-1.2.tar.gz + diff --git a/=announce-1.3 b/=announce-1.3 new file mode 100644 index 0000000..2f7edd0 --- /dev/null +++ b/=announce-1.3 @@ -0,0 +1,47 @@ +We are pleased to announce version 1.3 of the GNU distribution of the +Mach kernel. It may be found in the file (about 3.6 MB compressed) +ftp://ftp.gnu.org/gnu/gnumach/gnumach-1.3.tar.gz; +unidiffs from version 1.2 (about 310 KB compressed) are in +ftp://ftp.gnu.org/gnu/gnumach/gnumach-1.2-1.3.diff.gz. + +This distribution is only for x86 PC machines. +Volunteers interested in ports to other architectures are eagerly sought. + +We are no longer actively developing version 1.x of GNU Mach. We plan to +make only necessary bug fixes or trivial enhancements in the 1.x line, +and make further 1.x releases only as necessary for those purposes. +New development efforts have been underway for some time on a new version +of GNU Mach using the OSKit from the University of Utah for hardware +support. Those efforts previously called OSKit-Mach are now working +towards the future version 2.0 of GNU Mach. + +Aside from bug fixes, major changes from 1.2 (from the NEWS file) are: + +The kernel now directly supports "boot scripts" in the form of multiboot +module names with the same syntax as the Hurd's `serverboot' program. +That is, instead of telling GRUB "module /boot/serverboot", you can give +GRUB a series of command like "module /hurd/ext2fs ${...}" where the +syntax after "module" is the same as in boot scripts for Hurd's `serverboot'. + +The kernel message device `kmsg' is now enabled by default. +--disable-kmsg turns it off. + +Large disks (>= 10GB) are now correctly supported, the new get_status +call DEV_GET_RECORDS can return the number of records of a device. + +Lots of tweaks have been done to the virtual memory management to make +it perform better on today's machines. + +The console supports ANSI escape sequences for colors and attributes. + +Support for the terminal speeds B57600 and B115200 has been added. + + +Bug reports relating to this distribution should be sent to +bug-hurd@gnu.org. Requests for assistance should be made on +help-hurd@gnu.org. + +The md5sum checksums for this distribution are: + +61e90803889b079a380e30056b21d076 gnumach-1.3.tar.gz +56ca6aa9040c4d4c4ef7a9757bb0509c gnumach-1.2-1.3.diff.gz diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..246225e --- /dev/null +++ b/AUTHORS @@ -0,0 +1,4 @@ +-*- Text -*- + +The original source of this code is the Mach 3.0 distribution from CMU. +It was subsequently modified by the University of Utah and the GNU Project. diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..623b625 --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/COPYING3 b/COPYING3 new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/COPYING3 @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..9ed3b47 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,8 @@ +e227045b06d62ee7d2fbab9d5ade9030ff43170b is the last commit imported from CVS. +All commits after that one have valid author and committer information. + +Use this to examine the change log for earlier changes: + + $ git show e227045b06d62ee7d2fbab9d5ade9030ff43170b:ChangeLog + $ git show e227045b06d62ee7d2fbab9d5ade9030ff43170b:ChangeLog.0 + $ git show e227045b06d62ee7d2fbab9d5ade9030ff43170b:ChangeLog.00 diff --git a/DEVELOPMENT b/DEVELOPMENT new file mode 100644 index 0000000..0311cce --- /dev/null +++ b/DEVELOPMENT @@ -0,0 +1,84 @@ +-*- Text -*- + +If you're ``just'' looking for something to work on, have a look at the + * bug list, and + * task list, . + +HOW TO CONTRIBUTE LARGER CHUNKS +=============================== + +If you want to help the maintainers to be quickly able to evaluate and +check in your contribution, please try to follow these suggestions: + +Try to mark in the code stuff (i.e. whole functions, parts of header +files) that you've just copied (and then perhaps modified; also note that +briefly) from somewhere else and stuff that you've actually written +yourself. Either do that by simply writing a ChangeLog in parallel (an +informal one is fine as well) or put notes in the modified / imported +files. The one who will be checking in your patches will then probably +remove most of these notes, as soon as everything is written down in the +real ChangeLog. Logging your changes right from the beginning makes it +much easier for the maintainers to track down where which chunk is coming +from, so that they can be handled appropriately. + +HISTORY +======= + +`gnumach-1-branch-before_removing_unused_and_unsupported_code' was tagged +on 2006-02-20. After creating that tag, code for unused and unsupported +device driver for ISA cards and a good deal of i386 dependent, also +unused and unsupported code was removed. + +On 2006-03-19, support was removed for FIPC, which only ever was used +within the native Mach NE2000 NIC device driver, see +. +. + +Support for NORMA was removed on 2006-03-20. +. + +Support for PS2, i860, iPSC 386/860 and MB1/MB2/EXL was removed on +2006-11-05. +. + +Support for the old ipc interface, MACH_IPC_COMPAT, was removed on 2006-12-03. +. + +Support for building without CONTINUATIONS was removed on 2006-12-03. +. + +Support for FP emulation was removed on 2006-12-13. +. + +Support for Olivetti XP7 & XP9 was removed on 2007-01-02. +. + +Support for the `iopl' device and some i/o emulation code (that might be useful +for DOSEMU) was removed on 2007-04-02. +. + + +Be sure to check the ChangeLog and have a look at the repository at that states +if you want to work on those parts of GNU Mach. + +LAYOUT OF THE SOURCE TREE (very incomplete) + + * include/ + +[TODO: Check.] + + ... is mainly for installed header and definition files, but it also holds + pseudo-clones of C library headers, which don't get installed because the C + library has better versions. In that category are , + , , , , + , , and . By putting such + headers into there, the relevant kernel code is easier to understand, + because the user will expect that the file named or + does more or less what the normal C library file does, and + calling those or would make the reader have + to wonder or remember what they are. The directory is, essentially, a + special `/usr/include' for use by the kernel itself when compiling. It + only should get things which belong in `/usr/include'. The reason for + and is because those are files found in + `/usr/include', even if on an actual installed system the versions in + `/usr/include' are provided by a different package. diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..ad38249 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,265 @@ +# Makefile for GNU Mach. + +# Copyright (C) 2006, 2007, 2008, 2009, 2013 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +# +# Various definitions of the Automake environment. +# +# These will be augmented below. +# + +SUBDIRS = +DIST_SUBDIRS = +EXTRA_DIST = +DISTCHECK_CONFIGURE_FLAGS = +noinst_LIBRARIES = +noinst_PROGRAMS = +TESTS= +info_TEXINFOS = +MOSTLYCLEANFILES = +DISTCLEANFILES = +AM_CPPFLAGS = +AM_CCASFLAGS = +AM_CFLAGS = +AM_LDFLAGS = + +# +# Compilation flags +# + +GCC_INSTALL = $(shell LANG=C $(CC) -print-search-dirs | sed -n -e 's/install: \(.*\)/\1/p') +AM_CPPFLAGS += \ + -nostdinc -imacros config.h -I $(GCC_INSTALL)/include + +AM_CPPFLAGS += \ + -I$(systype) \ + -I. \ + -I$(top_srcdir)/$(systype) \ + -I$(top_srcdir)/$(systype)/include/mach/sa \ + -I$(top_srcdir)/include + +AM_CFLAGS += \ + -fno-builtin-log + +AM_CCASFLAGS += \ + -D__ASSEMBLY__ + +# Yes, this makes the eyes hurt. But perhaps someone will finally take care of +# all that scruffy Mach code... Also see . +AM_CFLAGS += \ + -Wall -Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes + +# We need the GNU-style inline +AM_CFLAGS += \ + -fgnu89-inline + +# Much of the Mach code predates C99 and makes invalid assumptions about +# type punning. +AM_CFLAGS += \ + -fno-strict-aliasing + +# The smashing stack protector might be enabled by default, but might emit +# unsuitable code. +if disable_smashing_stack_protector +AM_CFLAGS += \ + -fno-stack-protector +endif + +# We do not support or need position-independent +AM_CFLAGS += \ + -no-pie -fno-PIE -fno-pie -fno-pic + +# This must be the same size as port names, see e.g. ipc/ipc_entry.c +AM_CFLAGS += -DRDXTREE_KEY_32 + +# +# Silent build support. +# + +AWK_V = $(AWK_V_$(V)) +AWK_V_ = $(AWK_V_$(AM_DEFAULT_VERBOSITY)) +AWK_V_0 = @echo " AWK $@"; + +GZIP_V = $(GZIP_V_$(V)) +GZIP_V_ = $(GZIP_V_$(AM_DEFAULT_VERBOSITY)) +GZIP_V_0 = @echo " GZIP $@"; + +NM_V = $(NM_V_$(V)) +NM_V_ = $(NM_V_$(AM_DEFAULT_VERBOSITY)) +NM_V_0 = @echo " NM $@"; + +MIGCOM_V = $(MIGCOM_V_$(V)) +MIGCOM_V_ = $(MIGCOM_V_$(AM_DEFAULT_VERBOSITY)) +MIGCOM_V_0 = @echo " MIG $@"; + +# +# MIG Setup. +# + +# MIGCOM. +MIGCOM = $(MIG) -n -cc cat - /dev/null + +# We need this because we use $(CPP) to preprocess MIG .defs files. +CPP = @CPP@ -x c + +# +# Other Tools' Configuration. +# + +# Don't needlessly overwrite files whose contents haven't changed. +# This helps avoiding unnecessary recompilation cycles when keeping +# cross-compilation toolchains up-to-date. Thus, unconditionally use the +# `install-sh' that is supplied by GNU Automake 1.10.1, as the GNU Coreutils +# one doesn't provide this functionality yet (TODO: change that). TODO: +# `build-aux' is hardcoded. +install_sh = $(SHELL) $(abs_srcdir)/build-aux/install-sh -C +INSTALL = $(install_sh) + +# +# The main kernel functionality. +# + +noinst_LIBRARIES += \ + libkernel.a +libkernel_a_SOURCES = +nodist_libkernel_a_SOURCES = +MOSTLYCLEANFILES += \ + $(nodist_libkernel_a_SOURCES) + +gnumach_o_LDADD = \ + libkernel.a + +gnumach_SOURCES = +gnumach_LINKFLAGS = + +# Makerules: how to do some things. +include Makerules.am + +# Main Makefile fragment. +include Makefrag.am + +# Test suite. +include tests/Makefrag.am + +# Documentation. +include doc/Makefrag.am + +# +# Kernel Image +# + +# We need the following junk because of the include-files-from-libc.a magic. +# TODO. Is the following kosher from a Automake point of view? (I.e. a +# program `gnumach.o' that is then later used again as an object file.) +gnumach_o_SOURCES = +# TODO. ``-u _start''. System dependent? +gnumach_o_LINK = $(LD) $(LDFLAGS) -u _start -r -o $@ +noinst_PROGRAMS += \ + gnumach.o + +# This is the list of routines we use from libgcc. +libgcc_routines := udivdi3 __udivdi3 __udivmoddi4 __umoddi3 __divdi3 __divmoddi4 __moddi3 __ffsdi2 +# References generated by ld. +ld_magic_routines := __rel_iplt_start __rel_iplt_end __rela_iplt_start __rela_iplt_end _START etext _edata _end +gnumach-undef: gnumach.$(OBJEXT) + $(NM_V) $(NM) -u $< | sed 's/ *U *//' | sort -u > $@ +MOSTLYCLEANFILES += gnumach-undef +gnumach-undef-bad: gnumach-undef Makefile + $(AM_V_GEN) sed '$(foreach r,$(libgcc_routines) $(ld_magic_routines),/^$r$$/d;)' $< > $@ +MOSTLYCLEANFILES += gnumach-undef-bad +libgcc-routines.o: gnumach-undef gnumach-undef-bad + $(AM_V_at) if test -s gnumach-undef-bad; \ + then cat gnumach-undef-bad; exit 2; else true; fi + $(AM_V_CCLD) $(CCLD) $(LDFLAGS) -r -static \ + -o $@ `sed 's/^/-Wl,-u,/' < $<` -x c /dev/null -lgcc + @if nm $@ | grep __init_cpu_features; \ + then echo "Please install a 32bit libc without multiarch support (on Debian systems, the libc6-dev:i386 package containing /usr/lib/i386-linux-gnu/libc.a)". ; \ + false ; fi + +gnumach_LINK = $(LD) $(LDFLAGS) $(LINKFLAGS) $(gnumach_LINKFLAGS) -o $@ +gnumach_LDADD = gnumach.o libgcc-routines.o + +# +# Installation. +# + +exec_bootdir = \ + $(exec_prefix)/boot +exec_boot_PROGRAMS = \ + gnumach + +# +# Building a distribution. +# + +EXTRA_DIST += \ + config.status.dep.patch \ + Makefile.in.dep.patch + +EXTRA_DIST += \ + DEVELOPMENT + +dist-hook: dist-rm-CVS gen-ChangeLog + +.PHONY: dist-rm-CVS +dist-rm-CVS: +# Try to be very safe with respect to spuriously removing various directories +# in case of an error. + find $(distdir)/ -type d -name CVS | while read d; do \ + rm -f "$$d"/{Entries,Repository,Root,Tag} && \ + rmdir "$$d"; \ + done + +gen_start_commit = e227045b06d62ee7d2fbab9d5ade9030ff43170b +ChangeLog_files = ChangeLog ChangeLog.0 ChangeLog.00 +.PHONY: gen-ChangeLog +gen-ChangeLog: + $(AM_V_GEN)if test -d $(top_srcdir)/.git; then \ + (cd $(top_srcdir)/ && \ + ./gitlog-to-changelog --strip-tab \ + $(gen_start_commit).. && \ + echo) >> $(distdir)/cl-t && \ + for f in $(ChangeLog_files); do \ + (cd $(top_srcdir)/ && \ + git show $(gen_start_commit):$$f) >> $(distdir)/cl-t && \ + rm -f $(distdir)/$$f && \ + mv $(distdir)/cl-t $(distdir)/$$f \ + || exit $$?; \ + done; \ + fi + +DISTCLEANFILES += \ + Makefile.orig \ + config.status.orig + +# +# Legacy support. +# + +install-headers: install-data + @echo '*****************************************************' + @echo '* As you can see above, I was so kind to rewrite your' + @echo '* `make $@'\' + @echo '* into' + @echo '* `make $^'\' + @echo '* which is how it is to be spelled these days.' + @echo '*' + @echo '* Please get your instructions fixed.' + @echo '*****************************************************' + @echo + @echo 'Penalty:' + sleep 20 diff --git a/Makefile.in.dep.patch b/Makefile.in.dep.patch new file mode 100644 index 0000000..72fb65f --- /dev/null +++ b/Makefile.in.dep.patch @@ -0,0 +1,19 @@ +--- Makefile.in ++++ Makefile.in +@@ -4785,7 +4785,15 @@ distclean-compile: + + $(am__depfiles_remade): + @$(MKDIR_P) $(@D) +- @echo '# dummy' >$@-t && $(am__mv) $@-t $@ ++ # Ugly bootstrap hack to get to-be-generated files created ++ # Try to guess what file this dependency file is from... ++ @f=$(srcdir)/`dirname "$(@D)"`/`basename "$@" .Po | sed s/lib[^-]\*-//` ; \ ++ for f in "$$f"*; do \ ++ case $$f in \ ++ *.c | *.S) echo "$$f"': $$(filter-out $$(DIST_SOURCES),$$(SOURCES))' ;; \ ++ *) echo '# dummy';; \ ++ esac ; \ ++ done >$@-t && $(am__mv) $@-t $@ + + am--depfiles: $(am__depfiles_remade) + diff --git a/Makefrag.am b/Makefrag.am new file mode 100644 index 0000000..5b61a1d --- /dev/null +++ b/Makefrag.am @@ -0,0 +1,611 @@ +# Main Makefile fragment for GNU Mach. + +# Copyright (C) 1997, 1999, 2004, 2006, 2007, 2009 Free Software +# Foundation, Inc. + +# 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. +# +# THE FREE SOFTWARE FOUNDATION ALLOWS FREE USE OF THIS SOFTWARE IN ITS +# "AS IS" CONDITION. THE FREE SOFTWARE FOUNDATION DISCLAIMS ANY +# LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE +# USE OF THIS SOFTWARE. + +# +# DDB support --- eventually to die. Please. +# + +# Do we want the icky kernel debugger? +if enable_kdb +libkernel_a_SOURCES += \ + ddb/db_access.c \ + ddb/db_access.h \ + ddb/db_elf.c \ + ddb/db_elf.h \ + ddb/db_break.c \ + ddb/db_break.h \ + ddb/db_command.c \ + ddb/db_command.h \ + ddb/db_cond.c \ + ddb/db_cond.h \ + ddb/db_examine.c \ + ddb/db_examine.h \ + ddb/db_expr.c \ + ddb/db_expr.h \ + ddb/db_ext_symtab.c \ + ddb/db_input.c \ + ddb/db_input.h \ + ddb/db_lex.c \ + ddb/db_lex.h \ + ddb/db_macro.c \ + ddb/db_macro.h \ + ddb/db_mp.c \ + ddb/db_mp.h \ + ddb/db_output.c \ + ddb/db_output.h \ + ddb/db_print.c \ + ddb/db_print.h \ + ddb/db_run.c \ + ddb/db_run.h \ + ddb/db_sym.c \ + ddb/db_sym.h \ + ddb/db_task_thread.c \ + ddb/db_task_thread.h \ + ddb/db_trap.c \ + ddb/db_trap.h \ + ddb/db_variables.c \ + ddb/db_variables.h \ + ddb/db_watch.c \ + ddb/db_watch.h \ + ddb/db_write_cmd.c \ + ddb/db_write_cmd.h \ + ddb/nlist.h \ + ddb/stab.h \ + ddb/tr.h + +# We need frame pointers for trace to work properly. +AM_CFLAGS += -fno-omit-frame-pointer -fno-optimize-sibling-calls +endif + + +# +# IPC implementation. +# + +libkernel_a_SOURCES += \ + ipc/ipc_entry.c \ + ipc/ipc_entry.h \ + ipc/ipc_init.c \ + ipc/ipc_init.h \ + ipc/ipc_kmsg.c \ + ipc/ipc_kmsg.h \ + ipc/ipc_kmsg_queue.h \ + ipc/ipc_machdep.h \ + ipc/ipc_marequest.c \ + ipc/ipc_marequest.h \ + ipc/ipc_mqueue.c \ + ipc/ipc_mqueue.h \ + ipc/ipc_notify.c \ + ipc/ipc_notify.h \ + ipc/ipc_object.c \ + ipc/ipc_object.h \ + ipc/ipc_port.c \ + ipc/ipc_port.h \ + ipc/ipc_print.h \ + ipc/ipc_pset.c \ + ipc/ipc_pset.h \ + ipc/ipc_right.c \ + ipc/ipc_right.h \ + ipc/ipc_space.c \ + ipc/ipc_space.h \ + ipc/ipc_table.c \ + ipc/ipc_table.h \ + ipc/ipc_target.c \ + ipc/ipc_target.h \ + ipc/ipc_thread.c \ + ipc/ipc_thread.h \ + ipc/ipc_types.h \ + ipc/mach_msg.c \ + ipc/mach_msg.h \ + ipc/mach_port.c \ + ipc/mach_port.h \ + ipc/mach_debug.c \ + ipc/port.h +EXTRA_DIST += \ + ipc/mach_port.srv \ + ipc/notify.defs + + +# +# `kernel' implementation (tasks, threads, trivia, etc.). +# + +libkernel_a_SOURCES += \ + kern/act.c \ + kern/act.h \ + kern/assert.h \ + kern/ast.c \ + kern/ast.h \ + kern/atomic.h \ + kern/boot_script.h \ + kern/bootstrap.c \ + kern/bootstrap.h \ + kern/counters.c \ + kern/counters.h \ + kern/cpu_number.h \ + kern/debug.c \ + kern/debug.h \ + kern/eventcount.c \ + kern/eventcount.h \ + kern/exception.c \ + kern/exception.h \ + kern/gsync.c \ + kern/gsync.h \ + kern/host.c \ + kern/host.h \ + kern/ipc_host.c \ + kern/ipc_host.h \ + kern/ipc_kobject.c \ + kern/ipc_kobject.h \ + kern/ipc_mig.c \ + kern/ipc_mig.h \ + kern/ipc_sched.c \ + kern/ipc_sched.h \ + kern/ipc_tt.c \ + kern/ipc_tt.h \ + kern/kalloc.h \ + kern/kern_types.h \ + kern/kmutex.c \ + kern/kmutex.h \ + kern/list.h \ + kern/lock.c \ + kern/lock.h \ + kern/lock_mon.c \ + kern/log2.h \ + kern/mach_clock.c \ + kern/mach_clock.h \ + kern/mach_factor.c \ + kern/mach_factor.h \ + kern/machine.c \ + kern/machine.h \ + kern/macros.h \ + kern/pc_sample.c \ + kern/pc_sample.h \ + kern/printf.c \ + kern/printf.h \ + kern/priority.c \ + kern/priority.h \ + kern/processor.c \ + kern/processor.h \ + kern/profile.c \ + kern/queue.c \ + kern/queue.h \ + kern/rbtree.c \ + kern/rbtree.h \ + kern/rbtree_i.h \ + kern/rdxtree.c \ + kern/rdxtree.h \ + kern/rdxtree_i.h \ + kern/refcount.h \ + kern/slab.c \ + kern/slab.h \ + kern/smp.h \ + kern/smp.c \ + kern/sched.h \ + kern/sched_prim.c \ + kern/sched_prim.h \ + kern/shuttle.h \ + kern/startup.c \ + kern/startup.h \ + kern/strings.c \ + kern/syscall_emulation.c \ + kern/syscall_emulation.h \ + kern/syscall_subr.c \ + kern/syscall_subr.h \ + kern/syscall_sw.c \ + kern/syscall_sw.h \ + kern/task.c \ + kern/task.h \ + kern/thread.c \ + kern/thread.h \ + kern/thread_swap.c \ + kern/thread_swap.h \ + kern/timer.c \ + kern/timer.h \ + kern/xpr.c \ + kern/xpr.h \ + kern/elf-load.c \ + kern/boot_script.c +EXTRA_DIST += \ + kern/exc.defs \ + kern/mach.srv \ + kern/mach4.srv \ + kern/gnumach.srv \ + kern/experimental.srv \ + kern/mach_debug.srv \ + kern/mach_host.srv \ + kern/task_notify.cli + + +# +# Still more trivia. +# + +libkernel_a_SOURCES += \ + util/atoi.c \ + util/atoi.h \ + util/byteorder.h \ + util/byteorder.c + +# +# Virtual memory implementation. +# + +libkernel_a_SOURCES += \ + vm/memory_object_proxy.c \ + vm/memory_object_proxy.h \ + vm/memory_object.c \ + vm/memory_object.h \ + vm/pmap.h \ + vm/vm_debug.c \ + vm/vm_external.c \ + vm/vm_external.h \ + vm/vm_fault.c \ + vm/vm_fault.h \ + vm/vm_init.c \ + vm/vm_init.h \ + vm/vm_kern.c \ + vm/vm_kern.h \ + vm/vm_map.c \ + vm/vm_map.h \ + vm/vm_object.c \ + vm/vm_object.h \ + vm/vm_page.c \ + vm/vm_page.h \ + vm/vm_pageout.c \ + vm/vm_pageout.h \ + vm/vm_print.h \ + vm/vm_resident.c \ + vm/vm_resident.h \ + vm/vm_types.h \ + vm/vm_user.c \ + vm/vm_user.h +EXTRA_DIST += \ + vm/memory_object_default.cli \ + vm/memory_object_user.cli + + +# +# Device driver support. +# + +# These device support files are always needed; the others are needed only if +# particular drivers want the routines. +# TODO. Functions in device/subrs.c should each be moved elsewhere. +libkernel_a_SOURCES += \ + device/blkio.c \ + device/blkio.h \ + device/buf.h \ + device/chario.c \ + device/chario.h \ + device/cirbuf.h \ + device/conf.h \ + device/cons.c \ + device/cons.h \ + device/device_emul.h \ + device/dev_hdr.h \ + device/dev_lookup.c \ + device/dev_master.h \ + device/dev_name.c \ + device/dev_pager.c \ + device/dev_pager.h \ + device/device_init.c \ + device/device_init.h \ + device/device_port.h \ + device/device_types_kernel.h \ + device/ds_routines.c \ + device/ds_routines.h \ + device/if_ether.h \ + device/if_hdr.h \ + device/intr.c \ + device/intr.h \ + device/io_req.h \ + device/net_io.c \ + device/net_io.h \ + device/param.h \ + device/subrs.c \ + device/subrs.h \ + device/tty.h +EXTRA_DIST += \ + device/device.srv \ + device/device_pager.srv \ + device/device_reply.cli \ + device/memory_object_reply.cli + + +# +# `kmsg' device. +# + +if enable_kmsg +libkernel_a_SOURCES += \ + device/kmsg.c \ + device/kmsg.h +endif + + +# +# Version number. +# + +nodist_libkernel_a_SOURCES += \ + version.c + +# +# Installation. +# + +include_devicedir = $(includedir)/device +include_device_HEADERS = \ + include/device/audio_status.h \ + include/device/bpf.h \ + include/device/device.defs \ + include/device/device_reply.defs \ + include/device/device_request.defs \ + include/device/device_types.defs \ + include/device/device_types.h \ + include/device/disk_status.h \ + include/device/input.h \ + include/device/net_status.h \ + include/device/notify.defs \ + include/device/notify.h \ + include/device/tape_status.h \ + include/device/tty_status.h + +include_machdir = $(includedir)/mach +include_mach_HEADERS = \ + include/mach/default_pager.defs \ + include/mach/default_pager_types.defs \ + include/mach/exc.defs \ + include/mach/mach.defs \ + include/mach/mach4.defs \ + include/mach/gnumach.defs \ + include/mach/task_notify.defs \ + include/mach/mach_host.defs \ + include/mach/mach_port.defs \ + include/mach/mach_types.defs \ + include/mach/memory_object.defs \ + include/mach/memory_object_default.defs \ + include/mach/notify.defs \ + include/mach/std_types.defs \ + include/mach/experimental.defs \ + include/mach/alert.h \ + include/mach/boolean.h \ + include/mach/boot.h \ + include/mach/default_pager_types.h \ + include/mach/exception.h \ + include/mach/host_info.h \ + include/mach/kern_return.h \ + include/mach/mach_param.h \ + include/mach/mach_types.h \ + include/mach/machine.h \ + include/mach/macro_help.h \ + include/mach/memory_object.h \ + include/mach/message.h \ + include/mach/mig_errors.h \ + include/mach/notify.h \ + include/mach/pc_sample.h \ + include/mach/policy.h \ + include/mach/port.h \ + include/mach/processor_info.h \ + include/mach/profil.h \ + include/mach/profilparam.h \ + include/mach/std_types.h \ + include/mach/syscall_sw.h \ + include/mach/task_info.h \ + include/mach/task_special_ports.h \ + include/mach/thread_info.h \ + include/mach/thread_special_ports.h \ + include/mach/thread_status.h \ + include/mach/thread_switch.h \ + include/mach/time_value.h \ + include/mach/version.h \ + include/mach/vm_attributes.h \ + include/mach/vm_cache_statistics.h \ + include/mach/vm_inherit.h \ + include/mach/vm_param.h \ + include/mach/vm_prot.h \ + include/mach/vm_statistics.h \ + include/mach/vm_sync.h \ + include/mach/vm_wire.h \ + include/mach/inline.h \ + include/mach/xen.h + +# If we name this `*_execdir', Automake won't add it to `install-data'... +include_mach_eXecdir = $(includedir)/mach/exec +include_mach_eXec_HEADERS = \ + include/mach/exec/a.out.h \ + include/mach/exec/elf.h \ + include/mach/exec/exec.h + +include_mach_debugdir = $(includedir)/mach_debug +include_mach_debug_HEADERS = \ + $(addprefix include/mach_debug/, \ + hash_info.h \ + mach_debug.defs \ + mach_debug_types.defs \ + mach_debug_types.h \ + vm_info.h \ + slab_info.h \ + ) + +# Other headers for the distribution. We don't install these, because the +# GNU C library has correct versions for users to use. +# other-sys-headers := types.h reboot.h ioctl.h +# other-mach-headers := mig_support.h mach_traps.h error.h +# other-headers := alloca.h + +install-data-hook: + rm -f '$(DESTDIR)$(include_machdir)'/machine + ln -s '$(systype)' '$(DESTDIR)$(include_machdir)'/machine + +# +# Building a distribution. +# + +# Enable all available features. +DISTCHECK_CONFIGURE_FLAGS += \ + --enable-kdb + +# Instead of listing each file individually... +EXTRA_DIST += \ + include + +# +# Automatically generated source files. +# +# See Makerules.mig.am. +# + +# User stubs. +nodist_lib_dep_tr_for_defs_a_SOURCES += \ + vm/memory_object_user.user.defs.c \ + vm/memory_object_default.user.defs.c +nodist_libkernel_a_SOURCES += \ + vm/memory_object_user.user.h \ + vm/memory_object_user.user.c \ + vm/memory_object_user.user.msgids \ + vm/memory_object_default.user.h \ + vm/memory_object_default.user.c \ + vm/memory_object_default.user.msgids +# vm/memory_object_user.user.defs +# vm/memory_object_default.user.defs +nodist_lib_dep_tr_for_defs_a_SOURCES += \ + device/device_reply.user.defs.c \ + device/memory_object_reply.user.defs.c +nodist_libkernel_a_SOURCES += \ + device/device_reply.user.h \ + device/device_reply.user.c \ + device/device_reply.user.msgids \ + device/memory_object_reply.user.h \ + device/memory_object_reply.user.c \ + device/memory_object_reply.user.msgids +# device/device_reply.user.defs +# device/memory_object_reply.user.defs + +nodist_lib_dep_tr_for_defs_a_SOURCES += \ + kern/task_notify.user.defs.c +nodist_libkernel_a_SOURCES += \ + kern/task_notify.user.h \ + kern/task_notify.user.c \ + kern/task_notify.user.msgids + +# Server stubs. +nodist_lib_dep_tr_for_defs_a_SOURCES += \ + device/device.server.defs.c \ + device/device_pager.server.defs.c +nodist_libkernel_a_SOURCES += \ + device/device.server.h \ + device/device.server.c \ + device/device.server.msgids \ + device/device_pager.server.h \ + device/device_pager.server.c \ + device/device_pager.server.msgids +# device/device.server.defs +# device/device_pager.server.defs +nodist_lib_dep_tr_for_defs_a_SOURCES += \ + ipc/mach_port.server.defs.c +nodist_libkernel_a_SOURCES += \ + ipc/mach_port.server.h \ + ipc/mach_port.server.c \ + ipc/mach_port.server.msgids +# ipc/mach_port.server.defs +nodist_lib_dep_tr_for_defs_a_SOURCES += \ + kern/mach.server.defs.c \ + kern/mach4.server.defs.c \ + kern/gnumach.server.defs.c \ + kern/experimental.server.defs.c \ + kern/mach_debug.server.defs.c \ + kern/mach_host.server.defs.c +nodist_libkernel_a_SOURCES += \ + kern/mach.server.h \ + kern/mach.server.c \ + kern/mach.server.msgids \ + kern/mach4.server.h \ + kern/mach4.server.c \ + kern/mach4.server.msgids \ + kern/gnumach.server.h \ + kern/gnumach.server.c \ + kern/gnumach.server.msgids \ + kern/experimental.server.h \ + kern/experimental.server.c \ + kern/experimental.server.msgids \ + kern/mach_debug.server.h \ + kern/mach_debug.server.c \ + kern/mach_debug.server.msgids \ + kern/mach_host.server.h \ + kern/mach_host.server.c \ + kern/mach_host.server.msgids +# kern/mach.server.defs +# kern/mach4.server.defs +# kern/gnumach.server.defs +# kern/experimental.server.defs +# kern/mach_debug.server.defs +# kern/mach_host.server.defs + +# Stand-alone rule to generate the list of message ids when neither +# the client nor the server stubs are required. +nodist_lib_dep_tr_for_defs_a_SOURCES += \ + ipc/notify.none.defs.c \ + kern/exc.none.defs.c +nodist_libkernel_a_SOURCES += \ + ipc/notify.none.msgids \ + kern/exc.none.msgids +# ipc/notify.none.defs + +# rpctrace can make use of that. +MOSTLYCLEANFILES += \ + gnumach.msgids +gnumach.msgids: $(filter %.msgids,$(nodist_libkernel_a_SOURCES)) + $(AM_V_at) cat $^ > $@.new + $(AM_V_GEN) mv $@.new $@ +# `exec_' prefix, so that we don't try to build that file during when running +# `make install-data', as it may fail there, but isn't needed there either. +exec_msgidsdir = $(datadir)/msgids +exec_msgids_DATA = gnumach.msgids + +# +# Specific code. +# + +# Linux device drivers and the glue code. +include linux/Makefrag.am + +# +# Platform specific parts. +# + +# Xen. +if PLATFORM_xen +include xen/Makefrag.am +endif + +# +# Architecture specific parts. +# + +if HOST_ix86 +include i386/Makefrag_x86.am +endif +if HOST_x86_64 +include i386/Makefrag_x86.am +endif + +# ix86. +include i386/Makefrag.am + +# x86_64. +include x86_64/Makefrag.am diff --git a/Makerules.am b/Makerules.am new file mode 100644 index 0000000..5106fef --- /dev/null +++ b/Makerules.am @@ -0,0 +1,54 @@ +# Makerules: how to do some things. + +# Copyright (C) 2006, 2007, 2009 Free Software Foundation, Inc. + +# 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. +# +# THE FREE SOFTWARE FOUNDATION ALLOWS FREE USE OF THIS SOFTWARE IN ITS +# "AS IS" CONDITION. THE FREE SOFTWARE FOUNDATION DISCLAIMS ANY +# LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE +# USE OF THIS SOFTWARE. + +# +# Building foo.h from foo.sym. +# + +EXTRA_DIST += \ + gensym.awk +%.symc: %.sym gensym.awk + $(AWK_V) $(AWK) -f $(word 2,$^) $< > $@ +%.symc.o: %.symc config.h + $(AM_V_CC) $(COMPILE) -S -x c -o $@ $< +%.h: %.symc.o + $(AM_V_GEN) sed < $< > $@ \ + -e 's/^[^*].*$$//' \ + -e 's/^[*]/#define/' \ + -e 's/mAgIc[^-0-9]*//' + +# Makerules.mig: how to do some MIG-related things. +include Makerules.mig.am + +# +# gzip files. +# + +%.gz: % + $(GZIP_V) $(GZIP) -9 < $< > $@ + +# +# strip files. +# + +%.stripped: % + $(STRIP) -o $@ $< + +# +# Echo target. +# + +echo-%: + @echo '$* = `$($*)'\' diff --git a/Makerules.mig.am b/Makerules.mig.am new file mode 100644 index 0000000..8ae6555 --- /dev/null +++ b/Makerules.mig.am @@ -0,0 +1,127 @@ +# Makerules.mig: how to do some MIG-related things. + +# Copyright (C) 2006, 2007, 2009 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# Written by Thomas Schwinge. + +# serial 0 + +# TODO. This file should probably be distributed with GNU MIG and then there +# should be some mechanism so that every package using it is automagically +# using the latest available (or best-matching) version of it. Which is not +# trivial, as the file is already needed to build the build system. But then, +# this file does not really depend on GNU Automake. Hmm... + +# USAGE. + +# Before `include'ing this file, `noinst_LIBRARIES' and `MOSTLYCLEANFILES' have +# to be initialized. + +# For using these rules, `AM_CPPFLAGS', `MIGCOM', `MIGCOMFLAGS', `MIGCOMSFLAGS' +# and `MIGCOMUFLAGS' have to be defined as desired. + +# Then you can (read: currently ``have to''; see below for comments) use +# constructs like: +# +# # User stubs. +# nodist_lib_dep_tr_for_defs_a_SOURCES += \ +# vm/memory_object_user.user.defs.c +# nodist_libkernel_a_SOURCES += \ +# vm/memory_object_user.user.h \ +# vm/memory_object_user.user.c \ +# vm/memory_object_user.user.msgids +# +# # Server stubs. +# nodist_lib_dep_tr_for_defs_a_SOURCES += \ +# device/device.server.defs.c +# nodist_libkernel_a_SOURCES += \ +# device/device.server.h \ +# device/device.server.c \ +# device/device.server.msgids + +# +# Building RPC stubs. +# + +# TODO. Get rid of that stuff, lib_dep_tr_for_defs.a and the four following +# rules. See the thread at +# about what +# we really want to do. This requires work on GNU Automake. + +noinst_LIBRARIES += \ + lib_dep_tr_for_defs.a +nodist_lib_dep_tr_for_defs_a_SOURCES = +MOSTLYCLEANFILES += \ + $(nodist_lib_dep_tr_for_defs_a_SOURCES) +# Preprocess only. +lib_dep_tr_for_defs_a_CPPFLAGS = $(AM_CPPFLAGS) \ + -E + +%.server.defs.c: %.srv + $(AM_V_at) rm -f $@ + $(AM_V_GEN) cp -p $< $@ +%.server.h %.server.c %.server.msgids: lib_dep_tr_for_defs_a-%.server.defs.$(OBJEXT) + $(MIGCOM_V) $(MIGCOM) $(MIGCOMFLAGS) $(MIGCOMSFLAGS) \ + -sheader $*.server.h -server $*.server.c \ + -list $*.server.msgids \ + < $< +%.user.defs.c: %.cli + $(AM_V_at) rm -f $@ + $(AM_V_GEN) cp -p $< $@ +%.user.h %.user.c %.user.msgids: lib_dep_tr_for_defs_a-%.user.defs.$(OBJEXT) + $(MIGCOM_V) $(MIGCOM) $(MIGCOMFLAGS) $(MIGCOMUFLAGS) \ + -user $*.user.c -header $*.user.h \ + -list $*.user.msgids \ + < $< +# Stand-alone rule to generate the list of message ids when neither +# the client nor the server stubs are required. +%.none.defs.c: %.defs + $(AM_V_at) rm -f $@ + $(AM_V_GEN) cp -p $< $@ +%.none.msgids: lib_dep_tr_for_defs_a-%.none.defs.$(OBJEXT) + $(MIGCOM_V) $(MIGCOM) $(MIGCOMFLAGS) \ + -list $*.none.msgids \ + < $< + +# This is how it should be done, but this is not integrated into GNU Automake +# and is missing automatic inter-file dependency management because of that. + +# These chained rules could be (and used to be) single rules using pipes or +# could even --- if you dare to --- use the `mig' shell script, but it's +# convenient to be able to explicitly make the intermediate files when you want +# to deal with a problem in the MIG stub generator. + +# TODO. Get rid of the .srv files and rather use .defs files and MIG*SFLAGS? +#%.server.defs: %.srv +# $(CPP) $(AM_CPPFLAGS) $(CPPFLAGS) -o $@ $< +#%.server.defs: %.defs +# $(CPP) $(AM_CPPFLAGS) $(CPPFLAGS) $(MIGSFLAGS) -o $@ $< +#%.server.h %.server.c %.server.msgids: %.server.defs +# $(MIGCOM) $(MIGCOMFLAGS) $(MIGCOMSFLAGS) \ +# -sheader $*.server.h -server $*.server.c \ +# -list $*.server.msgids \ +# < $< +# TODO. Get rid of the .cli files and rather use .defs files and MIG*UFLAGS? +#%.user.defs: %.cli +# $(CPP) $(AM_CPPFLAGS) $(CPPFLAGS) -o $@ $< +#%.user.defs: %.defs +# $(CPP) $(AM_CPPFLAGS) $(CPPFLAGS) $(MIGUFLAGS) -o $@ $< +#%.user.h %.user.c %.user.msgids: %.user.defs +# $(MIGCOM) $(MIGCOMFLAGS) $(MIGCOMUFLAGS) \ +# -user $*.user.c -header $*.user.h \ +# -list $*.user.msgids \ +# < $< diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..8349550 --- /dev/null +++ b/NEWS @@ -0,0 +1,161 @@ +Version 1.8 (2016-12-18) + +The memory management system was extensively reworked. A new type for +physical addresses is now used where appropriate, and the system can +make use of the high memory segment. Many paging issues have been +addressed, and as a result the system handles low memory situations +more gracefully now. + +The virtual memory system now uses a red-black tree for allocations, +and as a result it now supports tasks with tens of thousands of +mappings. + +Debugging and error reporting has been improved. Among other things +the VM maps are now augmented with names that are used in error +messages, panics and assertions point to their locations, the lock +debugging mechanism has been fixed, and the kernel debugger can now +inspect stack traces reaching into the machine-dependent bits +implemented in assembler. + +As usual, bugs have been fixed throughout the code, including minor +issues with the gsync synchronization mechanism which is now used for +the internal locks in the GNU C Library (glibc). + +The deprecated external memory management interface has been removed. + +The partial ACPI support has been removed. + +Version 1.7 (2016-05-18) + +The code has been updated to work with newer versions of GCC, and numerous bugs +have been fixed throughout the code, including a pageout deadlock. The code +uses integer types from now instead of the old Mach types. + +The VM cache policy change has been merged. The kernel now caches +unreferenced VM objects unconditionally instead of using a fixed +limit. + +The physical page allocator of the X15 kernel has been integrated, and +is now used directly by the slab allocator. This increases the kernel +heap addressing important scalability issues. + +The gsync synchronization mechanism was added, similar to the Linux kernel's +futexes, to allow efficient and powerful userland synchronization. + +Support for profiling kernel code from userland through sampling was added. + +Version 1.6 (2015-10-31) + +The code has been updated to work with newer versions of the compiler, +and numerous bugs have been fixed throughout the code. + +The lock debugging infrastructure has been revived and improved, and +many locking issues have been fixed. + +The IPC tables and the hash table mapping objects to IPC entries have +been replaced by radix trees. This addresses a scalability issue, as +IPC tables required huge amounts of continuous virtual kernel memory. + +The kernel now allows non-privileged users to wire a small amount of +memory. + +A bug hindering the eviction of inactive pages by the pageout daemon +has been identified and fixed. + +The kernel now keeps timestamps relative to the system boot time. +Among other things this fixes bogus uptime readings if the system time +is altered. + +A reference leak in the exception handling mechanism has been +identified and fixed. + +ANSI escape sequences are now handled when using `printf'. This fixes +the formatting of messages printed by various Linux drivers. + +Version 1.5 (2015-04-10) + +Numerous cleanups and stylistic fixes of the code base. Several +problems have been identified using static analysis tools and +subsequently been fixed. + +A protected payload can now be associated with capabilities. This +payload is attached by the kernel to delivered messages and can be +used to speed up the object lookup in the receiving task. + +The kernel debugger can now parse ELF symbol tables, can be invoked +over serial lines, gained two new commands and has received usability +improvements. + +The VM pageout policy has been tuned to accommodate modern hardware. + +The kernel gained partial ACPI support on x86, enough to power down +the system. + +Version 1.4 (2013-09-27) + +Really too many to list them individually. Highlight include numerous bug and +stability fixes, a Xen port for 32-bit x86 including basic support for Physical +Address Extension (PAE), an initial AHCI driver (SATA hard disks), a new SLAB +memory allocator to replace the previous zone allocator, support for memory +object proxies, access restrictions for x86 I/O ports, support for some PCMCIA +devices based on the pcmcia-cs package. + +Version 1.3 + +The kernel now directly supports "boot scripts" in the form of multiboot +module names with the same syntax as the Hurd's `serverboot' program. +That is, instead of telling GRUB "module /boot/serverboot", you can give +GRUB a series of command like "module /hurd/ext2fs ${...}" where the +syntax after "module" is the same as in boot scripts for Hurd's `serverboot'. + +The kernel message device `kmsg' is now enabled by default. +--disable-kmsg turns it off. + +Large disks (>= 10GB) are now correctly supported, the new get_status +call DEV_GET_RECORDS can return the number of records of a device. + +Lots of tweaks have been done to the virtual memory management to make +it perform better on today's machines. + +The console supports ANSI escape sequences for colors and attributes. + +Support for the terminal speeds B57600 and B115200 has been added. + +Version 1.2 + +Many bug fixes. + +The task_basic_info RPC now has an additional field, holding the +creation time of the task. Likewise for thread_basic_info. + +The interface generator `MiG' has been split out. + +Partition names for disks are now printed in the correct way. + +Linux drivers are updated to 2.0.36. Many thanks to Okuji Yoshinori +for great work here. The Linux emulation support is much improved. + +The kernel message device `kmsg' is supported. --enable-kmsg turns on +the device. + +The parallel driver is enabled by --enable-lpr. + +New make targets, install-kernel and install-headers are added. The +former will install only the kernel, and the latter will install only +the header files. + +Print out Mach device names instead of Linux ones. + +Version 1.1 + +Cross-compilation support is much improved. Any of various popular +libc's is now sufficient for building clib-routines.o. + +New configure option --enable-kdb asks for kernel debugger to be +compiled in. + +Bug in --enable-ncr53c7xx has been fixed. + +Many thanks go to Marcus G. Daniels (marcus@cathcart.sysc.pdx.edu) for +his very helpful testing of the 1.0 release and for his many +improvements to the cross-compilation support. diff --git a/README b/README new file mode 100644 index 0000000..108452a --- /dev/null +++ b/README @@ -0,0 +1,56 @@ +This is GNU Mach, the GNU distribution of the Mach microkernel, +. Welcome. + +GNU Mach is the microkernel upon which a GNU Hurd system is based. It +provides an Inter Process Communication (IPC) mechanism that the Hurd +uses to define interfaces for implementing in a distributed multi-server +fashion the services a traditional operating system kernel provides. + +GNU Mach runs on 32-bit x86 machines. A version running on 64-bit x86 +(x86_64) machines is in progress. Volunteers interested in ports to +other architectures are sought; please contact us (see below) if you'd +like to help. + +libmach, bootloaders, default pagers, and the like are not part of +this distribution. For libraries, we refer you to the GNU C Library, +which has Mach support. For bootloaders, we refer you to GRUB. (This +kernel can be loaded by any bootloader that uses the multiboot +standard.) For default pagers, we refer you to your particular system +that you will run on top of Mach. + +The Mach Interface Generator (MIG) is no longer part of this distribution, and +instead is packaged separately: GNU MIG. + +Generic installation instructions may be found in the file INSTALL. + +By default, most drivers for network boards are included, as well as +drivers for IDE, SCSI and AHCI disks. + +If you want the in-kernel debugger compiled in, specify --enable-kdb +to configure. This is only useful if you actually anticipate +debugging the kernel, of course. We don't turn it on by default +because it adds considerably to the unpageable memory footprint of the +kernel. + +GNU Mach can be cross-built. No specific options need to be given when +building on a 32-bit x86 ELF userland such as GNU/Linux. Manually switch the +compiler to 32-bit mode when using a 64-bit x86 (x86_64) ELF toolchain: + + $ [...]/configure --host=i686-gnu CC='gcc -m32' LD='ld -melf_i386' + +or point to a 32-bit ELF toolchain: + + $ [...]/configure --host=i686-gnu CC=i686-linux-gnu-gcc LD=i686-linux-gnu-ld + +Also, GNU MIG needs to be a 32bit version to properly compile the interfaces, +you can specify for instance + + $ [...]/configure --host=i686-gnu CC=i686-linux-gnu-gcc LD=i686-linux-gnu-ld MIG=i686-linux-gnu-mig + + +Please read the FAQ at . +Bug reports should be sent to or filed on +. Requests for assistance +should be sent to or filed on +. You can also find us on +the Freenode IRC network in the #hurd channel. diff --git a/chips/busses.c b/chips/busses.c new file mode 100644 index 0000000..3811d0c --- /dev/null +++ b/chips/busses.c @@ -0,0 +1,232 @@ +/* + * Mach Operating System + * Copyright (c) 1993-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. + */ +/* + * File: busses.c + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 4/90 + * + * Generic autoconfiguration functions, + * usable to probe and attach devices + * on any bus that suits the generic bus + * structure, such as VME, TURBOChannel, + * and all the VAX busses. + * + */ + +#include +#include +#include +#include +#include + + + + +/* + * configure_bus_master + * + * Given the name of a bus_ctlr, look it up in the + * init table. If found, probe it. If there can be + * slaves attached, walk the device's init table + * for those that might be attached to this controller. + * Call the 'slave' function on each one to see if + * ok, then the 'attach' one. + * + * Returns 0 if the controller is not there. + * + */ +boolean_t configure_bus_master( + const char *name, + vm_offset_t virt, + vm_offset_t phys, + int adpt_no, + const char *bus_name) +{ + struct bus_device *device; + struct bus_ctlr *master; + struct bus_driver *driver; + + boolean_t found = FALSE; + + /* + * Match the name in the table, then pick the entry that has the + * right adaptor number, or one that has it wildcarded. Entries + * already allocated are marked alive, skip them. + */ + for (master = bus_master_init; master->driver; master++) { + if (master->alive) + continue; + if (((master->adaptor == adpt_no) || (master->adaptor == '?')) && + (strcmp(master->name, name) == 0)) { + found = TRUE; + break; + } + } + + if (!found) + return FALSE; + + /* + * Found a match, probe it + */ + driver = master->driver; + if ((*driver->probe) (virt, master) == 0) + return FALSE; + + master->alive = 1; + master->adaptor = adpt_no; + + /* + * Remember which controller this device is attached to + */ + driver->minfo[master->unit] = master; + + printf("%s%d: at %s%d\n", master->name, master->unit, bus_name, adpt_no); + + /* + * Now walk all devices to check those that might be attached to this + * controller. We match the unallocated ones that have the right + * controller number, or that have a widcarded controller number. + */ + for (device = bus_device_init; device->driver; device++) { + int ctlr; + if (device->alive || device->driver != driver || + (device->adaptor != '?' && device->adaptor != adpt_no)) + continue; + ctlr = device->ctlr; + if (ctlr == '?') device->ctlr = master->unit; + /* + * A matching entry. See if the slave-probing routine is + * happy. + */ + if ((device->ctlr != master->unit) || + ((*driver->slave) (device, virt) == 0)) { + device->ctlr = ctlr; + continue; + } + + device->alive = 1; + device->adaptor = adpt_no; + device->ctlr = master->unit; + + /* + * Save a backpointer to the controller + */ + device->mi = master; + + /* + * ..and to the device + */ + driver->dinfo[device->unit] = device; + + if (device->slave >= 0) + printf(" %s%d: at %s%d slave %d", + device->name, device->unit, + driver->mname, master->unit, device->slave); + else + printf(" %s%d: at %s%d", + device->name, device->unit, + driver->mname, master->unit); + + /* + * Now attach this slave + */ + (*driver->attach) (device); + printf("\n"); + } + return TRUE; +} + +/* + * configure_bus_device + * + * Given the name of a bus_device, look it up in the + * init table. If found, probe it. If it is present, + * call the driver's 'attach' function. + * + * Returns 0 if the device is not there. + * + */ +boolean_t configure_bus_device( + const char *name, + vm_offset_t virt, + vm_offset_t phys, + int adpt_no, + const char *bus_name) +{ + struct bus_device *device; + struct bus_driver *driver; + + boolean_t found = FALSE; + + /* + * Walk all devices to find one with the right name + * and adaptor number (or wildcard). The entry should + * be unallocated, and also the slave number should + * be wildcarded. + */ + for (device = bus_device_init; device->driver; device++) { + if (device->alive) + continue; + if (((device->adaptor == adpt_no) || (device->adaptor == '?')) && + (device->slave == -1) && + ((!device->phys_address) || + ((device->phys_address == phys) && (device->address == virt))) && + (strcmp(device->name, name) == 0)) { + found = TRUE; + break; + } + } + + if (!found) + return FALSE; + + /* + * Found an entry, probe the device + */ + driver = device->driver; + if ((*driver->probe) (virt, (struct bus_ctlr *)device) == 0) + return FALSE; + + device->alive = 1; + device->adaptor = adpt_no; + + printf("%s%d: at %s%d", device->name, device->unit, bus_name, adpt_no); + + /* + * Remember which driver this device is attached to + */ + driver->dinfo[device->unit] = device; + + /* + * Attach the device + */ + (*driver->attach) (device); + printf("\n"); + + return TRUE; +} + diff --git a/chips/busses.h b/chips/busses.h new file mode 100644 index 0000000..90eebc6 --- /dev/null +++ b/chips/busses.h @@ -0,0 +1,154 @@ +/* + * Mach Operating System + * Copyright (c) 1994-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. + */ +/* + * File: busses.h + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 4/90 + * + * Structures used by configuration routines to + * explore a given bus structure. + */ + +#ifndef _CHIPS_BUSSES_H_ +#define _CHIPS_BUSSES_H_ + +#include +#include + +/* + * + * This is mildly modeled after the Unibus on Vaxen, + * one of the most complicated bus structures. + * Therefore, let's hope this can be done once and for all. + * + * At the bottom level there is a "bus_device", which + * might exist in isolation (e.g. a clock on the CPU + * board) or be a standard component of an architecture + * (e.g. the bitmap display on some workstations). + * + * Disk devices and communication lines support multiple + * units, hence the "bus_driver" structure which is more + * flexible and allows probing and dynamic configuration + * of the number and type of attached devices. + * + * At the top level there is a "bus_ctlr" structure, used + * in systems where the I/O bus(ses) are separate from + * the memory bus(ses), and/or when memory boards can be + * added to the main bus (and they must be config-ed + * and/or can interrupt the processor for ECC errors). + * + * The autoconfiguration process typically starts at + * the top level and walks down tables that are + * defined either in a generic file or are specially + * created by config. + */ + +/* + * Per-controller structure. + */ +struct bus_ctlr { + struct bus_driver *driver; /* myself, as a device */ + char *name; /* readability */ + int unit; /* index in driver */ + void (*intr)(int); /* interrupt handler(s) */ + vm_offset_t address; /* device virtual address */ + int am; /* address modifier */ + vm_offset_t phys_address;/* device phys address */ + char adaptor; /* slot where found */ + char alive; /* probed successfully */ + char flags; /* any special conditions */ + vm_offset_t sysdep; /* On some systems, queue of + * operations in-progress */ + natural_t sysdep1; /* System dependent */ +}; + + +/* + * Per-``device'' structure + */ +struct bus_device { + struct bus_driver *driver; /* autoconf info */ + char *name; /* my name */ + int unit; + void (*intr)(int); + vm_offset_t address; /* device address */ + int am; /* address modifier */ + vm_offset_t phys_address;/* device phys address */ + char adaptor; + char alive; + char ctlr; + char slave; + int flags; + struct bus_ctlr *mi; /* backpointer to controller */ + struct bus_device *next; /* optional chaining */ + vm_offset_t sysdep; /* System dependent */ + natural_t sysdep1; /* System dependent */ +}; + +/* + * General flag definitions + */ +#define BUS_INTR_B4_PROBE 0x01 /* enable interrupts before probe */ +#define BUS_INTR_DISABLED 0x02 /* ignore all interrupts */ +#define BUS_CTLR 0x04 /* descriptor for a bus adaptor */ +#define BUS_XCLU 0x80 /* want exclusive use of bdp's */ + +/* + * Per-driver structure. + * + * Each bus driver defines entries for a set of routines + * that are used at boot time by the configuration program. + */ +struct bus_driver { + int (*probe)( /* see if the driver is there */ + vm_offset_t address, + struct bus_ctlr *); + int (*slave)( /* see if any slave is there */ + struct bus_device *, + vm_offset_t); + void (*attach)( /* setup driver after probe */ + struct bus_device *); + int (*dgo)(struct bus_device *); /* start transfer */ + vm_offset_t *addr; /* device csr addresses */ + char *dname; /* name of a device */ + struct bus_device **dinfo; /* backpointers to init structs */ + char *mname; /* name of a controller */ + struct bus_ctlr **minfo; /* backpointers to init structs */ + int flags; +}; + +#ifdef KERNEL +extern struct bus_ctlr bus_master_init[]; +extern struct bus_device bus_device_init[]; + +extern boolean_t configure_bus_master(const char *, vm_offset_t, vm_offset_t, + int, const char * ); +extern boolean_t configure_bus_device(const char *, vm_offset_t, vm_offset_t, + int, const char * ); +#endif /* KERNEL */ + + +#endif /* _CHIPS_BUSSES_H_ */ diff --git a/config.status.dep.patch b/config.status.dep.patch new file mode 100644 index 0000000..868737b --- /dev/null +++ b/config.status.dep.patch @@ -0,0 +1,18 @@ +--- config.status 2009-10-26 23:57:14.000000000 +0100 ++++ config.status.new 2009-10-27 00:04:26.000000000 +0100 +@@ -1553,7 +1553,14 @@ + s/.*/./; q'` + as_dir=$dirpart/$fdir; as_fn_mkdir_p + # echo "creating $dirpart/$file" +- echo '# dummy' > "$dirpart/$file" ++ # Try to guess what file this dependency file is from... ++ f=$srcdir/`dirname "$fdir"`/`basename "$file" .Po | sed s/lib[^-]\*-//` ++ for f in "$f"*; do ++ case $f in ++ *.c | *.S) echo "$f"': $(filter-out $(DIST_SOURCES),$(SOURCES))';; ++ *) echo '# dummy';; ++ esac ++ done > "$dirpart/$file" + done + done + } diff --git a/configfrag-first.ac b/configfrag-first.ac new file mode 100644 index 0000000..5dc0db2 --- /dev/null +++ b/configfrag-first.ac @@ -0,0 +1,29 @@ +dnl Configure fragment for general options. + +dnl Copyright (C) 2020 Free Software Foundation, Inc. + +dnl Permission to use, copy, modify and distribute this software and its +dnl documentation is hereby granted, provided that both the copyright +dnl notice and this permission notice appear in all copies of the +dnl software, derivative works or modified versions, and any portions +dnl thereof, and that both notices appear in supporting documentation. +dnl +dnl THE FREE SOFTWARE FOUNDATION ALLOWS FREE USE OF THIS SOFTWARE IN ITS +dnl "AS IS" CONDITION. THE FREE SOFTWARE FOUNDATION DISCLAIMS ANY +dnl LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE +dnl USE OF THIS SOFTWARE. + +# +# Common options +# + +ncom=0 +nlpr=0 + +AC_ARG_ENABLE([pae], + AS_HELP_STRING([--enable-pae], [PAE support (ix86-only); on i386-at disabled + by default, otherwise enabled by default])) + +dnl Local Variables: +dnl mode: autoconf +dnl End: diff --git a/configfrag.ac b/configfrag.ac new file mode 100644 index 0000000..b8b4126 --- /dev/null +++ b/configfrag.ac @@ -0,0 +1,183 @@ +dnl Configure fragment for general options. + +dnl Copyright (C) 2006, 2007 Free Software Foundation, Inc. + +dnl Permission to use, copy, modify and distribute this software and its +dnl documentation is hereby granted, provided that both the copyright +dnl notice and this permission notice appear in all copies of the +dnl software, derivative works or modified versions, and any portions +dnl thereof, and that both notices appear in supporting documentation. +dnl +dnl THE FREE SOFTWARE FOUNDATION ALLOWS FREE USE OF THIS SOFTWARE IN ITS +dnl "AS IS" CONDITION. THE FREE SOFTWARE FOUNDATION DISCLAIMS ANY +dnl LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE +dnl USE OF THIS SOFTWARE. + +# +# Definitions. +# + +AC_DEFINE([MACH], [1], [MACH]) +AC_DEFINE([CMU], [1], [CMU]) +AC_DEFINE([MACH_KERNEL], [1], [Standalone MACH kernel]) +AC_DEFINE([KERNEL], [1], [KERNEL]) + +# +# Formerly in `bogus/'. +# + +# i386/bogus/com.h +AC_DEFINE_UNQUOTED([NCOM], [$ncom], [NCOM]) + +# i386/bogus/lpr.h +AC_DEFINE_UNQUOTED([NLPR], [$nlpr], [NLPR]) + +[if [ x"$enable_pae" = xyes ]; then] + AC_DEFINE([PAE], [1], [PAE support]) + AM_CONDITIONAL([enable_pae], [true]) +[else] + AM_CONDITIONAL([enable_pae], [false]) +[fi] + +# When set, the bootstrap task symbols are preserved by the kernel debugger. +# Used in `kern/bootstrap.c'. +AC_DEFINE([BOOTSTRAP_SYMBOLS], [0], [BOOTSTRAP_SYMBOLS]) + +# Multiprocessor support is still broken. +AH_TEMPLATE([MULTIPROCESSOR], [set things up for a uniprocessor]) +AC_ARG_ENABLE([ncpus], + AS_HELP_STRING([--enable-ncpus=N], [specify the maximum number of cpus to be supported]), + [mach_ncpus=$enable_ncpus], + [mach_ncpus=1]) +AC_DEFINE_UNQUOTED([NCPUS], [$mach_ncpus], [number of CPUs]) +[if [ $mach_ncpus -gt 1 ]; then] + AC_DEFINE([MULTIPROCESSOR], [1], [set things up for a multiprocessor]) +[fi] + +# Restartable Atomic Sequences to get a really fast test-n-set. Can't be +# enabled, as the `void recover_ras()' function is missing. +AC_DEFINE([FAST_TAS], [0], [FAST_TAS]) + +# Cache footprint support. +AC_DEFINE([HW_FOOTPRINT], [0], [HW_FOOTPRINT]) + +# Counters. +AC_DEFINE([MACH_COUNTERS], [0], [MACH_COUNTERS]) + +# IPC debugging interface. +AC_DEFINE([MACH_DEBUG], [1], [MACH_DEBUG]) + +# Fixed priority threads. +AC_DEFINE([MACH_FIXPRI], [1], [MACH_FIXPRI]) + +# Mach host (cpu resource alloc.). +[if [ $mach_ncpus -gt 1 ]; then] + AC_DEFINE([MACH_HOST], [1], [MACH_HOST]) +[else] + AC_DEFINE([MACH_HOST], [0], [MACH_HOST]) +[fi] + +# IPC debugging calls. +AC_DEFINE([MACH_IPC_DEBUG], [1], [MACH_IPC_DEBUG]) + +# Testing code/printfs. +AC_DEFINE([MACH_IPC_TEST], [0], [MACH_IPC_TEST]) + +# Sanity-check locking. +AC_DEFINE([MACH_LDEBUG], [0], [MACH_LDEBUG]) + +# MP lock monitoring. Registers use of locks, contention. Depending on +# hardware also records time spent with locks held. Used in `kern/lock_mon.c'. +AC_DEFINE([MACH_LOCK_MON], [0], [MACH_LOCK_MON]) + +# Does the architecture provide machine-specific interfaces? +mach_machine_routines=${mach_machine_routines-0} +AC_DEFINE_UNQUOTED([MACH_MACHINE_ROUTINES], [$mach_machine_routines], + [MACH_MACHINE_ROUTINES]) + +# MP debugging. Use alternate locking routines to detect deadlocks. Used in +# `kern/lock_mon.c'. +AC_DEFINE([MACH_MP_DEBUG], [0], [MACH_MP_DEBUG]) + +# Paged-out page map hints. +AC_DEFINE([MACH_PAGEMAP], [1], [MACH_PAGEMAP]) + +# Do pc sample histogram. +[if [ $mach_ncpus -gt 1 ]; then] + # Apparently not MP-safe yet. + AC_DEFINE([MACH_PCSAMPLE], [0], [MACH_PCSAMPLE]) +[else] + AC_DEFINE([MACH_PCSAMPLE], [1], [MACH_PCSAMPLE]) +[fi] + +# Sample kernel too. +AC_ARG_ENABLE([kernsample], + AS_HELP_STRING([--enable-kernsample], [enable sampling kernel])) +[if [ x"$enable_kernsample" = xyes ]; then] + AC_DEFINE([MACH_KERNSAMPLE], [1], [MACH_KERNSAMPLE]) +[else] + AC_DEFINE([MACH_KERNSAMPLE], [0], [MACH_KERNSAMPLE]) +[fi] + +# TTD Remote Kernel Debugging. +AC_DEFINE([MACH_TTD], [0], [MACH_TTD]) + +# VM debugging calls. +AC_DEFINE([MACH_VM_DEBUG], [1], [MACH_VM_DEBUG]) + +# Mach-dep power conservation. +AC_DEFINE([POWER_SAVE], [1], [POWER_SAVE]) + +# Use statistical timing. +AC_DEFINE([STAT_TIME], [1], [STAT_TIME]) + +# Kernel tracing. +AC_DEFINE([XPR_DEBUG], [0], [XPR_DEBUG]) + +# Slab allocator debugging facilities. +AC_DEFINE([SLAB_VERIFY], [0], [SLAB_VERIFY]) + +# Enable the CPU pool layer in the slab allocator. +AC_DEFINE([SLAB_USE_CPU_POOLS], [0], [SLAB_USE_CPU_POOLS]) + +# +# Options. +# + +AC_HEADER_ASSERT() + +AC_ARG_ENABLE([kdb], + AS_HELP_STRING([--enable-kdb], [enable use of in-kernel debugger])) +[if [ x"$enable_kdb" = xyes ]; then] + AC_DEFINE([MACH_KDB], [1], [Use the in-kernel debugger?]) + AM_CONDITIONAL([enable_kdb], [true]) +[else] + # We need to be long winded here: bogus/mach_kdb.h made it default to zero, + # unless overridden. + AC_DEFINE([MACH_KDB], [0], [Use the in-kernel debugger?]) + AM_CONDITIONAL([enable_kdb], [false]) +[fi] + + +AC_ARG_ENABLE([kmsg], + AS_HELP_STRING([--disable-kmsg], [disable use of kmsg device])) +[if [ x"$enable_kmsg" != xno ]; then] + AC_DEFINE([MACH_KMSG], [], [enable use of kmsg device]) + AM_CONDITIONAL([enable_kmsg], [true]) +[else] + AM_CONDITIONAL([enable_kmsg], [false]) +[fi] + +# +# Set up `SYSTYPE/SYSTYPE' and `SYSTYPE/include/mach/SYSTYPE' links. +# + +# `${file}' and `$file' have different meanings here with respect to having the +# files in the referenced directory considered for `make dist' or not. See +# . +AC_CONFIG_LINKS([machine:$srcdir/$systype/$systype + mach/machine:$systype/include/mach/$systype]) + +dnl Local Variables: +dnl mode: autoconf +dnl End: diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..69f75cf --- /dev/null +++ b/configure.ac @@ -0,0 +1,270 @@ +dnl Configure script for GNU Mach. + +dnl Copyright (C) 1997, 1998, 1999, 2004, 2006, 2007, 2008, 2010, 2013 Free +dnl Software Foundation, Inc. + +dnl Permission to use, copy, modify and distribute this software and its +dnl documentation is hereby granted, provided that both the copyright +dnl notice and this permission notice appear in all copies of the +dnl software, derivative works or modified versions, and any portions +dnl thereof, and that both notices appear in supporting documentation. +dnl +dnl THE FREE SOFTWARE FOUNDATION ALLOWS FREE USE OF THIS SOFTWARE IN ITS +dnl "AS IS" CONDITION. THE FREE SOFTWARE FOUNDATION DISCLAIMS ANY +dnl LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE +dnl USE OF THIS SOFTWARE. + +AC_PREREQ([2.57]) + +m4_include([version.m4]) +AC_INIT([AC_PACKAGE_NAME], [AC_PACKAGE_VERSION], [AC_PACKAGE_BUGREPORT], + [AC_PACKAGE_TARNAME]) +AC_CONFIG_SRCDIR([kern/ipc_kobject.c]) + +if test -z "${CFLAGS+set}"; then + # Use these CFLAGS by default if nothing is set. + CFLAGS="-g -O2" +fi +# We don't need glibc to compile gnumach. +CFLAGS="$CFLAGS -ffreestanding -nostdlib" + +AC_CONFIG_AUX_DIR([build-aux]) + +AM_INIT_AUTOMAKE( + [1.10.2] + [dist-xz] +dnl Don't define `PACKAGE' and `VERSION'. + [no-define] +dnl Do not clutter the main build directory. + [subdir-objects] +dnl We require GNU make. + [-Wall -Wno-portability] +) + +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([no])], + [AC_SUBST([AM_DEFAULT_VERBOSITY], [1])]) + +# +# Deduce the architecture we're building for. +# +# TODO: Should we also support constructs like `i686_xen-pc-gnu' or +# `i686-pc_xen-gnu'? + +AC_CANONICAL_HOST + +AC_ARG_ENABLE([platform], + AS_HELP_STRING([--enable-platform=PLATFORM], [specify the platform to build a + kernel for. Defaults to `at' for `i?86'. The other possibility is + `xen'.]), + [host_platform=$enable_platform], + [host_platform=default]) +[# Supported configurations. +case $host_platform:$host_cpu in + default:i?86) + host_platform=at;; + default:x86_64)] + [host_platform=at;; + at:i?86 | xen:i?86 | at:x86_64 | xen:x86_64) + :;; + *)] + AC_MSG_ERROR([unsupported combination of cpu type `$host_cpu' and platform + `$host_platform'.])[;; +esac] +AC_SUBST([host_platform]) + +[# This is used in a few places. +case $host_cpu in + i?86) + systype=i386;; + *) + systype=$host_cpu;; +esac] +AC_SUBST([systype]) + +# +# Programs. +# + +AC_PROG_AWK +# Temporarily force cross compiling mode to make sure the configure script +# does not try to run compiled binaries. +save_cross_compiling=$cross_compiling +cross_compiling=yes +AM_PROG_AS +cross_compiling=$save_cross_compiling +AC_PROG_CC +AC_PROG_CPP +AC_PROG_INSTALL +AC_PROG_RANLIB +AC_CHECK_TOOL([AR], [ar]) +AC_CHECK_TOOL([LD], [ld]) +AC_CHECK_TOOL([NM], [nm]) + +AC_CHECK_TOOL([MIG], [mig], [no]) +AC_ARG_VAR([MIG], [Path to the mig tool]) + +if test x$MIG = xno +then + AC_MSG_WARN([mig was not found, we will not be able to build a kernel, only install headers. Install or build mig against them, and run configure again. If you already did so, perhaps you need to specify the path with MIG=]) + MIG=mig +fi + +dnl Needed for the Automake option `subdir-objects'. +AM_PROG_CC_C_O + +dnl Makerules can make use of these. +AC_CHECK_PROG([GZIP], [gzip], [gzip], [gzip-not-found]) +AC_CHECK_TOOL([STRIP], [strip]) + +dnl See below why we need to patch stuff during build... +AC_CHECK_PROG([PATCH], [patch], [patch], [patch-not-found]) + +# +# configure fragments. +# + +# Default set of device drivers. +AC_ARG_ENABLE([device-drivers], + AS_HELP_STRING([--enable-device-drivers=WHICH], [specify WHICH (on `ix86-at' + one of `default', `qemu', `none') to preset a certain subset of all + available device drivers, as indicated by the below-metioned ``enabled + ...'' comments; you can then still use further `--enable-*' or + `--disable-*' options to refine the selection of drivers to include in + order to choose only those you actually want to have enabled])) +[case $enable_device_drivers in + '') + enable_device_drivers=default;; + no) + enable_device_drivers=none;; + default | none | qemu) + :;; + *)] + AC_MSG_ERROR([invalid choice of] + [`--enable-device-drivers=$enable_device_drivers'.]) + [;; +esac] + +AC_ARG_ENABLE([user32], +AS_HELP_STRING([--enable-user32], [enable 32-bit user space on a 64-bit kernel])) +[if [ x"$enable_user32" = xyes ]; then] + AC_DEFINE([USER32], [], [enable 32-bit user on 64-bit kernel]) + AM_CONDITIONAL([enable_user32], [true]) +[else] + AM_CONDITIONAL([enable_user32], [false]) +[fi] + + +# Platform-specific configuration. + +# PC AT. +# TODO. Currently handled in `i386/configfrag.ac'. + +# General options. +m4_include([configfrag-first.ac]) + +# Xen. +m4_include([xen/configfrag.ac]) + +# Machine-specific configuration. + +# ix86. +m4_include([i386/configfrag.ac]) + +# x86_64 +m4_include([x86_64/configfrag.ac]) + +# General options. +m4_include([configfrag.ac]) + +# Linux code snarfed into GNU Mach. +m4_include([linux/configfrag.ac]) + +# The test suite. +m4_include([tests/configfrag.ac]) + +# +# Compiler features. +# + +# Smashing stack protector. +[ssp_possible=yes] +AC_MSG_CHECKING([whether the compiler accepts `-fstack-protector']) +# Is this a reliable test case? +AC_LANG_CONFTEST( + [AC_LANG_SOURCE([[void foo (void) { volatile char a[8]; a[3]; }]])]) +[# `$CC -c -o ...' might not be portable. But, oh, well... Is calling +# `ac_compile' like this correct, after all? +if eval "$ac_compile -S -fstack-protector -o conftest.s" 2> /dev/null; then] + AC_MSG_RESULT([yes]) + [# Should we clear up other files as well, having called `AC_LANG_CONFTEST'? + rm -f conftest.s +else + ssp_possible=no] + AC_MSG_RESULT([no]) +[fi +# Need that, because some distributions ship compilers that include +# `-fstack-protector' in the default specs.] +AM_CONDITIONAL([disable_smashing_stack_protector], + [[[ x"$ssp_possible" = xyes ]]]) + +# +# Output. +# + +AC_CONFIG_HEADERS([config.h]) +AC_CONFIG_FILES([Makefile version.c]) + +# +# The remaining ugly, dark corners... +# +# Attention, parents: don't show this to your children... +# + +# +# config.status.dep.patch +# +# This is a (ugly --- I admit) bootstrap hack to get to-be-generated files +# created before any other source files are compiled. +# +# See . +# +# We don't use `BUILT_SOURCES' (as it was suggested in the follow-up message), +# as we also want things like `make SPECIFIC_TARGET' to work. +# +# This affair is especially ugly because internals are used (the `# dummy' +# tag): internals that may be subject to changes. That's the reason why a +# real patch is being used here and not some `sed' magic: to make it fail +# loudly in case. +# +# For all shipped source files a dependency file is tried to be created where +# it is simply stated that the respective source file depends on _all_ +# to-be-generated files. Depending on all of them doesn't do any harm, as they +# will nevertheless have to be created, sooner or later. The problem is, that +# `config.status' doesn't know about the source file of the file it is +# currently creating the dependency file for. So we have it do an educated +# guess... Later, when compiling the source files, these dependency files will +# be rewritten to contain the files's actual dependencies. From then on this +# bootstrap hack will be forgotten. +# + +dnl AC_CONFIG_COMMANDS_POST([ +dnl sed -i -e \ +dnl 's%#\ dummy%Makefile: $(filter-out $(DIST_SOURCES),$(SOURCES))%' \ +dnl config.status +dnl ]) +AC_CONFIG_COMMANDS_POST([ + if "$PATCH" -f < "$srcdir"/config.status.dep.patch > /dev/null 2>&1 || + ( cd "$srcdir" && "$PATCH" -f < Makefile.in.dep.patch || + grep "Ugly bootstrap hack to get to-be-generated files created" Makefile.in ) > /dev/null 2>&1 + then] AC_MSG_NOTICE([Applied a patch to work around a deficiency in] + [Automake. See `configure.ac' for details.]) + [else] AC_MSG_ERROR([failed to patch using `config.status.dep.patch'.] + [You have a serious problem. Please contact <$PACKAGE_BUGREPORT>.]) + [fi +]) + +# +# Fire. +# + +AC_OUTPUT diff --git a/ddb/db_access.c b/ddb/db_access.c new file mode 100644 index 0000000..509c1ba --- /dev/null +++ b/ddb/db_access.c @@ -0,0 +1,136 @@ +/* + * 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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 7/90 + */ + +#if MACH_KDB + +#include +#include /* type definitions */ +#include /* function definitions */ +#include +#include +#include + + + +/* + * Access unaligned data items on aligned (longword) + * boundaries. + */ + +int db_access_level = DB_ACCESS_LEVEL; + +/* + * This table is for sign-extending things. + * Therefore its entries are signed, and yes + * they are infact negative numbers. + * So don't you put no more Us in it. Or Ls either. + * Otherwise there is no point having it, n'est pas ? + */ +static int db_extend[sizeof(int)+1] = { /* table for sign-extending */ + 0, + 0xFFFFFF80, + 0xFFFF8000, + 0xFF800000, + 0x80000000 +}; + +db_expr_t +db_get_task_value( + db_addr_t addr, + int size, + boolean_t is_signed, + task_t task) +{ + char data[sizeof(db_expr_t)]; + db_expr_t value; + int i; + + if (!db_read_bytes(addr, size, data, task)) + return 0; + + 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 <= sizeof(int)) { + if (is_signed && (value & db_extend[size]) != 0) + value |= db_extend[size]; + } + return (value); +} + +void +db_put_task_value( + db_addr_t addr, + int size, + db_expr_t value, + task_t task) +{ + char data[sizeof(db_expr_t)]; + 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, task); +} + +db_expr_t +db_get_value( + db_addr_t addr, + int size, + boolean_t is_signed) +{ + return(db_get_task_value(addr, size, is_signed, TASK_NULL)); +} + +void +db_put_value( + db_addr_t addr, + int size, + db_expr_t value) +{ + db_put_task_value(addr, size, value, TASK_NULL); +} + +#endif /* MACH_KDB */ diff --git a/ddb/db_access.h b/ddb/db_access.h new file mode 100644 index 0000000..3bda5a4 --- /dev/null +++ b/ddb/db_access.h @@ -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 "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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 7/90 + */ +/* + * Data access functions for debugger. + */ + +#ifndef _DDB_DB_ACCESS_H_ +#define _DDB_DB_ACCESS_H_ + +#include +#include +#include +#include + +/* implementation dependent access capability */ +#define DB_ACCESS_KERNEL 0 /* only kernel space */ +#define DB_ACCESS_CURRENT 1 /* kernel or current task space */ +#define DB_ACCESS_ANY 2 /* any space */ + +#ifndef DB_ACCESS_LEVEL +#define DB_ACCESS_LEVEL DB_ACCESS_KERNEL +#endif /* DB_ACCESS_LEVEL */ + +#ifndef DB_VALID_KERN_ADDR +#define DB_VALID_KERN_ADDR(addr) ((addr) >= VM_MIN_KERNEL_ADDRESS \ + && (addr) < VM_MAX_KERNEL_ADDRESS) +#define DB_VALID_ADDRESS(addr,user) ((user != 0) ^ DB_VALID_KERN_ADDR(addr)) +#define DB_PHYS_EQ(task1,addr1,task2,addr2) 0 +#define DB_CHECK_ACCESS(addr,size,task) db_is_current_task(task) +#endif /* DB_VALID_KERN_ADDR */ + +extern int db_access_level; + +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 ); + +extern db_expr_t db_get_task_value( db_addr_t addr, + int size, + boolean_t is_signed, + task_t task ); + +extern void db_put_task_value( db_addr_t addr, + int size, + db_expr_t value, + task_t task ); + +#endif /* _DDB_DB_ACCESS_H_ */ diff --git a/ddb/db_break.c b/ddb/db_break.c new file mode 100644 index 0000000..374dc6a --- /dev/null +++ b/ddb/db_break.c @@ -0,0 +1,746 @@ +/* + * 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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 7/90 + */ + +#if MACH_KDB + + +/* + * Breakpoints. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NBREAKPOINTS 100 +#define NTHREAD_LIST (NBREAKPOINTS*3) + +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; + +static struct db_thread_breakpoint db_thread_break_list[NTHREAD_LIST]; +static db_thread_breakpoint_t db_free_thread_break_list = 0; +static boolean_t db_thread_break_init = FALSE; +static int db_breakpoint_number = 0; + +static db_breakpoint_t +db_breakpoint_alloc(void) +{ + 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); +} + +static void +db_breakpoint_free(db_breakpoint_t bkpt) +{ + bkpt->link = db_free_breakpoints; + db_free_breakpoints = bkpt; +} + +static int +db_add_thread_breakpoint( + const db_breakpoint_t bkpt, + vm_offset_t task_thd, + int count, + boolean_t task_bpt) +{ + db_thread_breakpoint_t tp; + + if (db_thread_break_init == FALSE) { + for (tp = db_thread_break_list; + tp < &db_thread_break_list[NTHREAD_LIST-1]; tp++) + tp->tb_next = tp+1; + tp->tb_next = 0; + db_free_thread_break_list = db_thread_break_list; + db_thread_break_init = TRUE; + } + if (db_free_thread_break_list == 0) + return (-1); + tp = db_free_thread_break_list; + db_free_thread_break_list = tp->tb_next; + tp->tb_is_task = task_bpt; + tp->tb_task_thd = task_thd; + tp->tb_count = count; + tp->tb_init_count = count; + tp->tb_cond = 0; + tp->tb_number = ++db_breakpoint_number; + tp->tb_next = bkpt->threads; + bkpt->threads = tp; + return(0); +} + +static int +db_delete_thread_breakpoint( + db_breakpoint_t bkpt, + vm_offset_t task_thd) +{ + db_thread_breakpoint_t tp; + db_thread_breakpoint_t *tpp; + + if (task_thd == 0) { + /* delete all the thread-breakpoints */ + + for (tpp = &bkpt->threads; (tp = *tpp) != 0; tpp = &tp->tb_next) + db_cond_free(tp); + + *tpp = db_free_thread_break_list; + db_free_thread_break_list = bkpt->threads; + bkpt->threads = 0; + return 0; + } else { + /* delete the specified thread-breakpoint */ + + for (tpp = &bkpt->threads; (tp = *tpp) != 0; tpp = &tp->tb_next) + if (tp->tb_task_thd == task_thd) { + db_cond_free(tp); + *tpp = tp->tb_next; + tp->tb_next = db_free_thread_break_list; + db_free_thread_break_list = tp; + return 0; + } + + return -1; /* not found */ + } +} + +static db_thread_breakpoint_t __attribute__ ((pure)) +db_find_thread_breakpoint( + const db_breakpoint_t bkpt, + const thread_t thread) +{ + db_thread_breakpoint_t tp; + task_t task = (thread == THREAD_NULL)? TASK_NULL: thread->task; + + for (tp = bkpt->threads; tp; tp = tp->tb_next) { + if (tp->tb_is_task) { + if (tp->tb_task_thd == (vm_offset_t)task) + break; + continue; + } + if (tp->tb_task_thd == (vm_offset_t)thread || tp->tb_task_thd == 0) + break; + } + return(tp); +} + +db_thread_breakpoint_t +db_find_thread_breakpoint_here( + const task_t task, + db_addr_t addr) +{ + db_breakpoint_t bkpt; + + bkpt = db_find_breakpoint(task, addr); + if (bkpt == 0) + return(0); + return(db_find_thread_breakpoint(bkpt, current_thread())); +} + +db_thread_breakpoint_t +db_find_breakpoint_number( + int num, + db_breakpoint_t *bkptp) +{ + db_thread_breakpoint_t tp; + db_breakpoint_t bkpt; + + for (bkpt = db_breakpoint_list; bkpt != 0; bkpt = bkpt->link) { + for (tp = bkpt->threads; tp; tp = tp->tb_next) { + if (tp->tb_number == num) { + if (bkptp) + *bkptp = bkpt; + return(tp); + } + } + } + return(0); +} + +static void +db_force_delete_breakpoint( + db_breakpoint_t bkpt, + vm_offset_t task_thd, + boolean_t is_task) +{ + db_printf("deleted a stale breakpoint at "); + if (bkpt->task == TASK_NULL || db_lookup_task(bkpt->task) >= 0) + db_task_printsym(bkpt->address, DB_STGY_PROC, bkpt->task); + else + db_printf("%#X", bkpt->address); + if (bkpt->task) + db_printf(" in task %X", bkpt->task); + if (task_thd) + db_printf(" for %s %X", (is_task)? "task": "thread", task_thd); + db_printf("\n"); + db_delete_thread_breakpoint(bkpt, task_thd); +} + +void +db_check_breakpoint_valid(void) +{ + db_thread_breakpoint_t tbp, tbp_next; + db_breakpoint_t bkpt, *bkptp; + + bkptp = &db_breakpoint_list; + for (bkpt = *bkptp; bkpt; bkpt = *bkptp) { + if (bkpt->task != TASK_NULL) { + if (db_lookup_task(bkpt->task) < 0) { + db_force_delete_breakpoint(bkpt, 0, FALSE); + *bkptp = bkpt->link; + db_breakpoint_free(bkpt); + continue; + } + } else { + for (tbp = bkpt->threads; tbp; tbp = tbp_next) { + tbp_next = tbp->tb_next; + if (tbp->tb_task_thd == 0) + continue; + if ((tbp->tb_is_task && + db_lookup_task((task_t)(tbp->tb_task_thd)) < 0) || + (!tbp->tb_is_task && + db_lookup_thread((thread_t)(tbp->tb_task_thd)) < 0)) { + db_force_delete_breakpoint(bkpt, + tbp->tb_task_thd, tbp->tb_is_task); + } + } + if (bkpt->threads == 0) { + db_put_task_value(bkpt->address, BKPT_SIZE, + bkpt->bkpt_inst, bkpt->task); + *bkptp = bkpt->link; + db_breakpoint_free(bkpt); + continue; + } + } + bkptp = &bkpt->link; + } +} + +db_breakpoint_t +db_set_breakpoint( + const task_t task, + db_addr_t addr, + int count, + const thread_t thread, + boolean_t task_bpt) +{ + db_breakpoint_t bkpt; + db_breakpoint_t alloc_bkpt = 0; + vm_offset_t task_thd; + + bkpt = db_find_breakpoint(task, addr); + if (bkpt) { + if (thread == THREAD_NULL + || db_find_thread_breakpoint(bkpt, thread)) { + db_printf("Already set.\n"); + return NULL; + } + } else { + if (!DB_CHECK_ACCESS(addr, BKPT_SIZE, task)) { + db_printf("Cannot set break point at %X\n", addr); + return NULL; + } + alloc_bkpt = bkpt = db_breakpoint_alloc(); + if (bkpt == 0) { + db_printf("Too many breakpoints.\n"); + return NULL; + } + bkpt->task = task; + bkpt->flags = (task && thread == THREAD_NULL)? + (BKPT_USR_GLOBAL|BKPT_1ST_SET): 0; + bkpt->address = addr; + bkpt->threads = 0; + } + if (db_breakpoint_list == 0) + db_breakpoint_number = 0; + task_thd = (task_bpt)? (vm_offset_t)(thread->task): (vm_offset_t)thread; + if (db_add_thread_breakpoint(bkpt, task_thd, count, task_bpt) < 0) { + if (alloc_bkpt) + db_breakpoint_free(alloc_bkpt); + db_printf("Too many thread_breakpoints.\n"); + return NULL; + } else { + db_printf("set breakpoint #%d\n", db_breakpoint_number); + if (alloc_bkpt) { + bkpt->link = db_breakpoint_list; + db_breakpoint_list = bkpt; + } + return bkpt; + } +} + +static void +db_delete_breakpoint( + const task_t task, + db_addr_t addr, + vm_offset_t task_thd) +{ + db_breakpoint_t bkpt; + db_breakpoint_t *prev; + + for (prev = &db_breakpoint_list; (bkpt = *prev) != 0; + prev = &bkpt->link) { + if ((bkpt->task == task + || (task != TASK_NULL && (bkpt->flags & BKPT_USR_GLOBAL))) + && bkpt->address == addr) + break; + } + if (bkpt && (bkpt->flags & BKPT_SET_IN_MEM)) { + db_printf("cannot delete it now.\n"); + return; + } + if (bkpt == 0 + || db_delete_thread_breakpoint(bkpt, task_thd) < 0) { + db_printf("Not set.\n"); + return; + } + if (bkpt->threads == 0) { + *prev = bkpt->link; + db_breakpoint_free(bkpt); + } +} + +db_breakpoint_t __attribute__ ((pure)) +db_find_breakpoint( + const task_t task, + db_addr_t addr) +{ + db_breakpoint_t bkpt; + + for (bkpt = db_breakpoint_list; bkpt != 0; bkpt = bkpt->link) { + if ((bkpt->task == task + || (task != TASK_NULL && (bkpt->flags & BKPT_USR_GLOBAL))) + && bkpt->address == addr) + return (bkpt); + } + return (0); +} + +boolean_t +db_find_breakpoint_here( + const task_t task, + db_addr_t addr) +{ + db_breakpoint_t bkpt; + + for (bkpt = db_breakpoint_list; bkpt != 0; bkpt = bkpt->link) { + if ((bkpt->task == task + || (task != TASK_NULL && (bkpt->flags & BKPT_USR_GLOBAL))) + && bkpt->address == addr) + return(TRUE); + if ((bkpt->flags & BKPT_USR_GLOBAL) == 0 && + DB_PHYS_EQ(task, addr, bkpt->task, bkpt->address)) + return (TRUE); + } + return(FALSE); +} + +boolean_t db_breakpoints_inserted = TRUE; + +void +db_set_breakpoints(void) +{ + db_breakpoint_t bkpt; + task_t task; + db_expr_t inst; + task_t cur_task; + + cur_task = (current_thread())? current_thread()->task: TASK_NULL; + if (!db_breakpoints_inserted) { + for (bkpt = db_breakpoint_list; bkpt != 0; bkpt = bkpt->link) { + if (bkpt->flags & BKPT_SET_IN_MEM) + continue; + task = bkpt->task; + if (bkpt->flags & BKPT_USR_GLOBAL) { + if ((bkpt->flags & BKPT_1ST_SET) == 0) { + if (cur_task == TASK_NULL) + continue; + task = cur_task; + } else + bkpt->flags &= ~BKPT_1ST_SET; + } + if (DB_CHECK_ACCESS(bkpt->address, BKPT_SIZE, task)) { + inst = db_get_task_value(bkpt->address, BKPT_SIZE, FALSE, + task); + if (inst == BKPT_SET(inst)) + continue; + bkpt->bkpt_inst = inst; + db_put_task_value(bkpt->address, + BKPT_SIZE, + BKPT_SET(bkpt->bkpt_inst), task); + bkpt->flags |= BKPT_SET_IN_MEM; + } else { + db_printf("Warning: cannot set breakpoint at %X ", + bkpt->address); + if (task) + db_printf("in task %X\n", task); + else + db_printf("in kernel space\n"); + } + } + db_breakpoints_inserted = TRUE; + } +} + +void +db_clear_breakpoints(void) +{ + db_breakpoint_t bkpt, *bkptp; + task_t task; + task_t cur_task; + db_expr_t inst; + + cur_task = (current_thread())? current_thread()->task: TASK_NULL; + if (db_breakpoints_inserted) { + bkptp = &db_breakpoint_list; + for (bkpt = *bkptp; bkpt; bkpt = *bkptp) { + task = bkpt->task; + if (bkpt->flags & BKPT_USR_GLOBAL) { + if (cur_task == TASK_NULL) { + bkptp = &bkpt->link; + continue; + } + task = cur_task; + } + if ((bkpt->flags & BKPT_SET_IN_MEM) + && DB_CHECK_ACCESS(bkpt->address, BKPT_SIZE, task)) { + inst = db_get_task_value(bkpt->address, BKPT_SIZE, FALSE, + task); + if (inst != BKPT_SET(inst)) { + if (bkpt->flags & BKPT_USR_GLOBAL) { + bkptp = &bkpt->link; + continue; + } + db_force_delete_breakpoint(bkpt, 0, FALSE); + *bkptp = bkpt->link; + db_breakpoint_free(bkpt); + continue; + } + db_put_task_value(bkpt->address, BKPT_SIZE, + bkpt->bkpt_inst, task); + bkpt->flags &= ~BKPT_SET_IN_MEM; + } + bkptp = &bkpt->link; + } + 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( + task_t task, + db_addr_t addr) +{ + db_breakpoint_t bkpt; + + bkpt = db_breakpoint_alloc(); + if (bkpt == 0) { + db_printf("Too many breakpoints.\n"); + return 0; + } + bkpt->task = task; + bkpt->address = addr; + bkpt->flags = BKPT_TEMP; + bkpt->threads = 0; + if (db_add_thread_breakpoint(bkpt, 0, 1, FALSE) < 0) { + if (bkpt) + db_breakpoint_free(bkpt); + db_printf("Too many thread_breakpoints.\n"); + return 0; + } + bkpt->bkpt_inst = db_get_task_value(bkpt->address, BKPT_SIZE, + FALSE, task); + db_put_task_value(bkpt->address, BKPT_SIZE, + BKPT_SET(bkpt->bkpt_inst), task); + return bkpt; +} + +void +db_delete_temp_breakpoint( + task_t task, + db_breakpoint_t bkpt) +{ + db_put_task_value(bkpt->address, BKPT_SIZE, bkpt->bkpt_inst, task); + db_delete_thread_breakpoint(bkpt, 0); + db_breakpoint_free(bkpt); +} + +/* + * List breakpoints. + */ +static void +db_list_breakpoints(void) +{ + db_breakpoint_t bkpt; + + if (db_breakpoint_list == 0) { + db_printf("No breakpoints set\n"); + return; + } + + db_printf(" No Space Thread Cnt Address(Cond)\n"); + for (bkpt = db_breakpoint_list; + bkpt != 0; + bkpt = bkpt->link) + { + db_thread_breakpoint_t tp; + int task_id; + int thread_id; + + if (bkpt->threads) { + for (tp = bkpt->threads; tp; tp = tp->tb_next) { + db_printf("%3d ", tp->tb_number); + if (bkpt->flags & BKPT_USR_GLOBAL) + db_printf("user "); + else if (bkpt->task == TASK_NULL) + db_printf("kernel "); + else if ((task_id = db_lookup_task(bkpt->task)) < 0) + db_printf("%0*X ", 2*sizeof(vm_offset_t), bkpt->task); + else + db_printf("task%-3d ", task_id); + if (tp->tb_task_thd == 0) { + db_printf("all "); + } else { + if (tp->tb_is_task) { + task_id = db_lookup_task((task_t)(tp->tb_task_thd)); + if (task_id < 0) + db_printf("%0*X ", 2*sizeof(vm_offset_t), + tp->tb_task_thd); + else + db_printf("task%03d ", task_id); + } else { + thread_t thd = (thread_t)(tp->tb_task_thd); + task_id = db_lookup_task(thd->task); + thread_id = db_lookup_task_thread(thd->task, thd); + if (task_id < 0 || thread_id < 0) + db_printf("%0*X ", 2*sizeof(vm_offset_t), + tp->tb_task_thd); + else + db_printf("task%03d.%-3d ", task_id, thread_id); + } + } + db_printf("%3d ", tp->tb_init_count); + db_task_printsym(bkpt->address, DB_STGY_PROC, bkpt->task); + if (tp->tb_cond > 0) { + db_printf("("); + db_cond_print(tp); + db_printf(")"); + } + db_printf("\n"); + } + } else { + if (bkpt->task == TASK_NULL) + db_printf(" ? kernel "); + else + db_printf("%*X ", 2*sizeof(vm_offset_t), bkpt->task); + db_printf("(?) "); + db_task_printsym(bkpt->address, DB_STGY_PROC, bkpt->task); + db_printf("\n"); + } + } +} + +/* Delete breakpoint */ +/*ARGSUSED*/ +void +db_delete_cmd( + db_expr_t addr_, + int have_addr, + db_expr_t count, + const char * modif) +{ + int n; + thread_t thread; + vm_offset_t task_thd; + boolean_t user_global = FALSE; + boolean_t task_bpt = FALSE; + boolean_t user_space = FALSE; + boolean_t thd_bpt = FALSE; + db_expr_t addr; + int t; + + t = db_read_token(); + if (t == tSLASH) { + t = db_read_token(); + if (t != tIDENT) { + db_printf("Bad modifier \"%s\"\n", db_tok_string); + db_error(0); + } + user_global = db_option(db_tok_string, 'U'); + user_space = (user_global)? TRUE: db_option(db_tok_string, 'u'); + task_bpt = db_option(db_tok_string, 'T'); + thd_bpt = db_option(db_tok_string, 't'); + if (task_bpt && user_global) + db_error("Cannot specify both 'T' and 'U' option\n"); + t = db_read_token(); + } + if (t == tHASH) { + db_thread_breakpoint_t tbp; + db_breakpoint_t bkpt; + + if (db_read_token() != tNUMBER) { + db_printf("Bad break point number #%s\n", db_tok_string); + db_error(0); + } + if ((tbp = db_find_breakpoint_number(db_tok_number, &bkpt)) == 0) { + db_printf("No such break point #%d\n", db_tok_number); + db_error(0); + } + db_delete_breakpoint(bkpt->task, bkpt->address, tbp->tb_task_thd); + return; + } + db_unread_token(t); + if (!db_expression(&addr)) { + /* + * We attempt to pick up the user_space indication from db_dot, + * so that a plain "d" always works. + */ + addr = (db_expr_t)db_dot; + if (!user_space && !DB_VALID_ADDRESS((vm_offset_t)addr, FALSE)) + user_space = TRUE; + } + if (!DB_VALID_ADDRESS((vm_offset_t) addr, user_space)) { + db_printf("Address %#X is not in %s space\n", addr, + (user_space)? "user": "kernel"); + db_error(0); + } + if (thd_bpt || task_bpt) { + for (n = 0; db_get_next_thread(&thread, n); n++) { + if (thread == THREAD_NULL) + db_error("No active thread\n"); + if (task_bpt) { + if (thread->task == TASK_NULL) + db_error("No task\n"); + task_thd = (vm_offset_t) (thread->task); + } else + task_thd = (user_global)? 0: (vm_offset_t) thread; + db_delete_breakpoint(db_target_space(thread, user_space), + (db_addr_t)addr, task_thd); + } + } else { + db_delete_breakpoint(db_target_space(THREAD_NULL, user_space), + (db_addr_t)addr, 0); + } +} + +/* Set breakpoint with skip count */ +/*ARGSUSED*/ +void +db_breakpoint_cmd( + db_expr_t addr, + int have_addr, + db_expr_t count, + const char * modif) +{ + int n; + thread_t thread; + boolean_t user_global = db_option(modif, 'U'); + boolean_t task_bpt = db_option(modif, 'T'); + boolean_t user_space; + + if (count == -1) + count = 1; + + if (!task_bpt && db_option(modif,'t')) + task_bpt = TRUE; + + if (task_bpt && user_global) + db_error("Cannot specify both 'T' and 'U'\n"); + user_space = (user_global)? TRUE: db_option(modif, 'u'); + if (user_space && db_access_level < DB_ACCESS_CURRENT) + db_error("User space break point is not supported\n"); + if (!task_bpt && !DB_VALID_ADDRESS((vm_offset_t)addr, user_space)) { + /* if the user has explicitly specified user space, + do not insert a breakpoint into the kernel */ + if (user_space) + db_error("Invalid user space address\n"); + user_space = TRUE; + db_printf("%#X is in user space\n", addr); + } + if (db_option(modif, 't') || task_bpt) { + for (n = 0; db_get_next_thread(&thread, n); n++) { + if (thread == THREAD_NULL) + db_error("No active thread\n"); + if (task_bpt && thread->task == TASK_NULL) + db_error("No task\n"); + if (db_access_level <= DB_ACCESS_CURRENT && user_space + && thread->task != db_current_task()) + db_error("Cannot set break point in inactive user space\n"); + db_set_breakpoint(db_target_space(thread, user_space), + (db_addr_t)addr, count, + (user_global)? THREAD_NULL: thread, + task_bpt); + } + } else { + db_set_breakpoint(db_target_space(THREAD_NULL, user_space), + (db_addr_t)addr, + count, THREAD_NULL, FALSE); + } +} + +/* list breakpoints */ +void +db_listbreak_cmd( + db_expr_t addr, + int have_addr, + db_expr_t count, + const char * modif) +{ + db_list_breakpoints(); +} + +#endif /* MACH_KDB */ diff --git a/ddb/db_break.h b/ddb/db_break.h new file mode 100644 index 0000000..9f0ee95 --- /dev/null +++ b/ddb/db_break.h @@ -0,0 +1,111 @@ +/* + * 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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 7/90 + */ +#ifndef _DDB_DB_BREAK_H_ +#define _DDB_DB_BREAK_H_ + +#include +#include +#include +#include + +/* + * thread list at the same breakpoint address + */ +struct db_thread_breakpoint { + vm_offset_t tb_task_thd; /* target task or thread */ + boolean_t tb_is_task; /* task qualified */ + short tb_number; /* breakpoint number */ + short tb_init_count; /* skip count(initial value) */ + short tb_count; /* current skip count */ + short tb_cond; /* break condition */ + struct db_thread_breakpoint *tb_next; /* next chain */ +}; + +typedef struct db_thread_breakpoint *db_thread_breakpoint_t; + +/* + * Breakpoint. + */ + +struct db_breakpoint { + task_t task; /* target task */ + db_addr_t address; /* set here */ + db_thread_breakpoint_t threads; /* thread */ + int flags; /* flags: */ +#define BKPT_SINGLE_STEP 0x2 /* to simulate single step */ +#define BKPT_TEMP 0x4 /* temporary */ +#define BKPT_USR_GLOBAL 0x8 /* global user space break point */ +#define BKPT_SET_IN_MEM 0x10 /* break point is set in memory */ +#define BKPT_1ST_SET 0x20 /* 1st time set of user global bkpt */ +#define BKPT_EXTERNAL 0x40 /* break point is not from ddb */ + vm_size_t 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( const task_t task, db_addr_t addr) __attribute__ ((pure)); +extern boolean_t db_find_breakpoint_here( const task_t task, db_addr_t addr); +extern void db_set_breakpoints(void); +extern void db_clear_breakpoints(void); +extern db_thread_breakpoint_t db_find_thread_breakpoint_here + ( const task_t task, db_addr_t addr ); +extern db_thread_breakpoint_t db_find_breakpoint_number + ( int num, db_breakpoint_t *bkptp); + +extern db_breakpoint_t db_set_temp_breakpoint( task_t task, db_addr_t addr); +extern void db_delete_temp_breakpoint + ( task_t task, db_breakpoint_t bkpt); + +extern db_breakpoint_t db_set_breakpoint(const task_t task, db_addr_t addr, + int count, const thread_t thread, + boolean_t task_bpt); + +void db_listbreak_cmd( + db_expr_t addr, + boolean_t have_addr, + db_expr_t count, + const char *modif); + +void db_delete_cmd( + db_expr_t addr, + boolean_t have_addr, + db_expr_t count, + const char * modif); + +void db_breakpoint_cmd( + db_expr_t addr, + int have_addr, + db_expr_t count, + const char * modif); + +extern void db_check_breakpoint_valid(void); + +#endif /* _DDB_DB_BREAK_H_ */ diff --git a/ddb/db_command.c b/ddb/db_command.c new file mode 100644 index 0000000..4671fe8 --- /dev/null +++ b/ddb/db_command.c @@ -0,0 +1,594 @@ +/* + * Mach Operating System + * Copyright (c) 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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 7/90 + */ + +#if MACH_KDB + +/* + * Command dispatcher. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include /* 4proto */ +#include /* 4proto */ + +#include +#include +#include +#include +#include + +/* + * Exported global variables + */ +boolean_t db_cmd_loop_done; +jmp_buf_t *db_recover = 0; +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; + +/* + * 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. + */ +static int +db_cmd_search( + const char * name, + const struct db_command *table, + const struct db_command **cmdp /* out */) +{ + const struct db_command *cmd; + int result = CMD_NONE; + + for (cmd = table; cmd->name != 0; cmd++) { + const char *lp; + char *rp; + 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 (!strncmp(name, "help", strlen(name))) + result = CMD_HELP; + } + return (result); +} + +static void +db_cmd_list(const struct db_command *table) +{ + const struct db_command *cmd; + + for (cmd = table; cmd->name != 0; cmd++) { + db_printf("%-12s", cmd->name); + db_end_line(); + } +} + +static void +db_command( + const struct db_command **last_cmdp, /* IN_OUT */ + struct db_command *cmd_table) +{ + const struct db_command *cmd = NULL; + 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 || t == tSEMI_COLON) { + /* empty line repeats last command, at 'next' */ + cmd = *last_cmdp; + addr = (db_expr_t)db_next; + have_addr = FALSE; + count = 1; + modif[0] = '\0'; + if (t == tSEMI_COLON) + db_unread_token(t); + } + else if (t == tEXCL) { + 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: + if (db_exec_macro(db_tok_string) == 0) + return; + db_printf("No such command \"%s\"\n", db_tok_string); + 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 \"/%s\"\n", db_tok_string); + 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 after ','\n"); + db_flush_lex(); + return; + } + } + else { + db_unread_token(t); + count = -1; + } + } + } + *last_cmdp = cmd; + if (cmd != NULL) { + /* + * 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; + } + } +} + +static void +db_command_list( + const struct db_command **last_cmdp, /* IN_OUT */ + struct db_command *cmd_table) +{ + do { + db_command(last_cmdp, cmd_table); + db_skip_to_eol(); + } while (db_read_token() == tSEMI_COLON && db_cmd_loop_done == FALSE); +} + +struct db_command db_show_all_cmds[] = { + { "tasks", db_show_all_tasks, 0, 0 }, + { "threads", db_show_all_threads, 0, 0 }, + { "slocks", (db_command_fun_t)db_show_all_slocks, 0, 0 }, + { "runqs", (db_command_fun_t)db_show_all_runqs, 0, 0 }, + { (char *)0 } +}; + +struct db_command db_show_cmds[] = { + { "all", 0, 0, db_show_all_cmds }, + { "registers", (db_command_fun_t)db_show_regs, 0, 0 }, + { "breaks", db_listbreak_cmd, 0, 0 }, + { "watches", db_listwatch_cmd, 0, 0 }, + { "thread", db_show_one_thread, 0, 0 }, + { "task", db_show_one_task, 0, 0 }, + { "macro", db_show_macro, CS_OWN, 0 }, + { "map", vm_map_print, 0, 0 }, + { "object", (db_command_fun_t)vm_object_print, 0, 0 }, + { "page", (db_command_fun_t)vm_page_print, 0, 0 }, + { "copy", (db_command_fun_t)vm_map_copy_print, 0, 0 }, + { "port", (db_command_fun_t)ipc_port_print, 0, 0 }, + { "pset", (db_command_fun_t)ipc_pset_print, 0, 0 }, + { "kmsg", (db_command_fun_t)ipc_kmsg_print, 0, 0 }, + { "msg", (db_command_fun_t)ipc_msg_print, 0, 0 }, + { "ipc_port", db_show_port_id, 0, 0 }, + { "slabinfo", (db_command_fun_t)db_show_slab_info, 0, 0 }, + { "vmstat", (db_command_fun_t)db_show_vmstat, 0, 0 }, + { (char *)0, } +}; + +void +db_debug_all_traps_cmd(db_expr_t addr, + int have_addr, + db_expr_t count, + const char *modif); +void +db_debug_port_references_cmd(db_expr_t addr, + int have_addr, + db_expr_t count, + const char *modif); + +struct db_command db_debug_cmds[] = { + { "traps", db_debug_all_traps_cmd, 0, 0 }, + { "references", db_debug_port_references_cmd, 0, 0 }, + { (char *)0, } +}; + +struct db_command db_command_table[] = { +#ifdef DB_MACHINE_COMMANDS + /* this must be the first entry, if it exists */ + { "machine", 0, 0, 0}, +#endif + { "print", (db_command_fun_t)db_print_cmd, CS_OWN, 0 }, + { "examine", db_examine_cmd, CS_MORE|CS_SET_DOT, 0 }, + { "x", db_examine_cmd, CS_MORE|CS_SET_DOT, 0 }, + { "xf", db_examine_forward, CS_SET_DOT, 0 }, + { "xb", db_examine_backward, CS_SET_DOT, 0 }, + { "whatis", db_whatis_cmd, CS_MORE, 0 }, + { "search", db_search_cmd, CS_OWN|CS_SET_DOT, 0 }, + { "set", (db_command_fun_t)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, CS_OWN, 0 }, + { "d", db_delete_cmd, CS_OWN, 0 }, + { "break", db_breakpoint_cmd, CS_MORE, 0 }, + { "dwatch", db_deletewatch_cmd, CS_MORE, 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 }, + { "cond", db_cond_cmd, CS_OWN, 0 }, + { "call", (db_command_fun_t)db_fncall, CS_OWN, 0 }, + { "macro", db_def_macro_cmd, CS_OWN, 0 }, + { "dmacro", db_del_macro_cmd, CS_OWN, 0 }, + { "show", 0, 0, db_show_cmds }, + { "debug", 0, 0, db_debug_cmds }, + { "reset", (db_command_fun_t)db_reset_cpu, 0, 0 }, + { "reboot", (db_command_fun_t)db_reset_cpu, 0, 0 }, + { "halt", (db_command_fun_t)db_halt_cpu, 0, 0 }, + { (char *)0, } +}; + +#ifdef DB_MACHINE_COMMANDS + +/* this function should be called to install the machine dependent + commands. It should be called before the debugger is enabled */ +void db_machine_commands_install(struct db_command *ptr) +{ + db_command_table[0].more = ptr; + return; +} + +#endif /* DB_MACHINE_COMMANDS */ + + +const struct db_command *db_last_command = 0; + +void +db_help_cmd(void) +{ + struct db_command *cmd = db_command_table; + + while (cmd->name != 0) { + db_printf("%-12s", cmd->name); + db_end_line(); + cmd++; + } +} + +void +db_command_loop(void) +{ + jmp_buf_t db_jmpbuf; + jmp_buf_t *prev = db_recover; + extern int db_output_line; + extern int db_macro_level; + + /* + * Initialize 'prev' and 'next' to dot. + */ + db_prev = db_dot; + db_next = db_dot; + + db_cmd_loop_done = FALSE; + while (!db_cmd_loop_done) { + (void) _setjmp(db_recover = &db_jmpbuf); + db_macro_level = 0; + if (db_print_position() != 0) + db_printf("\n"); + db_output_line = 0; + db_printf("db%s", (db_default_thread)? "t": ""); +#if NCPUS > 1 + db_printf("{%d}", cpu_number()); +#endif + db_printf("> "); + + (void) db_read_line("!!"); + db_command_list(&db_last_command, db_command_table); + } + + db_recover = prev; +} + +boolean_t +db_exec_cmd_nest( + char *cmd, + int size) +{ + struct db_lex_context lex_context; + + db_cmd_loop_done = FALSE; + if (cmd) { + db_save_lex_context(&lex_context); + db_switch_input(cmd, size /**OLD, &lex_context OLD**/); + } + db_command_list(&db_last_command, db_command_table); + if (cmd) + db_restore_lex_context(&lex_context); + return(db_cmd_loop_done == FALSE); +} + +void db_error(const char *s) +{ + extern int db_macro_level; + + db_macro_level = 0; + if (db_recover) { + if (s) + db_printf(s); + db_flush_lex(); + _longjmp(db_recover, 1); + } + else + { + if (s) + db_printf(s); + panic("db_error"); + } +} + +/* + * Call random function: + * !expr(arg,arg,arg) + */ +void +db_fncall(void) +{ + db_expr_t fn_addr; +#define MAXARGS 11 + db_expr_t args[MAXARGS]; + int nargs = 0; + db_expr_t retval; + typedef db_expr_t(*function_t)(db_expr_t, db_expr_t, db_expr_t, + db_expr_t, db_expr_t, db_expr_t, + db_expr_t, db_expr_t, db_expr_t, + db_expr_t); + function_t func; + int t; + + if (!db_expression(&fn_addr)) { + db_printf("Bad function \"%s\"\n", db_tok_string); + db_flush_lex(); + return; + } + func = (function_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; + } + } + 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); +} + +boolean_t __attribute__ ((pure)) +db_option( + const char *modif, + int option) +{ + const char *p; + + for (p = modif; *p; p++) + if (*p == option) + return(TRUE); + return(FALSE); +} + +void +db_debug_all_traps_cmd(db_expr_t addr, + int have_addr, + db_expr_t count, + const char *modif) +{ + if (strcmp (modif, "on") == 0) + db_debug_all_traps (TRUE); + else if (strcmp (modif, "off") == 0) + db_debug_all_traps (FALSE); + else + db_error ("debug traps /on|/off\n"); +} + +void +db_debug_port_references_cmd(db_expr_t addr, + int have_addr, + db_expr_t count, + const char *modif) +{ + if (strcmp (modif, "on") == 0) + db_debug_port_references (TRUE); + else if (strcmp (modif, "off") == 0) + db_debug_port_references (FALSE); + else + db_error ("debug references /on|/off\n"); +} + +#endif /* MACH_KDB */ diff --git a/ddb/db_command.h b/ddb/db_command.h new file mode 100644 index 0000000..73690a4 --- /dev/null +++ b/ddb/db_command.h @@ -0,0 +1,80 @@ +/* + * Mach Operating System + * Copyright (c) 1992,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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 7/90 + */ + +#ifndef _DDB_DB_COMMAND_H_ +#define _DDB_DB_COMMAND_H_ + +#if MACH_KDB + +/* + * Command loop declarations. + */ + +#include +#include + +extern void db_command_loop(void); +extern boolean_t db_option(const char *, int) __attribute__ ((pure)); + +extern void db_error(const char *) __attribute__ ((noreturn)); /* 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 */ +extern jmp_buf_t * db_recover; /* error recovery */ + +typedef void (*db_command_fun_t)(db_expr_t, boolean_t, db_expr_t, const char *); + +/* + * Command table + */ +struct db_command { + char * name; /* command name */ + db_command_fun_t 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 db_command *more; /* another level of command */ +}; + +extern boolean_t db_exec_cmd_nest(char *cmd, int size); + +void db_fncall(void); + +void db_help_cmd(void); + +#endif /* MACH_KDB */ + +#endif /* _DDB_DB_COMMAND_H_ */ diff --git a/ddb/db_cond.c b/ddb/db_cond.c new file mode 100644 index 0000000..d45d9b8 --- /dev/null +++ b/ddb/db_cond.c @@ -0,0 +1,185 @@ +/* + * 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. + */ + +#if MACH_KDB + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + + +#define DB_MAX_COND 10 /* maximum conditions to be set */ + +int db_ncond_free = DB_MAX_COND; /* free condition */ +struct db_cond { + int c_size; /* size of cond */ + char c_cond_cmd[DB_LEX_LINE_SIZE]; /* cond & cmd */ +} db_cond[DB_MAX_COND]; + +void +db_cond_free(db_thread_breakpoint_t bkpt) +{ + if (bkpt->tb_cond > 0) { + db_cond[bkpt->tb_cond-1].c_size = 0; + db_ncond_free++; + bkpt->tb_cond = 0; + } +} + +boolean_t +db_cond_check(db_thread_breakpoint_t bkpt) +{ + struct db_cond *cp; + db_expr_t value; + int t; + jmp_buf_t db_jmpbuf; + extern jmp_buf_t *db_recover; + + if (bkpt->tb_cond <= 0) /* no condition */ + return(TRUE); + db_dot = PC_REGS(DDB_REGS); + db_prev = db_dot; + db_next = db_dot; + if (_setjmp(db_recover = &db_jmpbuf)) { + /* + * in case of error, return true to enter interactive mode + */ + return(TRUE); + } + + /* + * switch input, and evalutate condition + */ + cp = &db_cond[bkpt->tb_cond - 1]; + db_switch_input(cp->c_cond_cmd, cp->c_size); + if (!db_expression(&value)) { + db_printf("error: condition evaluation error\n"); + return(TRUE); + } + if (value == 0 || --(bkpt->tb_count) > 0) + return(FALSE); + + /* + * execute a command list if exist + */ + bkpt->tb_count = bkpt->tb_init_count; + if ((t = db_read_token()) != tEOL) { + db_unread_token(t); + return(db_exec_cmd_nest(0, 0)); + } + return(TRUE); +} + +void +db_cond_print(const db_thread_breakpoint_t bkpt) +{ + char *p, *ep; + struct db_cond *cp; + + if (bkpt->tb_cond <= 0) + return; + cp = &db_cond[bkpt->tb_cond-1]; + p = cp->c_cond_cmd; + ep = p + cp->c_size; + while (p < ep) { + if (*p == '\n' || *p == 0) + break; + db_putchar(*p++); + } +} + +void +db_cond_cmd( + db_expr_t addr, + int have_addr, + db_expr_t count, + const char * modif) +{ + int c; + struct db_cond *cp; + char *p; + db_expr_t value; + db_thread_breakpoint_t bkpt; + + if (db_read_token() != tHASH || db_read_token() != tNUMBER) { + db_printf("# expected instead of \"%s\"\n", db_tok_string); + db_error(0); + return; + } + if ((bkpt = db_find_breakpoint_number(db_tok_number, 0)) == 0) { + db_printf("No such break point #%d\n", db_tok_number); + db_error(0); + return; + } + /* + * if the break point already has a condition, free it first + */ + if (bkpt->tb_cond > 0) { + cp = &db_cond[bkpt->tb_cond - 1]; + db_cond_free(bkpt); + } else { + if (db_ncond_free <= 0) { + db_error("Too many conditions\n"); + return; + } + for (cp = db_cond; cp < &db_cond[DB_MAX_COND]; cp++) + if (cp->c_size == 0) + break; + if (cp >= &db_cond[DB_MAX_COND]) + panic("bad db_cond_free"); + } + for (c = db_read_char(); c == ' ' || c == '\t'; c = db_read_char()); + for (p = cp->c_cond_cmd; c >= 0; c = db_read_char()) + *p++ = c; + /* + * switch to saved data and call db_expression to check the condition. + * If no condition is supplied, db_expression will return false. + * In this case, clear previous condition of the break point. + * If condition is supplied, set the condition to the permanent area. + * Note: db_expression will not return here, if the condition + * expression is wrong. + */ + db_switch_input(cp->c_cond_cmd, p - cp->c_cond_cmd); + if (!db_expression(&value)) { + /* since condition is already freed, do nothing */ + db_flush_lex(); + return; + } + db_flush_lex(); + db_ncond_free--; + cp->c_size = p - cp->c_cond_cmd; + bkpt->tb_cond = (cp - db_cond) + 1; +} + +#endif /* MACH_KDB */ diff --git a/ddb/db_cond.h b/ddb/db_cond.h new file mode 100644 index 0000000..c867c6e --- /dev/null +++ b/ddb/db_cond.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2007 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Barry deFreese. + */ + +#ifndef _DDB_DB_COND_H_ +#define _DDB_DB_COND_H_ + +#include +#include + +extern void db_cond_free (const db_thread_breakpoint_t bkpt); + +extern boolean_t db_cond_check (db_thread_breakpoint_t bkpt); + +extern void db_cond_print (db_thread_breakpoint_t bkpt); + +extern void db_cond_cmd ( + db_expr_t addr, + boolean_t have_addr, + db_expr_t count, + const char * modif); + +#endif /* _DDB_DB_COND_H_ */ diff --git a/ddb/db_elf.c b/ddb/db_elf.c new file mode 100644 index 0000000..5ccfdd5 --- /dev/null +++ b/ddb/db_elf.c @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2014 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * 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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 7/90 + */ + +#if MACH_KDB + +/* + * Symbol table routines for ELF format files. + */ + +#include +#include +#include +#include /* data types */ +#include +#include +#include +#include + +#ifndef DB_NO_ELF + +struct db_symtab_elf { + int type; + Elf_Sym *start; + Elf_Sym *end; + char *strings; + char *map_pointer; /* symbols are for this map only, + if not null */ + char name[SYMTAB_NAME_LEN]; + /* symtab name */ +}; + +boolean_t +elf_db_sym_init (unsigned shdr_num, + vm_size_t shdr_size, + vm_offset_t shdr_addr, + unsigned shdr_shndx, + char *name, + char *task_addr) +{ + Elf_Shdr *shdr, *symtab, *strtab; + const char *shstrtab; + unsigned i; + + if (shdr_num == 0) + return FALSE; + + if (shdr_size != sizeof *shdr) + return FALSE; + + shdr = (Elf_Shdr *) shdr_addr; + + if (shdr[shdr_shndx].sh_type != SHT_STRTAB) + return FALSE; + + shstrtab = (const char *) phystokv (shdr[shdr_shndx].sh_addr); + + symtab = strtab = NULL; + for (i = 0; i < shdr_num; i++) + switch (shdr[i].sh_type) { + case SHT_SYMTAB: + if (symtab) + db_printf ("Ignoring additional ELF symbol table at %d\n", i); + else + symtab = &shdr[i]; + break; + + case SHT_STRTAB: + if (strcmp (&shstrtab[shdr[i].sh_name], ".strtab") == 0) { + if (strtab) + db_printf ("Ignoring additional ELF string table at %d\n", i); + else + strtab = &shdr[i]; + } + break; + } + + if (symtab == NULL || strtab == NULL) + return FALSE; + + if (db_add_symbol_table (SYMTAB_ELF, + (char *) phystokv (symtab->sh_addr), + (char *) phystokv (symtab->sh_addr)+symtab->sh_size, + name, + (char *) phystokv (strtab->sh_addr), + task_addr)) { + db_printf ("Loaded ELF symbol table for %s (%d symbols)\n", + name, symtab->sh_size / sizeof (Elf_Sym)); + return TRUE; + } + + return FALSE; +} + +/* + * lookup symbol by name + */ +db_sym_t +elf_db_lookup (db_symtab_t *stab, + char *symstr) +{ + struct db_symtab_elf *self = (struct db_symtab_elf *) stab; + Elf_Sym *s; + + for (s = self->start; s < self->end; s++) + if (strcmp (symstr, &self->strings[s->st_name]) == 0) + return (db_sym_t) s; + + return NULL; +} + +db_sym_t +elf_db_search_symbol (db_symtab_t *stab, + db_addr_t off, + db_strategy_t strategy, + db_expr_t *diffp) /* in/out */ +{ + struct db_symtab_elf *self = (struct db_symtab_elf *) stab; + unsigned long diff = *diffp; + Elf_Sym *s, *symp = NULL; + + for (s = self->start; s < self->end; s++) { + if (s->st_name == 0) + continue; + + if (strategy == DB_STGY_XTRN && (ELF_ST_BIND(s->st_info) != STB_GLOBAL)) + continue; + + if (off >= s->st_value) { + if (ELF_ST_TYPE(s->st_info) != STT_FUNC) + continue; + + if (off - s->st_value < diff) { + diff = off - s->st_value; + symp = s; + if (diff == 0 && (ELF_ST_BIND(s->st_info) == STB_GLOBAL)) + break; + } else if (off - s->st_value == diff) { + if (symp == NULL) + symp = s; + else if ((ELF_ST_BIND(symp->st_info) != STB_GLOBAL) + && (ELF_ST_BIND(s->st_info) == STB_GLOBAL)) + symp = s; /* pick the external symbol */ + } + } + } + + if (symp == NULL) + *diffp = off; + else + *diffp = diff; + + return (db_sym_t) symp; +} + +/* + * Return the name and value for a symbol. + */ +void +elf_db_symbol_values (db_symtab_t *stab, + db_sym_t sym, + char **namep, + db_expr_t *valuep) +{ + struct db_symtab_elf *self = (struct db_symtab_elf *) stab; + Elf_Sym *s = (Elf_Sym *) sym; + + if (namep) + *namep = &self->strings[s->st_name]; + if (valuep) + *valuep = s->st_value; +} + +/* + * Find filename and lineno within, given the current pc. + */ +boolean_t +elf_db_line_at_pc (db_symtab_t *stab, + db_sym_t sym, + char **file, + int *line, + db_addr_t pc) +{ + /* XXX Parse DWARF information. */ + return FALSE; +} + +#endif /* DB_NO_ELF */ + +#endif /* MACH_KDB */ diff --git a/ddb/db_elf.h b/ddb/db_elf.h new file mode 100644 index 0000000..12b8286 --- /dev/null +++ b/ddb/db_elf.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2013 Free Software Foundation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _DDB_DB_ELF_H_ +#define _DDB_DB_ELF_H_ + +#include +#include + +extern boolean_t +elf_db_line_at_pc( + db_symtab_t *stab, + db_sym_t sym, + char **file, + int *line, + db_addr_t pc); + +extern db_sym_t +elf_db_lookup( + db_symtab_t *stab, + char * symstr); + +extern db_sym_t +elf_db_search_symbol( + db_symtab_t * symtab, + db_addr_t off, + db_strategy_t strategy, + db_expr_t *diffp); + +extern void +elf_db_symbol_values( + db_symtab_t *stab, + db_sym_t sym, + char **namep, + db_expr_t *valuep); + +#endif /* _DDB_DB_ELF_H_ */ diff --git a/ddb/db_examine.c b/ddb/db_examine.c new file mode 100644 index 0000000..1941fc3 --- /dev/null +++ b/ddb/db_examine.c @@ -0,0 +1,664 @@ +/* + * Mach Operating System + * Copyright (c) 1992,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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 7/90 + */ + +#if MACH_KDB + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define db_thread_to_task(thread) ((thread)? thread->task: TASK_NULL) + +char db_examine_format[TOK_STRING_SIZE] = "x"; +int db_examine_count = 1; +db_addr_t db_examine_prev_addr = 0; +thread_t db_examine_thread = THREAD_NULL; + +/* + * Examine (print) data. + */ +/*ARGSUSED*/ +void +db_examine_cmd( + db_expr_t addr, + int have_addr, + db_expr_t count, + const char * modif) +{ + thread_t thread; + + if (modif[0] != '\0') + db_strcpy(db_examine_format, modif); + + if (count == -1) + count = 1; + db_examine_count = count; + if (db_option(modif, 't')) + { + if (!db_get_next_thread(&thread, 0)) + return; + } + else + if (db_option(modif, 'u')) + thread = current_thread(); + else + thread = THREAD_NULL; + + db_examine_thread = thread; + db_examine((db_addr_t) addr, db_examine_format, count, + db_thread_to_task(thread)); +} + +/* ARGSUSED */ +void +db_examine_forward( + db_expr_t addr, + int have_addr, + db_expr_t count, + const char * modif) +{ + db_examine(db_next, db_examine_format, db_examine_count, + db_thread_to_task(db_examine_thread)); +} + +/* ARGSUSED */ +void +db_examine_backward( + db_expr_t addr, + int have_addr, + db_expr_t count, + const char * modif) +{ + + db_examine(db_examine_prev_addr - (db_next - db_examine_prev_addr), + db_examine_format, db_examine_count, + db_thread_to_task(db_examine_thread)); +} + +void +db_examine( + db_addr_t addr, + const char * fmt, /* format string */ + int count, /* repeat count */ + task_t task) +{ + int c; + db_expr_t value; + int size; /* in bytes */ + int width; + const char * fp; + + db_examine_prev_addr = addr; + while (--count >= 0) { + fp = fmt; + size = 4; + width = 4*size; + while ((c = *fp++) != 0) { + switch (c) { + case 'b': + size = 1; + width = 4*size; + break; + case 'h': + size = 2; + width = 4*size; + break; + case 'l': + size = 4; + width = 4*size; + break; + case 'q': + size = 8; + width = 4*size; + break; + case 'a': /* address */ + case 'A': /* function address */ + /* always forces a new line */ + if (db_print_position() != 0) + db_printf("\n"); + db_prev = addr; + db_task_printsym(addr, + (c == 'a')?DB_STGY_ANY:DB_STGY_PROC, + task); + db_printf(":\t"); + break; + case 'm': + db_next = db_xcdump(addr, size, count + 1, task); + return; + default: + if (db_print_position() == 0) { + /* If we hit a new symbol, print it */ + char * name; + db_addr_t off; + + db_find_task_sym_and_offset(addr, &name, &off, task); + if (off == 0) + db_printf("%s:\t", name); + else + db_printf("\t\t"); + + db_prev = addr; + } + + switch (c) { + case ',': /* skip one unit w/o printing */ + addr += size; + break; + + case 'r': /* signed, current radix */ + value = db_get_task_value(addr,size,TRUE,task); + addr += size; + db_printf("%-*R", width, value); + break; + case 'x': /* unsigned hex */ + value = db_get_task_value(addr,size,FALSE,task); + addr += size; + db_printf("%-*X", width, value); + break; + case 'z': /* signed hex */ + value = db_get_task_value(addr,size,TRUE,task); + addr += size; + db_printf("%-*Z", width, value); + break; + case 'd': /* signed decimal */ + value = db_get_task_value(addr,size,TRUE,task); + addr += size; + db_printf("%-*D", width, value); + break; + case 'U': /* unsigned decimal */ + value = db_get_task_value(addr,size,FALSE,task); + addr += size; + db_printf("%-*U", width, value); + break; + case 'o': /* unsigned octal */ + value = db_get_task_value(addr,size,FALSE,task); + addr += size; + db_printf("%-*O", width, value); + break; + case 'c': /* character */ + value = db_get_task_value(addr,1,FALSE,task); + addr += 1; + if (value >= ' ' && value <= '~') + db_printf("%c", value); + else + db_printf("\\%03o", value); + break; + case 's': /* null-terminated string */ + for (;;) { + value = db_get_task_value(addr,1,FALSE,task); + 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, task); + break; + case 'I': /* instruction, alternate form */ + addr = db_disasm(addr, TRUE, task); + break; + default: + break; + } + if (db_print_position() != 0) + db_end_line(); + break; + } + } + } + db_next = addr; +} + +/* + * Find out what this address may be + */ +/*ARGSUSED*/ +void +db_whatis_cmd( + db_expr_t addr, + int have_addr, + db_expr_t count, + const char * modif) +{ + /* TODO: Add whatever you can think of */ + + int i; + + { + /* tasks */ + + task_t task; + int task_id = 0; + processor_set_t pset; + thread_t thread; + int thread_id; + vm_map_entry_t entry; + + queue_iterate(&all_psets, pset, processor_set_t, all_psets) + queue_iterate(&pset->tasks, task, task_t, pset_tasks) { + if (addr >= (vm_offset_t) task + && addr < (vm_offset_t) task + sizeof(*task)) + db_printf("%3d %0*X %s [%d]\n", + task_id, + 2*sizeof(vm_offset_t), + task, + task->name, + task->thread_count); + + if (addr >= (vm_offset_t) task->map + && addr < (vm_offset_t) task->map + sizeof(*(task->map))) + db_printf("$map%d %X for $task%d %s\n", + task_id, (vm_offset_t) task->map, task_id, task->name); + + for (entry = vm_map_first_entry(task->map); + entry != vm_map_to_entry(task->map); + entry = entry->vme_next) + if (addr >= (vm_offset_t) entry + && addr < (vm_offset_t) entry + sizeof(*entry)) + db_printf("$map%d %X for $task%d %s entry 0x%X: ", + task_id, (vm_offset_t) task->map, task_id, task->name, + (vm_offset_t) entry); + + if (pmap_whatis(task->map->pmap, addr)) + db_printf(" in $task%d %s\n", task_id, task->name); + + if ((task == current_task() || task == kernel_task) + && addr >= vm_map_min(task->map) + && addr < vm_map_max(task->map)) { + db_printf("inside $map%d of $task%d %s\n", task_id, task_id, task->name); + + for (entry = vm_map_first_entry(task->map); + entry != vm_map_to_entry(task->map); + entry = entry->vme_next) + if (addr >= entry->vme_start + && addr < entry->vme_end) { + db_printf(" entry 0x%X: ", (vm_offset_t) entry); + if (entry->is_sub_map) + db_printf("submap=0x%X, offset=0x%X\n", + (vm_offset_t) entry->object.sub_map, + (vm_offset_t) entry->offset); + else + db_printf("object=0x%X, offset=0x%X\n", + (vm_offset_t) entry->object.vm_object, + (vm_offset_t) entry->offset); + } + } + + thread_id = 0; + queue_iterate(&task->thread_list, thread, thread_t, thread_list) { + if (addr >= (vm_offset_t) thread + && addr < (vm_offset_t) thread + sizeof(*thread)) { + db_printf("In $task%d %s\n", task_id, task->name); + db_print_thread(thread, thread_id, 0); + } + if (addr >= thread->kernel_stack + && addr < thread->kernel_stack + KERNEL_STACK_SIZE) { + db_printf("In $task%d %s\n", task_id, task->name); + db_printf(" on stack of $thread%d.%d\n", task_id, thread_id); + db_print_thread(thread, thread_id, 0); + } + thread_id++; + } + task_id++; + } + } + + pmap_whatis(kernel_pmap, addr); + + { + /* runqs */ + if (addr >= (vm_offset_t) &default_pset.runq + && addr < (vm_offset_t) &default_pset.runq + sizeof(default_pset.runq)) + db_printf("default runq %p\n", &default_pset.runq); + for (i = 0; i < smp_get_numcpus(); i++) { + processor_t proc = cpu_to_processor(i); + if (addr >= (vm_offset_t) &proc->runq + && addr < (vm_offset_t) &proc->runq + sizeof(proc->runq)) + db_printf("Processor #%d runq %p\n", &proc->runq); + } + } + + { + /* stacks */ + for (i = 0; i < smp_get_numcpus(); i++) { + if (addr >= percpu_array[i].active_stack + && addr < percpu_array[i].active_stack + KERNEL_STACK_SIZE) + db_printf("Processor #%d active stack\n", i); + } + } + + db_whatis_slab(addr); + + { + /* page */ + phys_addr_t pa; + if (DB_VALID_KERN_ADDR(addr)) + pa = kvtophys(addr); + else + pa = pmap_extract(current_task()->map->pmap, addr); + + if (pa) { + struct vm_page *page = vm_page_lookup_pa(pa); + db_printf("phys %llx, page %p\n", (unsigned long long) pa, page); + if (page) { + const char *types[] = { + [VM_PT_FREE] = "free", + [VM_PT_RESERVED] = "reserved", + [VM_PT_TABLE] = "table", + [VM_PT_KERNEL] = "kernel", + }; + db_printf(" %s\n", types[page->type]); + db_printf(" free %u\n", page->free); + db_printf(" external %u\n", page->external); + db_printf(" busy %u\n", page->busy); + db_printf(" private %u\n", page->private); + db_printf(" object %lx\n", page->object); + db_printf(" offset %lx\n", page->offset); + db_printf(" wired %u\n", page->wire_count); + db_printf(" segment %u\n", page->seg_index); + db_printf(" order %u\n", page->order); + } + } + } +} + +/* + * Print value. + */ +char db_print_format = 'x'; + +/*ARGSUSED*/ +void +db_print_cmd(void) +{ + db_expr_t value; + int t; + task_t task = TASK_NULL; + + if ((t = db_read_token()) == tSLASH) { + if (db_read_token() != tIDENT) { + db_printf("Bad modifier \"/%s\"\n", db_tok_string); + db_error(0); + /* NOTREACHED */ + } + if (db_tok_string[0]) + db_print_format = db_tok_string[0]; + if (db_option(db_tok_string, 't') && db_default_thread) + task = db_default_thread->task; + } else + db_unread_token(t); + + for ( ; ; ) { + t = db_read_token(); + if (t == tSTRING) { + db_printf("%s", db_tok_string); + continue; + } + db_unread_token(t); + if (!db_expression(&value)) + break; + switch (db_print_format) { + case 'a': + db_task_printsym((db_addr_t)value, DB_STGY_ANY, task); + break; + case 'r': + db_printf("%*r", 3+2*sizeof(db_expr_t), value); + break; + case 'x': + db_printf("%*x", 2*sizeof(db_expr_t), value); + break; + case 'z': + db_printf("%*z", 2*sizeof(db_expr_t), value); + break; + case 'd': + db_printf("%*d", 3+2*sizeof(db_expr_t), value); + break; + case 'u': + db_printf("%*u", 3+2*sizeof(db_expr_t), value); + break; + case 'o': + db_printf("%o", 4*sizeof(db_expr_t), value); + break; + case 'c': + value = value & 0xFF; + if (value >= ' ' && value <= '~') + db_printf("%c", value); + else + db_printf("\\%03o", value); + break; + default: + db_printf("Unknown format %c\n", db_print_format); + db_print_format = 'x'; + db_error(0); + } + } +} + +void +db_print_loc_and_inst( + db_addr_t loc, + task_t task) +{ + db_task_printsym(loc, DB_STGY_PROC, task); + db_printf(":\t"); + (void) db_disasm(loc, TRUE, task); +} + +void +db_strcpy(char *dst, const char *src) +{ + while ((*dst++ = *src++)) + ; +} + +/* + * Search for a value in memory. + * Syntax: search [/bhl] addr value [mask] [,count] [thread] + */ +void +db_search_cmd( + db_expr_t e, + boolean_t b, + db_expr_t e2, + const char * cc) +{ + int t; + db_addr_t addr; + int size = 0; + db_expr_t value; + db_expr_t mask; + db_addr_t count; + thread_t thread; + boolean_t thread_flag = FALSE; + char *p; + + t = db_read_token(); + if (t == tSLASH) { + t = db_read_token(); + if (t != tIDENT) { + bad_modifier: + db_printf("Bad modifier \"/%s\"\n", db_tok_string); + db_flush_lex(); + return; + } + + for (p = db_tok_string; *p; p++) { + switch(*p) { + case 'b': + size = sizeof(char); + break; + case 'h': + size = sizeof(short); + break; + case 'l': + size = sizeof(long); + break; + case 't': + thread_flag = TRUE; + break; + default: + goto bad_modifier; + } + } + } else { + db_unread_token(t); + size = sizeof(int); + } + + 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 = ~0; + + t = db_read_token(); + if (t == tCOMMA) { + if (!db_expression((db_expr_t *)&count)) { + db_printf("Count missing\n"); + db_flush_lex(); + return; + } + } else { + db_unread_token(t); + count = -1; /* effectively forever */ + } + if (thread_flag) { + if (!db_get_next_thread(&thread, 0)) + return; + } else + thread = THREAD_NULL; + + db_search(addr, size, value, mask, count, db_thread_to_task(thread)); +} + +void +db_search( + db_addr_t addr, + int size, + db_expr_t value, + db_expr_t mask, + unsigned int count, + task_t task) +{ + while (count-- != 0) { + db_prev = addr; + if ((db_get_task_value(addr, size, FALSE, task) & mask) == value) + break; + addr += size; + } + db_next = addr; +} + +#define DB_XCDUMP_NC 16 + +int +db_xcdump( + db_addr_t addr, + int size, + int count, + task_t task) +{ + int i, n; + db_expr_t value; + int bcount; + db_addr_t off; + char *name; + char data[DB_XCDUMP_NC]; + + db_find_task_sym_and_offset(addr, &name, &off, task); + for (n = count*size; n > 0; n -= bcount) { + db_prev = addr; + if (off == 0) { + db_printf("%s:\n", name); + off = -1; + } + db_printf("%0*X:%s", 2*sizeof(db_addr_t), addr, + (size != 1)? " ": ""); + bcount = ((n > DB_XCDUMP_NC)? DB_XCDUMP_NC: n); + if (trunc_page(addr) != trunc_page(addr+bcount-1)) { + db_addr_t next_page_addr = trunc_page(addr+bcount-1); + if (!DB_CHECK_ACCESS(next_page_addr, sizeof(int), task)) + bcount = next_page_addr - addr; + } + if (!db_read_bytes(addr, bcount, data, task)) { + db_printf("*\n"); + continue; + } + for (i = 0; i < bcount && off != 0; i += size) { + if (i % 4 == 0) + db_printf(" "); + value = db_get_task_value(addr, size, FALSE, task); + db_printf("%0*x ", size*2, value); + addr += size; + db_find_task_sym_and_offset(addr, &name, &off, task); + } + db_printf("%*s", + ((DB_XCDUMP_NC-i)/size)*(size*2+1)+(DB_XCDUMP_NC-i)/4, + ""); + bcount = i; + db_printf("%s*", (size != 1)? " ": ""); + for (i = 0; i < bcount; i++) { + value = data[i]; + db_printf("%c", (value >= ' ' && value <= '~')? value: '.'); + } + db_printf("*\n"); + } + return(addr); +} + +#endif /* MACH_KDB */ diff --git a/ddb/db_examine.h b/ddb/db_examine.h new file mode 100644 index 0000000..c76fa2a --- /dev/null +++ b/ddb/db_examine.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2007 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Barry deFreese. + */ + +#ifndef _DDB_DB_EXAMINE_H_ +#define _DDB_DB_EXAMINE_H_ + +#include +#include +#include + +extern void db_examine_cmd ( + db_expr_t addr, + int have_addr, + db_expr_t count, + const char *modif); + +extern void db_strcpy (char *dst, const char *src); + +extern void db_examine ( + db_addr_t addr, + const char *fmt, + int count, + task_t task); + +void db_examine_forward( + db_expr_t addr, + int have_addr, + db_expr_t count, + const char * modif); + +void db_examine_backward( + db_expr_t addr, + int have_addr, + db_expr_t count, + const char * modif); + +extern void db_print_loc_and_inst ( + db_addr_t loc, + task_t task); + +int db_xcdump( + db_addr_t addr, + int size, + int count, + task_t task); + +extern void db_whatis_cmd ( + db_expr_t addr, + int have_addr, + db_expr_t count, + const char *modif); + +void db_print_cmd(void); + +void db_search_cmd( + db_expr_t addr, + boolean_t have_addr, + db_expr_t count, + const char * modif); + +void db_search( + db_addr_t addr, + int size, + db_expr_t value, + db_expr_t mask, + unsigned int count, + task_t task); + +/* instruction disassembler */ +extern db_addr_t db_disasm( + db_addr_t pc, + boolean_t altform, + task_t task); + +#endif /* _DDB_DB_EXAMINE_H_ */ diff --git a/ddb/db_expr.c b/ddb/db_expr.c new file mode 100644 index 0000000..90edb6f --- /dev/null +++ b/ddb/db_expr.c @@ -0,0 +1,382 @@ +/* + * 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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 7/90 + */ + +#if MACH_KDB + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static boolean_t +db_term(db_expr_t *valuep) +{ + int t; + + switch(t = db_read_token()) { + case tIDENT: + if (!db_value_of_name(db_tok_string, valuep)) { + db_printf("Symbol \"%s\" not found\n", db_tok_string); + db_error(0); + /*NOTREACHED*/ + } + return (TRUE); + case tNUMBER: + *valuep = db_tok_number; + return (TRUE); + case tDOT: + *valuep = (db_expr_t)db_dot; + return (TRUE); + case tDOTDOT: + *valuep = (db_expr_t)db_prev; + return (TRUE); + case tPLUS: + *valuep = (db_expr_t) db_next; + return (TRUE); + case tQUOTE: + *valuep = (db_expr_t)db_last_addr; + return (TRUE); + case tDOLLAR: + if (!db_get_variable(valuep)) + return (FALSE); + return (TRUE); + case tLPAREN: + if (!db_expression(valuep)) { + db_error("Unmached ()s\n"); + /*NOTREACHED*/ + } + t = db_read_token(); + if (t != tRPAREN) { + db_printf("')' expected at \"%s...\"\n", db_tok_string); + db_error(0); + /*NOTREACHED*/ + } + return (TRUE); + default: + db_unread_token(t); + return (FALSE); + } +} + +int +db_size_option(const char *modif, boolean_t *u_option, boolean_t *t_option) +{ + const char *p; + int size = sizeof(int); + + *u_option = FALSE; + *t_option = FALSE; + for (p = modif; *p; p++) { + switch(*p) { + case 'b': + size = sizeof(char); + break; + case 'h': + size = sizeof(short); + break; + case 'l': + size = sizeof(long); + break; + case 'u': + *u_option = TRUE; + break; + case 't': + *t_option = TRUE; + break; + } + } + return(size); +} + +static boolean_t +db_unary(db_expr_t *valuep) +{ + int t; + int size; + boolean_t u_opt, t_opt; + task_t task; + extern task_t db_default_task; + + t = db_read_token(); + if (t == tMINUS) { + if (!db_unary(valuep)) { + db_error("Expression syntax error after '-'\n"); + /*NOTREACHED*/ + } + *valuep = -*valuep; + return (TRUE); + } + if (t == tSTAR) { + /* indirection */ + if (!db_unary(valuep)) { + db_error("Expression syntax error after '*'\n"); + /*NOTREACHED*/ + } + task = TASK_NULL; + size = sizeof(db_addr_t); + u_opt = FALSE; + t = db_read_token(); + if (t == tIDENT && db_tok_string[0] == ':') { + size = db_size_option(&db_tok_string[1], &u_opt, &t_opt); + if (t_opt) + task = db_default_task; + } else + db_unread_token(t); + *valuep = db_get_task_value((db_addr_t)*valuep, size, !u_opt, task); + return (TRUE); + } + if (t == tEXCL) { + if (!db_unary(valuep)) { + db_error("Expression syntax error after '!'\n"); + /*NOTREACHED*/ + } + *valuep = (!(*valuep)); + return (TRUE); + } + db_unread_token(t); + return (db_term(valuep)); +} + +static boolean_t +db_mult_expr(db_expr_t *valuep) +{ + db_expr_t lhs = 0, rhs; + int t; + char c; + + if (!db_unary(&lhs)) + return (FALSE); + + t = db_read_token(); + while (t == tSTAR || t == tSLASH || t == tPCT || t == tHASH + || t == tBIT_AND) { + c = db_tok_string[0]; + if (!db_term(&rhs)) { + db_printf("Expression syntax error after '%c'\n", c); + db_error(0); + /*NOTREACHED*/ + } + switch(t) { + case tSTAR: + lhs *= rhs; + break; + case tBIT_AND: + lhs &= rhs; + break; + default: + 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); +} + +static boolean_t +db_add_expr(db_expr_t *valuep) +{ + db_expr_t lhs, rhs; + int t; + char c; + + if (!db_mult_expr(&lhs)) + return (FALSE); + + t = db_read_token(); + while (t == tPLUS || t == tMINUS || t == tBIT_OR) { + c = db_tok_string[0]; + if (!db_mult_expr(&rhs)) { + db_printf("Expression syntax error after '%c'\n", c); + db_error(0); + /*NOTREACHED*/ + } + if (t == tPLUS) + lhs += rhs; + else if (t == tMINUS) + lhs -= rhs; + else + lhs |= rhs; + t = db_read_token(); + } + db_unread_token(t); + *valuep = lhs; + return (TRUE); +} + +static boolean_t +db_shift_expr(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_printf("Expression syntax error after \"%s\"\n", + (t == tSHIFT_L)? "<<": ">>"); + db_error(0); + /*NOTREACHED*/ + } + if (rhs < 0) { + db_error("Negative shift amount\n"); + /*NOTREACHED*/ + } + if (t == tSHIFT_L) + lhs <<= rhs; + else { + /* Shift right is unsigned */ + lhs = (natural_t) lhs >> rhs; + } + t = db_read_token(); + } + db_unread_token(t); + *valuep = lhs; + return (TRUE); +} + +static boolean_t +db_logical_relation_expr(db_expr_t *valuep) +{ + db_expr_t lhs, rhs; + int t; + char op[3]; + + if (!db_shift_expr(&lhs)) + return(FALSE); + + t = db_read_token(); + while (t == tLOG_EQ || t == tLOG_NOT_EQ + || t == tGREATER || t == tGREATER_EQ + || t == tLESS || t == tLESS_EQ) { + op[0] = db_tok_string[0]; + op[1] = db_tok_string[1]; + op[2] = 0; + if (!db_shift_expr(&rhs)) { + db_printf("Expression syntax error after \"%s\"\n", op); + db_error(0); + /*NOTREACHED*/ + } + switch(t) { + case tLOG_EQ: + lhs = (lhs == rhs); + break; + case tLOG_NOT_EQ: + lhs = (lhs != rhs); + break; + case tGREATER: + lhs = (lhs > rhs); + break; + case tGREATER_EQ: + lhs = (lhs >= rhs); + break; + case tLESS: + lhs = (lhs < rhs); + break; + case tLESS_EQ: + lhs = (lhs <= rhs); + break; + } + t = db_read_token(); + } + db_unread_token(t); + *valuep = lhs; + return (TRUE); +} + +static boolean_t +db_logical_and_expr(db_expr_t *valuep) +{ + db_expr_t lhs, rhs; + int t; + + if (!db_logical_relation_expr(&lhs)) + return(FALSE); + + t = db_read_token(); + while (t == tLOG_AND) { + if (!db_logical_relation_expr(&rhs)) { + db_error("Expression syntax error after \"&&\"\n"); + /*NOTREACHED*/ + } + lhs = (lhs && rhs); + } + db_unread_token(t); + *valuep = lhs; + return (TRUE); +} + +static boolean_t +db_logical_or_expr(db_expr_t *valuep) +{ + db_expr_t lhs, rhs; + int t; + + if (!db_logical_and_expr(&lhs)) + return(FALSE); + + t = db_read_token(); + while (t == tLOG_OR) { + if (!db_logical_and_expr(&rhs)) { + db_error("Expression syntax error after \"||\"\n"); + /*NOTREACHED*/ + } + lhs = (lhs || rhs); + } + db_unread_token(t); + *valuep = lhs; + return (TRUE); +} + +int +db_expression(db_expr_t *valuep) +{ + return (db_logical_or_expr(valuep)); +} + +#endif /* MACH_KDB */ diff --git a/ddb/db_expr.h b/ddb/db_expr.h new file mode 100644 index 0000000..9c304e6 --- /dev/null +++ b/ddb/db_expr.h @@ -0,0 +1,26 @@ +/* + * (c) Copyright 1992, 1993, 1994, 1995 OPEN SOFTWARE FOUNDATION, INC. + * ALL RIGHTS RESERVED + */ +/* + * OSF RI nmk19b2 5/2/95 + */ + +#ifndef _DDB_DB_EXPR_H_ +#define _DDB_DB_EXPR_H_ + +#include +#include + + +/* Prototypes for functions exported by this module. + */ + +int db_size_option( + const char *modif, + boolean_t *u_option, + boolean_t *t_option); + +int db_expression(db_expr_t *valuep); + +#endif /* !_DDB_DB_EXPR_H_ */ diff --git a/ddb/db_ext_symtab.c b/ddb/db_ext_symtab.c new file mode 100644 index 0000000..db7bec2 --- /dev/null +++ b/ddb/db_ext_symtab.c @@ -0,0 +1,123 @@ +/* + * 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. + */ + +#if MACH_KDB + +#if MACH_DEBUG + +#include /* vm_address_t */ +#include /* pointer_t */ +#include +#include +#include +#include +#include +#include +#include +#include + + + +/* + * Loads a symbol table for an external file into the kernel debugger. + * The symbol table data is an array of characters. It is assumed that + * the caller and the kernel debugger agree on its format. + */ +kern_return_t +host_load_symbol_table( + host_t host, + task_t task, + const char * name, + pointer_t symtab, + unsigned int symtab_count) +{ + kern_return_t result; + vm_offset_t symtab_start; + vm_offset_t symtab_end; + vm_map_t map; + vm_map_copy_t symtab_copy_object; + + if (host == HOST_NULL) + return (KERN_INVALID_ARGUMENT); + + /* + * Copy the symbol table array into the kernel. + * We make a copy of the copy object, and clear + * the old one, so that returning error will not + * deallocate the data twice. + */ + symtab_copy_object = (vm_map_copy_t) symtab; + result = vm_map_copyout( + kernel_map, + &symtab_start, + vm_map_copy_copy(symtab_copy_object)); + if (result != KERN_SUCCESS) + return (result); + + symtab_end = symtab_start + symtab_count; + + /* + * Add the symbol table. + * Do not keep a reference for the task map. XXX + */ + if (task == TASK_NULL) + map = VM_MAP_NULL; + else + map = task->map; + if (!X_db_sym_init((char *)symtab_start, + (char *)symtab_end, + name, + (char *)map)) + { + /* + * Not enough room for symbol table - failure. + */ + (void) vm_deallocate(kernel_map, + symtab_start, + symtab_count); + return (KERN_FAILURE); + } + + /* + * Wire down the symbol table + */ + (void) vm_map_pageable(kernel_map, + symtab_start, + round_page(symtab_end), + VM_PROT_READ|VM_PROT_WRITE, + TRUE, TRUE); + + /* + * Discard the original copy object + */ + vm_map_copy_discard(symtab_copy_object); + + return (KERN_SUCCESS); +} + +#endif /* MACH_DEBUG */ + +#endif /* MACH_KDB */ diff --git a/ddb/db_input.c b/ddb/db_input.c new file mode 100644 index 0000000..357474b --- /dev/null +++ b/ddb/db_input.c @@ -0,0 +1,414 @@ +/* + * Mach Operating System + * Copyright (c) 1992,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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 7/90 + */ + +#if MACH_KDB + +#include +#include +#include +#include +#include +#include + +#ifndef DB_HISTORY_SIZE +#define DB_HISTORY_SIZE 4000 +#endif /* DB_HISTORY_SIZE */ + +/* + * 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 */ +#if DB_HISTORY_SIZE != 0 +char db_history[DB_HISTORY_SIZE]; /* start of history buffer */ +int db_history_size = DB_HISTORY_SIZE;/* size of history buffer */ +char * db_history_curr = db_history; /* start of current line */ +char * db_history_last = db_history; /* start of last line */ +char * db_history_prev = (char *) 0; /* start of previous line */ +#endif + +#define CTRL(c) ((c) & 0x1f) +#define isspace(c) ((c) == ' ' || (c) == '\t') +#define BLANK ' ' +#define BACKUP '\b' + +static void +db_putstring(const char *s, int count) +{ + while (--count >= 0) + cnputc(*s++); +} + +static void +db_putnchars(int c, int count) +{ + while (--count >= 0) + cnputc(c); +} + +/* + * Delete N characters, forward or backward + */ +#define DEL_FWD 0 +#define DEL_BWD 1 +static void +db_delete( + int n, + int bwd) +{ + 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; +} + +static void +db_delete_line(void) +{ + db_delete(db_le - db_lc, DEL_FWD); + db_delete(db_lc - db_lbuf_start, DEL_BWD); + db_le = db_lc = db_lbuf_start; +} + +#if DB_HISTORY_SIZE != 0 +#define INC_DB_CURR() \ + do { \ + db_history_curr++; \ + if (db_history_curr > \ + db_history + db_history_size - 1) \ + db_history_curr = db_history; \ + } while (0) +#define DEC_DB_CURR() \ + do { \ + db_history_curr--; \ + if (db_history_curr < db_history) \ + db_history_curr = db_history + \ + db_history_size - 1; \ + } while (0) +#endif /* DB_HISTORY_SIZE */ + +/* returns TRUE at end-of-line */ +static boolean_t +db_inputchar(int c) +{ + static int escaped, csi; + int was_escaped = escaped, was_csi = csi; + escaped = 0; + csi = 0; + + switch (c) { + case CTRL('b'): + left: + /* back up one character */ + if (db_lc > db_lbuf_start) { + cnputc(BACKUP); + db_lc--; + } + break; + case CTRL('f'): + right: + /* 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('u'): + /* delete line */ + db_delete_line(); + 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; +#if DB_HISTORY_SIZE != 0 + case CTRL('p'): + up: + DEC_DB_CURR(); + while (db_history_curr != db_history_last) { + DEC_DB_CURR(); + if (*db_history_curr == '\0') + break; + } + db_delete_line(); + if (db_history_curr == db_history_last) { + INC_DB_CURR(); + db_le = db_lc = db_lbuf_start; + } else { + char *p; + INC_DB_CURR(); + for (p = db_history_curr, db_le = db_lbuf_start; + *p; ) { + *db_le++ = *p++; + if (p == db_history + db_history_size) { + p = db_history; + } + } + db_lc = db_le; + } + db_putstring(db_lbuf_start, db_le - db_lbuf_start); + break; + case CTRL('n'): + down: + while (db_history_curr != db_history_last) { + if (*db_history_curr == '\0') + break; + INC_DB_CURR(); + } + if (db_history_curr != db_history_last) { + INC_DB_CURR(); + db_delete_line(); + if (db_history_curr != db_history_last) { + char *p; + for (p = db_history_curr, + db_le = db_lbuf_start; *p;) { + *db_le++ = *p++; + if (p == db_history + + db_history_size) { + p = db_history; + } + } + db_lc = db_le; + } + db_putstring(db_lbuf_start, db_le - db_lbuf_start); + } + break; +#endif /* DB_HISTORY_SIZE */ + 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': +#if DB_HISTORY_SIZE != 0 + /* + * Check whether current line is the same + * as previous saved line. If it is, don`t + * save it. + */ + if (db_history_curr == db_history_prev) { + char *pp, *pc; + + /* + * Is it the same? + */ + for (pp = db_history_prev, pc = db_lbuf_start; + pc != db_le && *pp; ) { + if (*pp != *pc) + break; + if (++pp == db_history + db_history_size) { + pp = db_history; + } + pc++; + } + if (!*pp && pc == db_le) { + /* + * Repeated previous line. Don`t save. + */ + db_history_curr = db_history_last; + *db_le++ = c; + return (TRUE); + } + } + if (db_le != db_lbuf_start) { + char *p; + db_history_prev = db_history_last; + for (p = db_lbuf_start; p != db_le; p++) { + *db_history_last++ = *p; + if (db_history_last == db_history + + db_history_size) { + db_history_last = db_history; + } + } + *db_history_last++ = '\0'; + } + db_history_curr = db_history_last; +#endif /* DB_HISTORY_SIZE */ + *db_le++ = c; + return (TRUE); + case '\033': + escaped = 1; + break; + case '[': + if (was_escaped) + csi = 1; + else + goto plain; + break; + case 'A': + if (was_csi) + goto up; + else + goto plain; + case 'B': + if (was_csi) + goto down; + else + goto plain; + case 'C': + if (was_csi) + goto right; + else + goto plain; + case 'D': + if (was_csi) + goto left; + else + goto plain; + + default: + plain: + if (db_le == db_lbuf_end) { + cnputc('\007'); + } + else if (c >= ' ' && c <= '~') { + 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 (FALSE); +} + +int +db_readline( + char * lstart, + int lsize) +{ + db_force_whitespace(); /* synch output position */ + + db_lbuf_start = lstart; + db_lbuf_end = lstart + lsize - 1; + 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(void) +{ + 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; + } +} + +#endif /* MACH_KDB */ diff --git a/ddb/db_input.h b/ddb/db_input.h new file mode 100644 index 0000000..352f035 --- /dev/null +++ b/ddb/db_input.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2007 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Barry deFreese. + */ + +#ifndef _DDB_DB_INPUT_H_ +#define _DDB_DB_INPUT_H_ + +#include + +/* Needs to be implemented by each arch. */ +extern void kdb_kintr(void); + +extern int db_readline (char *lstart, int lsize); + +extern void db_check_interrupt(void); + +#endif /* _DDB_DB_INPUT_H_ */ diff --git a/ddb/db_lex.c b/ddb/db_lex.c new file mode 100644 index 0000000..49063e1 --- /dev/null +++ b/ddb/db_lex.c @@ -0,0 +1,454 @@ +/* + * 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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 7/90 + */ + +#if MACH_KDB + +/* + * Lexical analyzer. + */ +#include +#include +#include +#include +#include +#include +#include + +char db_line[DB_LEX_LINE_SIZE]; +char db_last_line[DB_LEX_LINE_SIZE]; +char *db_lp, *db_endlp; +char *db_last_lp; +int db_look_char = 0; +db_expr_t db_look_token = 0; + +int +db_read_line(const char *repeat_last) +{ + int i; + + i = db_readline(db_line, sizeof(db_line)); + if (i == 0) + return (0); /* EOI */ + if (repeat_last) { + if (strncmp(db_line, repeat_last, strlen(repeat_last)) == 0) { + db_strcpy(db_line, db_last_line); + db_printf("%s", db_line); + i = strlen(db_line); + } else if (db_line[0] != '\n' && db_line[0] != 0) + db_strcpy(db_last_line, db_line); + } + db_lp = db_line; + db_endlp = db_lp + i; + db_last_lp = db_lp; + db_look_char = 0; + db_look_token = 0; + return (i); +} + +void +db_flush_line(void) +{ + db_lp = db_line; + db_last_lp = db_lp; + db_endlp = db_line; +} + +void +db_switch_input( + char *buffer, + int size) +{ + db_lp = buffer; + db_last_lp = db_lp; + db_endlp = buffer + size; + db_look_char = 0; + db_look_token = 0; +} + +void +db_save_lex_context(struct db_lex_context *lp) +{ + lp->l_ptr = db_lp; + lp->l_eptr = db_endlp; + lp->l_char = db_look_char; + lp->l_token = db_look_token; +} + +void +db_restore_lex_context(const struct db_lex_context *lp) +{ + db_lp = lp->l_ptr; + db_last_lp = db_lp; + db_endlp = lp->l_eptr; + db_look_char = lp->l_char; + db_look_token = lp->l_token; +} + +int +db_read_char(void) +{ + 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(int c) +{ + db_look_char = c; +} + +void +db_unread_token(int t) +{ + db_look_token = t; +} + +int +db_read_token(void) +{ + int t; + + if (db_look_token) { + t = db_look_token; + db_look_token = 0; + } + else { + db_last_lp = db_lp; + if (db_look_char) + db_last_lp--; + t = db_lex(); + } + return (t); +} + +db_expr_t db_tok_number; +char db_tok_string[TOK_STRING_SIZE]; +db_expr_t db_radix = 16; + +void +db_flush_lex(void) +{ + db_flush_line(); + db_look_char = 0; + db_look_token = 0; +} + +#define DB_DISP_SKIP 40 /* number of chars to display skip */ + +void +db_skip_to_eol(void) +{ + int skip; + int t; + int n; + char *p; + + t = db_read_token(); + p = db_last_lp; + for (skip = 0; t != tEOL && t != tSEMI_COLON && t != tEOF; skip++) + t = db_read_token(); + if (t == tSEMI_COLON) + db_unread_token(t); + if (skip != 0) { + while (p < db_last_lp && (*p == ' ' || *p == '\t')) + p++; + db_printf("Warning: Skipped input data \""); + for (n = 0; n < DB_DISP_SKIP && p < db_last_lp; n++) + db_printf("%c", *p++); + if (n >= DB_DISP_SKIP) + db_printf("...."); + db_printf("\"\n"); + } +} + +int +db_lex(void) +{ + char *cp; + int c; + + c = db_read_char(); + while (c <= ' ' || c > '~') { + if (c == '\n' || c == -1) + return (tEOL); + c = db_read_char(); + } + + cp = db_tok_string; + *cp++ = c; + + if (c >= '0' && c <= '9') { + /* number */ + int r, digit; + + 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 { + cp--; + r = db_radix; + db_unread_char(c); + } + c = db_read_char(); + *cp++ = c; + } + 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 + digit = c - 'A' + 10; + } + else + break; + db_tok_number = db_tok_number * r + digit; + c = db_read_char(); + if (cp < &db_tok_string[sizeof(db_tok_string)-1]) + *cp++ = c; + } + cp[-1] = 0; + if ((c >= '0' && c <= '9') || + (c >= 'A' && c <= 'Z') || + (c >= 'a' && c <= 'z') || + (c == '_')) + { + db_printf("Bad character '%c' after number %s\n", + c, db_tok_string); + db_error(0); + db_flush_lex(); + return (tEOF); + } + db_unread_char(c); + return (tNUMBER); + } + if ((c >= 'A' && c <= 'Z') || + (c >= 'a' && c <= 'z') || + c == '_' || c == '\\' || c == ':') + { + /* identifier */ + if (c == '\\') { + c = db_read_char(); + if (c == '\n' || c == -1) + db_error("Bad '\\' at the end of line\n"); + cp[-1] = c; + } + while (1) { + c = db_read_char(); + if ((c >= 'A' && c <= 'Z') || + (c >= 'a' && c <= 'z') || + (c >= '0' && c <= '9') || + c == '_' || c == '\\' || c == ':' || c == '.') + { + if (c == '\\') { + c = db_read_char(); + if (c == '\n' || c == -1) + db_error("Bad '\\' at the end of line\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); + } + + *cp = 0; + switch (c) { + case '+': + return (tPLUS); + case '-': + return (tMINUS); + case '.': + c = db_read_char(); + if (c == '.') { + *cp++ = c; + *cp = 0; + return (tDOTDOT); + } + db_unread_char(c); + return (tDOT); + case '*': + return (tSTAR); + case '/': + return (tSLASH); + case '=': + c = db_read_char(); + if (c == '=') { + *cp++ = c; + *cp = 0; + return(tLOG_EQ); + } + db_unread_char(c); + return (tEQ); + case '%': + return (tPCT); + case '#': + return (tHASH); + case '(': + return (tLPAREN); + case ')': + return (tRPAREN); + case ',': + return (tCOMMA); + case '\'': + return (tQUOTE); + case '"': + /* string */ + cp = db_tok_string; + c = db_read_char(); + while (c != '"' && c > 0 && c != '\n') { + if (cp >= &db_tok_string[sizeof(db_tok_string)-1]) { + db_error("Too long string\n"); + db_flush_lex(); + return (tEOF); + } + if (c == '\\') { + c = db_read_char(); + switch(c) { + case 'n': + c = '\n'; break; + case 't': + c = '\t'; break; + case '\\': + case '"': + break; + default: + db_printf("Bad escape sequence '\\%c'\n", c); + db_error(0); + db_flush_lex(); + return (tEOF); + } + } + *cp++ = c; + c = db_read_char(); + } + *cp = 0; + if (c != '"') { + db_error("Non terminated string constant\n"); + db_flush_lex(); + return (tEOF); + } + return (tSTRING); + case '$': + return (tDOLLAR); + case '!': + c = db_read_char(); + if (c == '=') { + *cp++ = c; + *cp = 0; + return(tLOG_NOT_EQ); + } + db_unread_char(c); + return (tEXCL); + case '&': + c = db_read_char(); + if (c == '&') { + *cp++ = c; + *cp = 0; + return(tLOG_AND); + } + db_unread_char(c); + return(tBIT_AND); + case '|': + c = db_read_char(); + if (c == '|') { + *cp++ = c; + *cp = 0; + return(tLOG_OR); + } + db_unread_char(c); + return(tBIT_OR); + case '<': + c = db_read_char(); + *cp++ = c; + *cp = 0; + if (c == '<') + return (tSHIFT_L); + if (c == '=') + return (tLESS_EQ); + cp[-1] = 0; + db_unread_char(c); + return(tLESS); + break; + case '>': + c = db_read_char(); + *cp++ = c; + *cp = 0; + if (c == '>') + return (tSHIFT_R); + if (c == '=') + return (tGREATER_EQ); + cp[-1] = 0; + db_unread_char(c); + return (tGREATER); + break; + case ';': + return (tSEMI_COLON); + case '?': + return (tQUESTION); + case -1: + db_strcpy(db_tok_string, ""); + return (tEOF); + } + db_printf("Bad character '%c'\n", c); + db_flush_lex(); + return (tEOF); +} + +#endif /* MACH_KDB */ diff --git a/ddb/db_lex.h b/ddb/db_lex.h new file mode 100644 index 0000000..f7677df --- /dev/null +++ b/ddb/db_lex.h @@ -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 "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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 7/90 + */ +/* + * Lexical analyzer. + */ + +#ifndef _DDB_DB_LEX_H_ +#define _DDB_DB_LEX_H_ + +#define TOK_STRING_SIZE 64 +#define DB_LEX_LINE_SIZE 256 + +struct db_lex_context { + int l_char; /* peek char */ + int l_token; /* peek token */ + char *l_ptr; /* line pointer */ + char *l_eptr; /* line end pointer */ +}; + +extern int db_lex(void); +extern int db_read_line(const char *rep_str); +extern void db_flush_line(void); +extern int db_read_char(void); +extern void db_unread_char(int c); +extern int db_read_token(void); +extern void db_unread_token(int t); +extern void db_flush_lex(void); +extern void db_switch_input(char *, int); +extern void db_save_lex_context(struct db_lex_context *); +extern void db_restore_lex_context(const struct db_lex_context *); +extern void db_skip_to_eol(void); + +extern db_expr_t db_tok_number; +extern char db_tok_string[TOK_STRING_SIZE]; +extern db_expr_t 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 tQUOTE 15 +#define tDOLLAR 16 +#define tEXCL 17 +#define tSHIFT_L 18 +#define tSHIFT_R 19 +#define tDOTDOT 20 +#define tSEMI_COLON 21 +#define tLOG_EQ 22 +#define tLOG_NOT_EQ 23 +#define tLESS 24 +#define tLESS_EQ 25 +#define tGREATER 26 +#define tGREATER_EQ 27 +#define tBIT_AND 28 +#define tBIT_OR 29 +#define tLOG_AND 30 +#define tLOG_OR 31 +#define tSTRING 32 +#define tQUESTION 33 + +#endif /* _DDB_DB_LEX_H_ */ diff --git a/ddb/db_macro.c b/ddb/db_macro.c new file mode 100644 index 0000000..63159d7 --- /dev/null +++ b/ddb/db_macro.c @@ -0,0 +1,197 @@ +/* + * 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. + */ + +#if MACH_KDB + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +/* + * debugger macro support + */ + +#define DB_MACRO_LEVEL 5 /* max macro nesting */ +#define DB_NARGS 10 /* max args */ +#define DB_NUSER_MACRO 10 /* max user macros */ + +int db_macro_free = DB_NUSER_MACRO; +struct db_user_macro { + char m_name[TOK_STRING_SIZE]; + char m_lbuf[DB_LEX_LINE_SIZE]; + int m_size; +} db_user_macro[DB_NUSER_MACRO]; + +int db_macro_level = 0; +db_expr_t db_macro_args[DB_MACRO_LEVEL][DB_NARGS]; + +static struct db_user_macro * +db_lookup_macro(const char *name) +{ + struct db_user_macro *mp; + + for (mp = db_user_macro; mp < &db_user_macro[DB_NUSER_MACRO]; mp++) { + if (mp->m_name[0] == 0) + continue; + if (strcmp(mp->m_name, name) == 0) + return(mp); + } + return(0); +} + +void +db_def_macro_cmd( + db_expr_t addr, + int have_addr, + db_expr_t count, + const char * modif) +{ + char *p; + int c; + struct db_user_macro *mp, *ep; + + if (db_read_token() != tIDENT) { + db_printf("Bad macro name \"%s\"\n", db_tok_string); + db_error(0); + /* NOTREACHED */ + } + if ((mp = db_lookup_macro(db_tok_string)) == 0) { + if (db_macro_free <= 0) + db_error("Too many macros\n"); + /* NOTREACHED */ + ep = &db_user_macro[DB_NUSER_MACRO]; + for (mp = db_user_macro; mp < ep && mp->m_name[0]; mp++); + if (mp >= ep) + db_error("ddb: internal error(macro)\n"); + /* NOTREACHED */ + db_macro_free--; + db_strcpy(mp->m_name, db_tok_string); + } + for (c = db_read_char(); c == ' ' || c == '\t'; c = db_read_char()); + for (p = mp->m_lbuf; c > 0; c = db_read_char()) + *p++ = c; + *p = 0; + mp->m_size = p - mp->m_lbuf; +} + +void +db_del_macro_cmd( + db_expr_t addr, + int have_addr, + db_expr_t count, + const char * modif) +{ + struct db_user_macro *mp; + + if (db_read_token() != tIDENT + || (mp = db_lookup_macro(db_tok_string)) == 0) { + db_printf("No such macro \"%s\"\n", db_tok_string); + db_error(0); + /* NOTREACHED */ + } else { + mp->m_name[0] = 0; + db_macro_free++; + } +} + +void +db_show_macro( + db_expr_t addr, + int have_addr, + db_expr_t count, + const char * modif) +{ + struct db_user_macro *mp; + int t; + char *name = 0; + + if ((t = db_read_token()) == tIDENT) + name = db_tok_string; + else + db_unread_token(t); + for (mp = db_user_macro; mp < &db_user_macro[DB_NUSER_MACRO]; mp++) { + if (mp->m_name[0] == 0) + continue; + if (name && strcmp(mp->m_name, name)) + continue; + db_printf("%s: %s", mp->m_name, mp->m_lbuf); + } +} + +int +db_exec_macro(const char *name) +{ + struct db_user_macro *mp; + int n; + + if ((mp = db_lookup_macro(name)) == 0) + return(-1); + if (db_macro_level+1 >= DB_MACRO_LEVEL) { + db_macro_level = 0; + db_error("Too many macro nest\n"); + /* NOTREACHED */ + } + for (n = 0; + n < DB_NARGS && + db_expression(&db_macro_args[db_macro_level+1][n]); + n++); + while (n < DB_NARGS) + db_macro_args[db_macro_level+1][n++] = 0; + db_macro_level++; + db_exec_cmd_nest(mp->m_lbuf, mp->m_size); + db_macro_level--; + return(0); +} + +void +/* ARGSUSED */ +db_arg_variable( + struct db_variable *vp, + db_expr_t *valuep, + int flag, + db_var_aux_param_t ap) +{ + if (ap->level != 1 || ap->suffix[0] < 1 || ap->suffix[0] > DB_NARGS) { + db_error("Bad $arg variable\n"); + /* NOTREACHED */ + } + if (flag == DB_VAR_GET) + *valuep = db_macro_args[db_macro_level][ap->suffix[0]-1]; + else + db_macro_args[db_macro_level][ap->suffix[0]-1] = *valuep; + return; +} + +#endif /* MACH_KDB */ diff --git a/ddb/db_macro.h b/ddb/db_macro.h new file mode 100644 index 0000000..9188247 --- /dev/null +++ b/ddb/db_macro.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2007 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Barry deFreese. + */ + +#ifndef _DDB_DB_MACRO_H_ +#define _DDB_DB_MACRO_H_ + +#include +#include + +extern void db_def_macro_cmd ( + db_expr_t addr, + boolean_t have_addr, + db_expr_t count, + const char * modif); + +extern void db_del_macro_cmd ( + db_expr_t addr, + boolean_t have_addr, + db_expr_t count, + const char * modif); + +extern void db_show_macro ( + db_expr_t addr, + boolean_t have_addr, + db_expr_t count, + const char * modif); + +extern int db_exec_macro (const char *name); + +extern void db_arg_variable ( + struct db_variable *vp, + db_expr_t *valuep, + int flag, + db_var_aux_param_t ap); + +#endif /* _DDB_DB_MACRO_H_ */ diff --git a/ddb/db_mp.c b/ddb/db_mp.c new file mode 100644 index 0000000..5cf800c --- /dev/null +++ b/ddb/db_mp.c @@ -0,0 +1,339 @@ +/* + * Mach Operating System + * Copyright (c) 1993,1992 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. + */ + +#if MACH_KDB + +#if NCPUS > 1 + +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +/* + * Routines to interlock access to the kernel debugger on + * multiprocessors. + */ + +int db_spl; +def_simple_lock_irq_data(static,db_lock) /* lock to enter debugger */ +volatile int db_cpu = -1; /* CPU currently in debugger */ + /* -1 if none */ +int db_active[NCPUS] = { 0 }; /* count recursive entries + into debugger */ +int db_slave[NCPUS] = { 0 }; /* nonzero if cpu interrupted + by another cpu in debugger */ + +boolean_t db_enter_debug = FALSE; + +/* + * Called when entering kernel debugger. + * Takes db lock. If we were called remotely (slave state) we just + * wait for db_cpu to be equal to cpu_number(). Otherwise enter debugger + * if not active on another cpu + */ + +boolean_t +db_enter(void) +{ + int mycpu = cpu_number(); + + /* + * Count recursive entries to debugger. + */ + db_active[mycpu]++; + + /* + * Wait for other CPUS to leave debugger. + */ + db_spl = lock_db(); + + if (db_enter_debug) + db_printf( + "db_enter: cpu %d[%d], master %d, db_cpu %d, run mode %d\n", + mycpu, db_slave[mycpu], master_cpu, db_cpu, db_run_mode); + + /* + * If no CPU in debugger, and I am not being stopped, + * enter the debugger. + */ + if (db_cpu == -1 && !db_slave[mycpu]) { + remote_db(); /* stop other cpus */ + db_cpu = mycpu; + return TRUE; + } + /* + * If I am already in the debugger (recursive entry + * or returning from single step), enter debugger. + */ + else if (db_cpu == mycpu) + return TRUE; + /* + * Otherwise, cannot enter debugger. + */ + else + return FALSE; +} + +/* + * Leave debugger. + */ +void +db_leave(void) +{ + int mycpu = cpu_number(); + + /* + * If continuing, give up debugger + */ + if (db_run_mode == STEP_CONTINUE) + db_cpu = -1; + + /* + * If I am a slave, drop my slave count. + */ + if (db_slave[mycpu]) + db_slave[mycpu]--; + if (db_enter_debug) + db_printf("db_leave: cpu %d[%d], db_cpu %d, run_mode %d\n", + mycpu, db_slave[mycpu], db_cpu, db_run_mode); + /* + * Unlock debugger. + */ + unlock_db(db_spl); + + /* + * Drop recursive entry count. + */ + db_active[mycpu]--; +} + + +/* + * invoke kernel debugger on slave processors + */ + +void +remote_db(void) { + int my_cpu = cpu_number(); + int i; + + for (i = 0; i < NCPUS; i++) { + if (i != my_cpu && + machine_slot[i].is_cpu && + machine_slot[i].running) + { + cpu_interrupt_to_db(i); + } + } +} + +/* + * Save and restore DB global registers. + * + * DB_SAVE_CTXT must be at the start of a block, and + * DB_RESTORE_CTXT must be in the same block. + */ + +#ifdef __STDC__ +#define DB_SAVE(type, name) extern type name; type name##_save = name +#define DB_RESTORE(name) name = name##_save +#else /* __STDC__ */ +#define DB_SAVE(type, name) extern type name; type name/**/_save = name +#define DB_RESTORE(name) name = name/**/_save +#endif /* __STDC__ */ + +#define DB_SAVE_CTXT() \ + DB_SAVE(int, db_run_mode); \ + DB_SAVE(boolean_t, db_sstep_print); \ + DB_SAVE(int, db_loop_count); \ + DB_SAVE(int, db_call_depth); \ + DB_SAVE(int, db_inst_count); \ + DB_SAVE(int, db_last_inst_count); \ + DB_SAVE(int, db_load_count); \ + DB_SAVE(int, db_store_count); \ + DB_SAVE(boolean_t, db_cmd_loop_done); \ + DB_SAVE(jmp_buf_t *, db_recover); \ + DB_SAVE(db_addr_t, db_dot); \ + DB_SAVE(db_addr_t, db_last_addr); \ + DB_SAVE(db_addr_t, db_prev); \ + DB_SAVE(db_addr_t, db_next); \ + SAVE_DDB_REGS + +#define DB_RESTORE_CTXT() \ + DB_RESTORE(db_run_mode); \ + DB_RESTORE(db_sstep_print); \ + DB_RESTORE(db_loop_count); \ + DB_RESTORE(db_call_depth); \ + DB_RESTORE(db_inst_count); \ + DB_RESTORE(db_last_inst_count); \ + DB_RESTORE(db_load_count); \ + DB_RESTORE(db_store_count); \ + DB_RESTORE(db_cmd_loop_done); \ + DB_RESTORE(db_recover); \ + DB_RESTORE(db_dot); \ + DB_RESTORE(db_last_addr); \ + DB_RESTORE(db_prev); \ + DB_RESTORE(db_next); \ + RESTORE_DDB_REGS + +/* + * switch to another cpu + */ +void +db_on(int cpu) +{ + /* + * Save ddb global variables + */ + DB_SAVE_CTXT(); + + /* + * Don`t do if bad CPU number. + * CPU must also be spinning in db_entry. + */ + if (cpu < 0 || cpu >= NCPUS || !db_active[cpu]) + return; + + /* + * Give debugger to that CPU + */ + db_cpu = cpu; + unlock_db(db_spl); + + /* + * Wait for it to come back again + */ + db_spl = lock_db(); + + /* + * Restore ddb globals + */ + DB_RESTORE_CTXT(); + + if (db_cpu == -1) /* someone continued */ + db_continue_cmd(0, 0, 0, ""); +} + +/* + * Called by interprocessor interrupt when one CPU is + * in kernel debugger and wants to stop other CPUs + */ +void +remote_db_enter(void) +{ + db_slave[cpu_number()]++; + kdb_kintr(); +} + +/* + * Acquire kernel debugger. + * Conditional code for forwarding characters from slave to console + * if console on master only. + */ + +/* + * As long as db_cpu is not -1 or cpu_number(), we know that debugger + * is active on another cpu. + */ +int +lock_db(void) +{ + int my_cpu = cpu_number(); + int s; + + for (;;) { +#if CONSOLE_ON_MASTER + if (my_cpu == master_cpu) { + db_console(); + } +#endif /* CONSOLE_ON_MASTER */ + if (db_cpu != -1 && db_cpu != my_cpu) + continue; + +#if CONSOLE_ON_MASTER + if (my_cpu == master_cpu) { + if (!(s = simple_lock_try_irq(&db_lock))) + continue; + } + else { + s = simple_lock_irq(&db_lock); + } +#else /* CONSOLE_ON_MASTER */ + s = simple_lock_irq(&db_lock); +#endif /* CONSOLE_ON_MASTER */ + if (db_cpu == -1 || db_cpu == my_cpu) + break; + unlock_db(s); + } + + return s; +} + +void +unlock_db(int s) +{ + simple_unlock_irq(s, &db_lock); +} + +#if CONSOLE_ON_MASTER +void +db_console(void) +{ + if (i_bit(CBUS_PUT_CHAR, my_word)) { + volatile u_char c = cbus_ochar; + i_bit_clear(CBUS_PUT_CHAR, my_word); + cnputc(c); + } else if (i_bit(CBUS_GET_CHAR, my_word)) { + if (cbus_wait_char) + cbus_ichar = cngetc(); + else + cbus_ichar = cnmaygetc(); + i_bit_clear(CBUS_GET_CHAR, my_word); +#ifndef notdef + } else if (!cnmaygetc()) { +#else /* notdef */ + } else if (com_is_char() && !com_getc(TRUE)) { +#endif /* notdef */ + simple_unlock(&db_lock); + db_cpu = my_cpu; + } +} +#endif /* CONSOLE_ON_MASTER */ + +#endif /* NCPUS > 1 */ + +#endif /* MACH_KDB */ diff --git a/ddb/db_mp.h b/ddb/db_mp.h new file mode 100644 index 0000000..8a0a9e1 --- /dev/null +++ b/ddb/db_mp.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2013 Free Software Foundation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _DDB_DB_MP_H_ +#define _DDB_DB_MP_H_ + +void remote_db(void); +int lock_db(void); +void unlock_db(int); +void db_on(int i); + +#if CONSOLE_ON_MASTER +void db_console(void); +#endif /* CONSOLE_ON_MASTER */ + +boolean_t db_enter(void); +void remote_db_enter(void); +void db_leave(void); + +#endif /* _DDB_DB_MP_H_ */ diff --git a/ddb/db_output.c b/ddb/db_output.c new file mode 100644 index 0000000..9a76f54 --- /dev/null +++ b/ddb/db_output.c @@ -0,0 +1,217 @@ +/* + * 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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 7/90 + */ + +#if MACH_KDB + +/* + * Printf and character output for debugger. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * 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. + */ + +#ifndef DB_MAX_LINE +#define DB_MAX_LINE 24 /* maximum line */ +#define DB_MAX_WIDTH 80 /* maximum width */ +#endif /* DB_MAX_LINE */ + +#define DB_MIN_MAX_WIDTH 20 /* minimum max width */ +#define DB_MIN_MAX_LINE 3 /* minimum max line */ +#define CTRL(c) ((c) & 0xff) + +int db_output_position = 0; /* output column */ +int db_output_line = 0; /* output line number */ +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_line = DB_MAX_LINE; /* output max lines */ +int db_max_width = DB_MAX_WIDTH; /* output line width */ + +/* + * Force pending whitespace. + */ +void +db_force_whitespace(void) +{ + 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) { + cnputc('\t'); + last_print = next_tab; + } + else { + cnputc(' '); + last_print++; + } + } + db_last_non_space = db_output_position; +} + +static void +db_more(void) +{ + char *p; + boolean_t quit_output = FALSE; + + for (p = "--db_more--"; *p; p++) + cnputc(*p); + switch(cngetc()) { + case ' ': + db_output_line = 0; + break; + case 'q': + case CTRL('c'): + db_output_line = 0; + quit_output = TRUE; + break; + default: + db_output_line--; + break; + } + p = "\b\b\b\b\b\b\b\b\b\b\b \b\b\b\b\b\b\b\b\b\b\b"; + while (*p) + cnputc(*p++); + if (quit_output) { + db_error(0); + /* NOTREACHED */ + } +} + +/* + * Output character. Buffer whitespace. + */ +void +db_putchar(int c) /* character to output */ +{ + if (db_max_line >= DB_MIN_MAX_LINE && db_output_line >= db_max_line-1) + db_more(); + 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++; + if (db_max_width >= DB_MIN_MAX_WIDTH + && db_output_position >= db_max_width) { + /* auto new line */ + cnputc('\n'); + db_output_position = 0; + db_last_non_space = 0; + db_output_line++; + } + db_last_non_space = db_output_position; + } + else if (c == '\n') { + /* Return */ + cnputc(c); + db_output_position = 0; + db_last_non_space = 0; + db_output_line++; + 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 */ +} + +static void +db_id_putc(char c, vm_offset_t dummy) +{ + db_putchar(c); +} + +/* + * Return output position + */ +int __attribute__ ((pure)) +db_print_position(void) +{ + return (db_output_position); +} + +/* + * End line if too long. + */ +void db_end_line(void) +{ + if (db_output_position >= db_max_width-1) + db_printf("\n"); +} + +/*VARARGS1*/ +int +db_printf(const char *fmt, ...) +{ + va_list listp; + + va_start(listp, fmt); + _doprnt(fmt, listp, db_id_putc, db_radix, 0); + va_end(listp); + return 0; +} + +#endif /* MACH_KDB */ diff --git a/ddb/db_output.h b/ddb/db_output.h new file mode 100644 index 0000000..7920179 --- /dev/null +++ b/ddb/db_output.h @@ -0,0 +1,46 @@ +/* + * 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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 8/90 + */ + +/* + * Printing routines for kernel debugger. + */ + +#ifndef _DDB_DB_OUTPUT_H_ +#define _DDB_DB_OUTPUT_H_ + +extern void db_force_whitespace(void); +extern int db_print_position(void) __attribute__ ((pure)); +extern void db_end_line(void); +extern int db_printf(const char *fmt, ...); +/* alternate name */ +#define kdbprintf db_printf +extern void db_putchar(int c); + +#endif /* _DDB_DB_OUTPUT_H_ */ diff --git a/ddb/db_print.c b/ddb/db_print.c new file mode 100644 index 0000000..f08dd6c --- /dev/null +++ b/ddb/db_print.c @@ -0,0 +1,573 @@ +/* + * 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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 7/90 + */ + +#if MACH_KDB + +/* + * Miscellaneous printing. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +extern unsigned long db_maxoff; + +/* ARGSUSED */ +void +db_show_regs( + db_expr_t addr, + boolean_t have_addr, + db_expr_t count, + char *modif) +{ + struct db_variable *regp; + db_expr_t value; + db_addr_t offset; + char * name; + int i; + struct db_var_aux_param aux_param; + task_t task = TASK_NULL; + + aux_param.modif = modif; + aux_param.thread = THREAD_NULL; + if (db_option(modif, 't')) { + if (have_addr) { + if (!db_check_thread_address_valid((thread_t)addr)) + return; + aux_param.thread = (thread_t)addr; + } else + aux_param.thread = db_default_thread; + if (aux_param.thread != THREAD_NULL) + task = aux_param.thread->task; + } + for (regp = db_regs; regp < db_eregs; regp++) { + if (regp->max_level > 1) { + db_printf("bad multi-suffixed register %s\n", regp->name); + continue; + } + aux_param.level = regp->max_level; + for (i = regp->low; i <= regp->high; i++) { + aux_param.suffix[0] = i; + db_read_write_variable(regp, &value, DB_VAR_GET, &aux_param); + if (regp->max_level > 0) + db_printf("%s%d%*s", regp->name, i, + 12-strlen(regp->name)-((i<10)?1:2), ""); + else + db_printf("%-12s", regp->name); + db_printf("%#*N", 2+2*sizeof(vm_offset_t), value); + db_find_xtrn_task_sym_and_offset((db_addr_t)value, &name, + &offset, task); + if (name != 0 && offset <= db_maxoff && offset != value) { + db_printf("\t%s", name); + if (offset != 0) + db_printf("+%#r", offset); + } + db_printf("\n"); + } + } +} + +#define OPTION_LONG 0x001 /* long print option */ +#define OPTION_USER 0x002 /* print ps-like stuff */ +#define OPTION_SCHED 0x004 /* print scheduling info */ +#define OPTION_INDENT 0x100 /* print with indent */ +#define OPTION_THREAD_TITLE 0x200 /* print thread title */ +#define OPTION_TASK_TITLE 0x400 /* print thread title */ + +#ifndef DB_TASK_NAME +#define DB_TASK_NAME(task) /* no task name */ +#define DB_TASK_NAME_TITLE "" /* no task name */ +#endif /* DB_TASK_NAME */ + +#ifndef db_thread_fp_used +#define db_thread_fp_used(thread) FALSE +#endif + +static char * +db_thread_stat( + const thread_t thread, + char *status) +{ + char *p = status; + + *p++ = (thread->state & TH_RUN) ? 'R' : '.'; + *p++ = (thread->state & TH_WAIT) ? 'W' : '.'; + *p++ = (thread->state & TH_SUSP) ? 'S' : '.'; + *p++ = (thread->state & TH_SWAPPED) ? 'O' : '.'; + *p++ = (thread->state & TH_UNINT) ? 'N' : '.'; + /* show if the FPU has been used */ + *p++ = db_thread_fp_used(thread) ? 'F' : '.'; + *p++ = 0; + return(status); +} + +void +db_print_thread( + thread_t thread, + int thread_id, + int flag) +{ + if (flag & OPTION_USER) { + char status[8]; + char *indent = ""; + if (flag & OPTION_INDENT) + indent = " "; + + if (flag & OPTION_LONG) { + if (flag & OPTION_THREAD_TITLE) { + db_printf("%s ID: THREAD STAT STACK PCB", indent); + db_printf(" SUS PRI CONTINUE,WAIT_FUNC\n"); + } + db_printf("%s%3d%c %0*X %s %0*X %0*X %3d %3d ", + indent, thread_id, + (thread == current_thread())? '#': ':', + 2*sizeof(vm_offset_t), thread, + db_thread_stat(thread, status), + 2*sizeof(vm_offset_t), thread->kernel_stack, + 2*sizeof(vm_offset_t), thread->pcb, + thread->suspend_count, thread->sched_pri); + if ((thread->state & TH_SWAPPED) && thread->swap_func) { + db_task_printsym((db_addr_t)thread->swap_func, + DB_STGY_ANY, kernel_task); + db_printf(", "); + } + if (thread->state & TH_WAIT) + db_task_printsym((db_addr_t)thread->wait_event, + DB_STGY_ANY, kernel_task); + db_printf("\n"); + } else if (flag & OPTION_SCHED) { + if (flag & OPTION_THREAD_TITLE) { + db_printf("%s " + "STAT PRIORITY POLICY USAGE LAST\n", + indent); + db_printf("%s ID: " + "RWSONF SET MAX COMP DEPR P DATA CPU SCHED UPDATED\n", + indent); + db_printf(" \n"); + } + db_printf("%s%3d%c %s %4d %4d %4d %4d %c %4d %10d %10d %10d\n", + indent, thread_id, + (thread == current_thread())? '#': ':', + db_thread_stat(thread, status), + thread->priority, + thread->max_priority, + thread->sched_pri, + thread->depress_priority, +#if MACH_FIXPRI + thread->policy == POLICY_TIMESHARE ? 'T' : 'F', + thread->sched_data, +#else /* MACH_FIXPRI */ + 'T', 0, +#endif /* MACH_FIXPRI */ + thread->cpu_usage, + thread->sched_usage, + thread->sched_stamp); + } else { + if (thread_id % 3 == 0) { + if (flag & OPTION_INDENT) + db_printf("\n "); + } else + db_printf(" "); + db_printf("%3d%c(%0*X,%s)", thread_id, + (thread == current_thread())? '#': ':', + 2*sizeof(vm_offset_t), thread, + db_thread_stat(thread, status)); + } + } else { + if (flag & OPTION_INDENT) + db_printf(" %3d ", thread_id); + if (thread->name[0] && + strncmp (thread->name, thread->task->name, THREAD_NAME_SIZE)) + db_printf("%s ", thread->name); + db_printf("(%0*X) ", 2*sizeof(vm_offset_t), thread); + char status[8]; + db_printf("%s", db_thread_stat(thread, status)); + if (thread->state & TH_SWAPPED) { + if (thread->swap_func) { + db_printf("("); + db_task_printsym((db_addr_t)thread->swap_func, + DB_STGY_ANY, kernel_task); + db_printf(")"); + } else { + db_printf("(swapped)"); + } + } + if (thread->state & TH_WAIT) { + db_printf(" "); + db_task_printsym((db_addr_t)thread->wait_event, + DB_STGY_ANY, kernel_task); + } + db_printf("\n"); + } +} + +static void +db_print_task( + task_t task, + int task_id, + int flag) +{ + thread_t thread; + int thread_id; + + if (flag & OPTION_USER) { + if (flag & OPTION_TASK_TITLE) { + db_printf(" ID: TASK MAP THD SUS PR %s", + DB_TASK_NAME_TITLE); + if ((flag & (OPTION_LONG|OPTION_SCHED)) == 0) + db_printf(" THREADS"); + db_printf("\n"); + } + db_printf("%3d: %0*X %0*X %3d %3d %2d ", + task_id, 2*sizeof(vm_offset_t), task, + 2*sizeof(vm_offset_t), task->map, task->thread_count, + task->suspend_count, task->priority); + DB_TASK_NAME(task); + if (flag & (OPTION_LONG|OPTION_SCHED)) { + if (flag & OPTION_TASK_TITLE) + flag |= OPTION_THREAD_TITLE; + db_printf("\n"); + } else if (task->thread_count <= 1) + flag &= ~OPTION_INDENT; + thread_id = 0; + queue_iterate(&task->thread_list, thread, thread_t, thread_list) { + db_print_thread(thread, thread_id, flag); + flag &= ~OPTION_THREAD_TITLE; + thread_id++; + } + if ((flag & (OPTION_LONG|OPTION_SCHED)) == 0) + db_printf("\n"); + } else { + if (flag & OPTION_TASK_TITLE) + db_printf(" TASK THREADS\n"); + if (task->name[0]) + db_printf("%3d %s (%0*X): ", task_id, task->name, + 2*sizeof(vm_offset_t), task); + else + db_printf("%3d (%0*X): ", task_id, + 2*sizeof(vm_offset_t), task); + if (task->thread_count == 0) { + db_printf("no threads\n"); + } else { + if (task->thread_count > 1) { + db_printf("%d threads: \n", task->thread_count); + flag |= OPTION_INDENT; + } else + flag &= ~OPTION_INDENT; + thread_id = 0; + queue_iterate(&task->thread_list, thread, + thread_t, thread_list) + db_print_thread(thread, thread_id++, flag); + } + } +} + +void +db_show_all_tasks(db_expr_t addr, + boolean_t have_addr, + db_expr_t count, + const char *modif) +{ + task_t task; + int task_id = 0; + processor_set_t pset; + + db_printf(" ID %-*s NAME [THREADS]\n", 2*sizeof(vm_offset_t), "TASK"); + + queue_iterate(&all_psets, pset, processor_set_t, all_psets) + queue_iterate(&pset->tasks, task, task_t, pset_tasks) { + db_printf("%3d %0*X %s [%d]\n", + task_id, + 2*sizeof(vm_offset_t), + task, + task->name, + task->thread_count); + task_id++; + } +} + +static void showrq(run_queue_t rq) +{ + db_printf("count(%d) low(%d)\n", rq->count, rq->low); +} + +/*ARGSUSED*/ +void +db_show_all_runqs( + db_expr_t addr, + boolean_t have_addr, + db_expr_t count, + const char * modif) +{ + int i = 0; + processor_set_t pset; + + queue_iterate(&all_psets, pset, processor_set_t, all_psets) { + db_printf("Processor set #%d runq:\t", i); + showrq(&pset->runq); + i++; + } + for (i = 0; i < smp_get_numcpus(); i++) { + db_printf("Processor #%d runq:\t", i); + showrq(&cpu_to_processor(i)->runq); + } + db_printf("Stuck threads:\t%d", stuck_count); +} + +/*ARGSUSED*/ +void +db_show_all_threads( + db_expr_t addr, + boolean_t have_addr, + db_expr_t count, + const char * modif) +{ + task_t task; + int task_id; + int flag; + processor_set_t pset; + + flag = OPTION_TASK_TITLE|OPTION_INDENT; + if (db_option(modif, 'u')) + flag |= OPTION_USER; + if (db_option(modif, 'l')) + flag |= OPTION_LONG; + if (db_option(modif, 's')) + flag |= OPTION_SCHED; + + task_id = 0; + queue_iterate(&all_psets, pset, processor_set_t, all_psets) { + queue_iterate(&pset->tasks, task, task_t, pset_tasks) { + db_print_task(task, task_id, flag); + flag &= ~OPTION_TASK_TITLE; + task_id++; + } + } +} + +db_addr_t +db_task_from_space( + ipc_space_t space, + int *task_id) +{ + task_t task; + int tid = 0; + processor_set_t pset; + + queue_iterate(&all_psets, pset, processor_set_t, all_psets) { + queue_iterate(&pset->tasks, task, task_t, pset_tasks) { + if (task->itk_space == space) { + *task_id = tid; + return (db_addr_t)task; + } + tid++; + } + } + *task_id = 0; + return (0); +} + +/*ARGSUSED*/ +void +db_show_one_thread( + db_expr_t addr, + boolean_t have_addr, + db_expr_t count, + const char * modif) +{ + int flag; + int thread_id; + thread_t thread; + + flag = OPTION_THREAD_TITLE; + if (db_option(modif, 'u')) + flag |= OPTION_USER; + if (db_option(modif, 'l')) + flag |= OPTION_LONG; + if (db_option(modif, 's')) + flag |= OPTION_SCHED; + + if (!have_addr) { + thread = current_thread(); + if (thread == THREAD_NULL) { + db_error("No thread\n"); + /*NOTREACHED*/ + } + } else + thread = (thread_t) addr; + + if ((thread_id = db_lookup_thread(thread)) < 0) { + db_printf("bad thread address %#X\n", addr); + db_error(0); + /*NOTREACHED*/ + } + + if (flag & OPTION_USER) { + db_printf("TASK%d(%0*X):\n", + db_lookup_task(thread->task), + 2*sizeof(vm_offset_t), thread->task); + db_print_thread(thread, thread_id, flag); + } else { + db_printf("task %d(%0*X): thread %d", + db_lookup_task(thread->task), + 2*sizeof(vm_offset_t), thread->task, thread_id); + db_print_thread(thread, thread_id, flag); + } +} + +/*ARGSUSED*/ +void +db_show_one_task( + db_expr_t addr, + boolean_t have_addr, + db_expr_t count, + const char * modif) +{ + int flag; + int task_id; + task_t task; + + flag = OPTION_TASK_TITLE; + if (db_option(modif, 'u')) + flag |= OPTION_USER; + if (db_option(modif, 'l')) + flag |= OPTION_LONG; + + if (!have_addr) { + task = db_current_task(); + if (task == TASK_NULL) { + db_error("No task\n"); + /*NOTREACHED*/ + } + } else + task = (task_t) addr; + + if ((task_id = db_lookup_task(task)) < 0) { + db_printf("bad task address %#X\n", addr); + db_error(0); + /*NOTREACHED*/ + } + + db_print_task(task, task_id, flag); +} + +static int +db_port_iterate(const thread_t thread, void (*func)(int, const ipc_port_t, unsigned, int)) +{ + ipc_entry_t entry; + int n = 0; + struct rdxtree_iter iter; + rdxtree_for_each(&thread->task->itk_space->is_map, &iter, entry) { + if (entry->ie_bits & MACH_PORT_TYPE_PORT_RIGHTS) + (*func)(entry->ie_name, (ipc_port_t) entry->ie_object, + entry->ie_bits, n++); + } + return(n); +} + +static void +db_print_port_id(int id, const ipc_port_t port, unsigned bits, int n) +{ + if (n != 0 && n % 3 == 0) + db_printf("\n"); + db_printf("\tport%d(%s,%x)", id, + (bits & MACH_PORT_TYPE_RECEIVE)? "r": + (bits & MACH_PORT_TYPE_SEND)? "s": "S", port); +} + +static void +db_print_port_id_long( + int id, + const ipc_port_t port, + unsigned bits, + int n) +{ + if (n != 0) + db_printf("\n"); + db_printf("\tport%d(%s, port=0x%x", id, + (bits & MACH_PORT_TYPE_RECEIVE)? "r": + (bits & MACH_PORT_TYPE_SEND)? "s": "S", port); + db_printf(", receiver_name=0x%x)", port->ip_receiver_name); +} + +/* ARGSUSED */ +void +db_show_port_id( + db_expr_t addr, + boolean_t have_addr, + db_expr_t count, + const char * modif) +{ + thread_t thread; + + if (!have_addr) { + thread = current_thread(); + if (thread == THREAD_NULL) { + db_error("No thread\n"); + /*NOTREACHED*/ + } + } else + thread = (thread_t) addr; + if (db_lookup_thread(thread) < 0) { + db_printf("Bad thread address %#X\n", addr); + db_error(0); + /*NOTREACHED*/ + } + if (db_option(modif, 'l')) + { + if (db_port_iterate(thread, db_print_port_id_long)) + db_printf("\n"); + return; + } + if (db_port_iterate(thread, db_print_port_id)) + db_printf("\n"); +} + +#endif /* MACH_KDB */ diff --git a/ddb/db_print.h b/ddb/db_print.h new file mode 100644 index 0000000..b86c696 --- /dev/null +++ b/ddb/db_print.h @@ -0,0 +1,68 @@ +/* + * (c) Copyright 1992, 1993, 1994, 1995 OPEN SOFTWARE FOUNDATION, INC. + * ALL RIGHTS RESERVED + */ +/* + * OSF RI nmk19b2 5/2/95 + */ + +#ifndef _DDB_DB_PRINT_H_ +#define _DDB_DB_PRINT_H_ + +#include +#include + +/* Prototypes for functions exported by this module. + */ +void db_show_regs( + db_expr_t addr, + boolean_t have_addr, + db_expr_t count, + char *modif); + +void db_show_one_task( + db_expr_t addr, + boolean_t have_addr, + db_expr_t count, + const char * modif); + +void db_show_port_id( + db_expr_t addr, + boolean_t have_addr, + db_expr_t count, + const char * modif); + +void db_show_one_thread( + db_expr_t addr, + int have_addr, + db_expr_t count, + const char * modif); + +void db_show_all_tasks( + db_expr_t addr, + int have_addr, + db_expr_t count, + const char * modif); + +void db_show_all_threads( + db_expr_t addr, + int have_addr, + db_expr_t count, + const char * modif); + +void db_show_all_runqs( + db_expr_t addr, + int have_addr, + db_expr_t count, + const char * modif); + +db_addr_t db_task_from_space( + ipc_space_t space, + int *task_id); + +void db_print_thread( + thread_t thread, + int thread_id, + int flag); + +#endif /* !_DDB_DB_PRINT_H_ */ diff --git a/ddb/db_run.c b/ddb/db_run.c new file mode 100644 index 0000000..0c8c12f --- /dev/null +++ b/ddb/db_run.c @@ -0,0 +1,430 @@ +/* + * Mach Operating System + * Copyright (c) 1993-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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 7/90 + */ + +#if MACH_KDB + +/* + * Commands to run process. + */ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +int db_run_mode; + +boolean_t db_sstep_print; +int db_loop_count; +int db_call_depth; + +int db_inst_count; +int db_last_inst_count; +int db_load_count; +int db_store_count; + +boolean_t +db_stop_at_pc( + boolean_t *is_breakpoint, + task_t task) +{ + db_addr_t pc; + db_thread_breakpoint_t bkpt; + + db_clear_task_single_step(DDB_REGS, task); + 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 /* FIXUP_PC_AFTER_BREAK */ + + /* + * Now check for a breakpoint at this address. + */ + bkpt = db_find_thread_breakpoint_here(task, pc); + if (bkpt) { + if (db_cond_check(bkpt)) { + *is_breakpoint = TRUE; + return (TRUE); /* stop here */ + } + } + *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_print_loc_and_inst(pc, task); + } + return (FALSE); /* continue */ + } + } + if (db_run_mode == STEP_RETURN) { + /* WARNING: the following assumes an instruction fits an int */ + db_expr_t ins = db_get_task_value(pc, sizeof(int), FALSE, task); + + /* 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)) { + int i; + + db_printf("[after %6d /%4d] ", + db_inst_count, + db_inst_count - db_last_inst_count); + db_last_inst_count = db_inst_count; + for (i = db_call_depth; --i > 0; ) + db_printf(" "); + db_print_loc_and_inst(pc, task); + db_printf("\n"); + } + } + if (inst_call(ins)) + db_call_depth++; + return (FALSE); /* continue */ + } + } + if (db_run_mode == STEP_CALLT) { + /* WARNING: the following assumes an instruction fits an int */ + db_expr_t ins = db_get_task_value(pc, sizeof(int), FALSE, task); + + /* continue until call or return */ + + if (!inst_call(ins) && + !inst_return(ins) && + !inst_trap_return(ins)) { + return (FALSE); /* continue */ + } + } + if (db_find_breakpoint_here(task, pc)) + return(FALSE); + db_run_mode = STEP_NONE; + return (TRUE); +} + +void +db_restart_at_pc( + boolean_t watchpt, + task_t task) +{ + db_addr_t pc = PC_REGS(DDB_REGS); + + if ((db_run_mode == STEP_COUNT) || + (db_run_mode == STEP_RETURN) || + (db_run_mode == STEP_CALLT)) { + + /* + * We are about to execute this instruction, + * so count it now. + */ + + db_get_task_value(pc, sizeof(int), FALSE, task); + db_inst_count++; + db_load_count += inst_load(ins); + db_store_count += inst_store(ins); +#ifdef SOFTWARE_SSTEP + db_addr_t brpc; + /* Account for instructions in delay slots */ + brpc = next_instr_address(pc, 1, task); + if ((brpc != pc) && (inst_branch(ins) || inst_call(ins))) { + /* Note: this ~assumes an instruction <= sizeof(int) */ + db_get_task_value(brpc, sizeof(int), FALSE, task); + 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(task, pc)) { + /* + * Step over breakpoint/watchpoint. + */ + db_run_mode = STEP_INVISIBLE; + db_set_task_single_step(DDB_REGS, task); + } else { + db_set_breakpoints(); + db_set_watchpoints(); + } + } else { + db_set_task_single_step(DDB_REGS, task); + } +} + +void +db_single_step( + db_regs_t *regs, + task_t task) +{ + if (db_run_mode == STEP_CONTINUE) { + db_run_mode = STEP_INVISIBLE; + db_set_task_single_step(regs, task); + } +} + +#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,task) 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; + +db_breakpoint_t __attribute__ ((pure)) +db_find_temp_breakpoint(const task_t task, db_addr_t addr) +{ + if (db_taken_bkpt && (db_taken_bkpt->address == addr) && + db_taken_bkpt->task == task) + return db_taken_bkpt; + if (db_not_taken_bkpt && (db_not_taken_bkpt->address == addr) && + db_not_taken_bkpt->task == task) + return db_not_taken_bkpt; + return 0; +} + +void +db_set_task_single_step( + db_regs_t *regs, + task_t task) +{ + db_addr_t pc = PC_REGS(regs), brpc; + unsigned int inst; + boolean_t unconditional; + + /* + * User was stopped at pc, e.g. the instruction + * at pc was not executed. + */ + inst = db_get_task_value(pc, sizeof(int), FALSE, task); + if (inst_branch(inst) || inst_call(inst)) { + extern db_expr_t getreg_val(); + + brpc = branch_taken(inst, pc, getreg_val, regs); + if (brpc != pc) { /* self-branches are hopeless */ + db_taken_bkpt = db_set_temp_breakpoint(task, brpc); + } else + db_taken_bkpt = 0; + pc = next_instr_address(pc,1,task); + } + + /* check if this control flow instruction is an unconditional transfer */ + unconditional = inst_unconditional_flow_transfer(inst); + + pc = next_instr_address(pc,0,task); + /* + We only set the sequential breakpoint if previous instruction was not + an unconditional change of flow of control. If the previous instruction + is an unconditional change of flow of control, setting a breakpoint in the + next sequential location may set a breakpoint in data or in another routine, + which could screw up either the program or the debugger. + (Consider, for instance, that the next sequential instruction is the + start of a routine needed by the debugger.) + */ + if (!unconditional && db_find_breakpoint_here(task, pc) == 0) { + db_not_taken_bkpt = db_set_temp_breakpoint(task, pc); + } + else + db_not_taken_bkpt = 0; +} + +void +db_clear_task_single_step(const db_regs_t *regs, task_t task) +{ + if (db_taken_bkpt != 0) { + db_delete_temp_breakpoint(task, db_taken_bkpt); + db_taken_bkpt = 0; + } + if (db_not_taken_bkpt != 0) { + db_delete_temp_breakpoint(task, 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( + db_expr_t addr, + int have_addr, + db_expr_t count, + const 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_last_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( + db_expr_t addr, + int have_addr, + db_expr_t count, + const 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_last_inst_count = 0; + db_load_count = 0; + db_store_count = 0; + + db_cmd_loop_done = 1; +} + +/*ARGSUSED*/ +void +db_trace_until_matching_cmd( + db_expr_t addr, + int have_addr, + db_expr_t count, + const 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_last_inst_count = 0; + db_load_count = 0; + db_store_count = 0; + + db_cmd_loop_done = 1; +} + +/* continue */ +/*ARGSUSED*/ +void +db_continue_cmd( + db_expr_t addr, + int have_addr, + db_expr_t count, + const char * modif) +{ + if (modif[0] == 'c') + db_run_mode = STEP_COUNT; + else + db_run_mode = STEP_CONTINUE; + db_inst_count = 0; + db_last_inst_count = 0; + db_load_count = 0; + db_store_count = 0; + + db_cmd_loop_done = 1; +} + +boolean_t +db_in_single_step(void) +{ + return(db_run_mode != STEP_NONE && db_run_mode != STEP_CONTINUE); +} + +#endif /* MACH_KDB */ diff --git a/ddb/db_run.h b/ddb/db_run.h new file mode 100644 index 0000000..c042d4c --- /dev/null +++ b/ddb/db_run.h @@ -0,0 +1,94 @@ +/* + * Mach Operating System + * Copyright (c) 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. + */ + +#ifndef _DDB_DB_RUN_H_ +#define _DDB_DB_RUN_H_ + +#include +#include + +extern int db_run_mode; + +/* modes the system may be running in */ + +#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 + +extern void db_single_step(db_regs_t *regs, task_t task); + +extern void db_single_step_cmd( + db_expr_t addr, + int have_addr, + db_expr_t count, + const char *modif); + +void db_trace_until_call_cmd( + db_expr_t addr, + int have_addr, + db_expr_t count, + const char * modif); + +void db_trace_until_matching_cmd( + db_expr_t addr, + int have_addr, + db_expr_t count, + const char * modif); + +void db_continue_cmd( + db_expr_t addr, + int have_addr, + db_expr_t count, + const char * modif); + +#ifndef db_set_single_step +void db_set_task_single_step(db_regs_t *, task_t); +#else +#define db_set_task_single_step(regs, task) db_set_single_step(regs) +#endif +#ifndef db_clear_single_step +void db_clear_task_single_step(const db_regs_t *, task_t); +#else +#define db_clear_task_single_step(regs, task) db_clear_single_step(regs) +#endif + +extern boolean_t db_in_single_step(void); + +extern void +db_restart_at_pc( + boolean_t watchpt, + task_t task); + +extern boolean_t +db_stop_at_pc( + boolean_t *is_breakpoint, + task_t task); + +#endif /* _DDB_DB_RUN_H_ */ diff --git a/ddb/db_sym.c b/ddb/db_sym.c new file mode 100644 index 0000000..f0adb0c --- /dev/null +++ b/ddb/db_sym.c @@ -0,0 +1,532 @@ +/* + * Mach Operating System + * Copyright (c) 1992,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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 7/90 + */ + +#if MACH_KDB + +#include +#include +#include +#include +#include +#include +#include +#include + +#include /* vm_map_t */ + +/* + * Multiple symbol tables + */ +#define MAXNOSYMTABS 5 /* mach, bootstrap, ux, emulator, 1 spare */ + +db_symtab_t db_symtabs[MAXNOSYMTABS] = {{0,},}; +int db_nsymtab = 0; + +db_symtab_t *db_last_symtab; + +/* + * Add symbol table, with given name, to list of symbol tables. + */ +boolean_t +db_add_symbol_table( + int type, + char *start, + char *end, + const char *name, + char *ref, + char *map_pointer) +{ + db_symtab_t *st; + extern vm_map_t kernel_map; + + if (db_nsymtab >= MAXNOSYMTABS) + return (FALSE); + + st = &db_symtabs[db_nsymtab]; + st->type = type; + st->start = start; + st->end = end; + st->private = ref; + st->map_pointer = (map_pointer == (char *)kernel_map)? 0: map_pointer; + strncpy(st->name, name, sizeof st->name - 1); + st->name[sizeof st->name - 1] = '\0'; + + db_nsymtab++; + + return (TRUE); +} + +/* + * db_qualify("vm_map", "ux") returns "ux::vm_map". + * + * Note: return value points to static data whose content is + * overwritten by each call... but in practice this seems okay. + */ +static char * __attribute__ ((pure)) +db_qualify(const char *symname, const char *symtabname) +{ + static char tmp[256]; + char *s; + + s = tmp; + while ((*s++ = *symtabname++)) { + } + s[-1] = ':'; + *s++ = ':'; + while ((*s++ = *symname++)) { + } + return tmp; +} + + +boolean_t +db_eqname( const char* src, const 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( + char *name, + db_expr_t *valuep) +{ + db_sym_t sym; + + sym = db_lookup(name); + if (sym == DB_SYM_NULL) + return (FALSE); + db_symbol_values(0, sym, &name, valuep); + + db_free_symbol(sym); + 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(char *symstr) +{ + db_sym_t sp; + int i; + int symtab_start = 0; + int symtab_end = db_nsymtab; + char *cp; + + /* + * Look for, remove, and remember any symbol table specifier. + */ + for (cp = symstr; *cp; cp++) { + if (*cp == ':' && cp[1] == ':') { + *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\n"); + symstr = cp+2; + } + } + + /* + * 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; + } + db_free_symbol(sp); + } + return 0; +} + +/* + * Common utility routine to parse a symbol string into a file + * name, a symbol name and line number. + * This routine is called from X_db_lookup if the object dependent + * handler supports qualified search with a file name or a line number. + * It parses the symbol string, and call an object dependent routine + * with parsed file name, symbol name and line number. + */ +db_sym_t +db_sym_parse_and_lookup( + db_sym_t (*func) (db_symtab_t *, const char*, const char*, int), + db_symtab_t *symtab, + char *symstr) +{ + char *p; + int n; + int n_name; + int line_number; + char *file_name = 0; + char *sym_name = 0; + char *component[3]; + db_sym_t found = DB_SYM_NULL; + + /* + * disassemble the symbol into components: + * [file_name:]symbol[:line_nubmer] + */ + component[0] = symstr; + component[1] = component[2] = 0; + for (p = symstr, n = 1; *p; p++) { + if (*p == ':') { + if (n >= 3) + break; + *p = 0; + component[n++] = p+1; + } + } + if (*p != 0) + goto out; + line_number = 0; + n_name = n; + p = component[n-1]; + if (*p >= '0' && *p <= '9') { + if (n == 1) + goto out; + for (line_number = 0; *p; p++) { + if (*p < '0' || *p > '9') + goto out; + line_number = line_number*10 + *p - '0'; + } + n_name--; + } else if (n >= 3) + goto out; + if (n_name == 1) { + for (p = component[0]; *p && *p != '.'; p++); + if (*p == '.') { + file_name = component[0]; + sym_name = 0; + } else { + file_name = 0; + sym_name = component[0]; + } + } else { + file_name = component[0]; + sym_name = component[1]; + } + found = func(symtab, file_name, sym_name, line_number); + +out: + while (--n >= 1) + component[n][-1] = ':'; + return(found); +} + +/* + * 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; + +static boolean_t +db_name_is_ambiguous(char *sym_name) +{ + int i; + boolean_t found_once = FALSE; + + if (!db_qualify_ambiguous_names) + return FALSE; + + for (i = 0; i < db_nsymtab; i++) { + db_sym_t sp = X_db_lookup(&db_symtabs[i], sym_name); + if (sp) { + if (found_once) + { + db_free_symbol(sp); + return TRUE; + } + found_once = TRUE; + } + db_free_symbol(sp); + } + return FALSE; +} + +/* + * Find the closest symbol to val, and return its name + * and the difference between val and the symbol found. + * + * Logic change. If the task argument is non NULL and a + * matching symbol is found in a symbol table which explicitly + * specifies its map to be task->map, that symbol will have + * precedence over any symbol from a symbol table will a null + * map. This allows overlapping kernel/user maps to work correctly. + * + */ +db_sym_t +db_search_task_symbol( + db_addr_t val, + db_strategy_t strategy, + db_addr_t *offp, /* better be unsigned */ + task_t task) +{ + db_sym_t ret; + + if (task != TASK_NULL) + ret = db_search_in_task_symbol(val, strategy, offp, task); + else + { + ret = db_search_in_task_symbol(val, strategy, offp, task); + /* + db_search_in_task_symbol will return success with + a very large offset when it should have failed. + */ + if (ret == DB_SYM_NULL || (*offp) > 0x1000000) + { + db_free_symbol(ret); + task = db_current_task(); + ret = db_search_in_task_symbol(val, strategy, offp, task); + } + } + + return ret; +} + +db_sym_t +db_search_in_task_symbol( + db_addr_t val, + db_strategy_t strategy, + db_addr_t *offp, + task_t task) +{ + vm_size_t diff; + vm_size_t newdiff; + int i; + db_symtab_t *sp; + db_sym_t ret = DB_SYM_NULL, sym; + vm_map_t map_for_val; + + map_for_val = (task == TASK_NULL)? VM_MAP_NULL: task->map; + newdiff = diff = ~0; + db_last_symtab = (db_symtab_t *) 0; + for (sp = &db_symtabs[0], i = 0; i < db_nsymtab; sp++, i++) + { + newdiff = ~0; + if ((vm_map_t)sp->map_pointer == VM_MAP_NULL || + (vm_map_t)sp->map_pointer == map_for_val) + { + sym = X_db_search_symbol(sp, val, strategy, (db_expr_t*)&newdiff); + if (sym == DB_SYM_NULL) + continue; + if (db_last_symtab == (db_symtab_t *) 0) + { /* first hit */ + db_last_symtab = sp; + diff = newdiff; + db_free_symbol(ret); + ret = sym; + continue; + } + if ((vm_map_t) sp->map_pointer == VM_MAP_NULL && + (vm_map_t) db_last_symtab->map_pointer == VM_MAP_NULL && + newdiff < diff ) + { /* closer null map match */ + db_last_symtab = sp; + diff = newdiff; + db_free_symbol(ret); + ret = sym; + continue; + } + if ((vm_map_t) sp->map_pointer != VM_MAP_NULL && + (newdiff < 0x100000) && + ((vm_map_t) db_last_symtab->map_pointer == VM_MAP_NULL || + newdiff < diff )) + { /* update if new is in matching map and symbol is "close", + and + old is VM_MAP_NULL or old in is matching map but is further away + */ + db_last_symtab = sp; + diff = newdiff; + db_free_symbol(ret); + ret = sym; + continue; + } + } + } + + *offp = diff; + return ret; +} + +/* + * Return name and value of a symbol + */ +void +db_symbol_values( + db_symtab_t *stab, + db_sym_t sym, + char **namep, + db_expr_t *valuep) +{ + db_expr_t value; + char *name; + + if (sym == DB_SYM_NULL) { + *namep = 0; + return; + } + if (stab == 0) + stab = db_last_symtab; + + X_db_symbol_values(stab, sym, &name, &value); + + if (db_name_is_ambiguous(name)) + *namep = db_qualify(name, db_last_symtab->name); + else + *namep = name; + if (valuep) + *valuep = value; +} + + +/* + * Print 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 long db_maxoff = 0x4000; + +void +db_task_printsym( + db_addr_t off, + db_strategy_t strategy, + task_t task) +{ + db_addr_t d; + char *filename; + char *name; + db_expr_t value; + int linenum; + db_sym_t cursym; + + cursym = db_search_task_symbol(off, strategy, &d, task); + db_symbol_values(0, cursym, &name, &value); + if (name == 0 || d >= db_maxoff || value == 0 || *name == 0) { + db_printf("%#n", off); + db_free_symbol(cursym); + return; + } + db_printf("%s", name); + if (d) + db_printf("+0x%x", d); + if (strategy == DB_STGY_PROC) { + if (db_line_at_pc(cursym, &filename, &linenum, off)) { + db_printf(" [%s", filename); + if (linenum > 0) + db_printf(":%d", linenum); + db_printf("]"); + } + } + db_free_symbol(cursym); +} + +void +db_printsym( + db_expr_t off, + db_strategy_t strategy) +{ + db_task_printsym(off, strategy, TASK_NULL); +} + +boolean_t +db_line_at_pc( + db_sym_t sym, + char **filename, + int *linenum, + db_addr_t pc) +{ + return (db_last_symtab) ? + X_db_line_at_pc( db_last_symtab, sym, filename, linenum, pc) : + FALSE; +} + +void db_free_symbol(db_sym_t s) +{ + return (db_last_symtab) ? + X_db_free_symbol( db_last_symtab, s) : + FALSE; +} + +/* + * Switch into symbol-table specific routines + */ + +static void dummy_db_free_symbol(db_sym_t symbol) { } +static boolean_t dummy_db_sym_init(char *a, char *b, const char *c, char *d) { + return FALSE; +} + + +struct db_sym_switch x_db[] = { + + /* BSD a.out format (really, sdb/dbx(1) symtabs) not supported */ + { 0,}, + + { 0,}, + + /* Machdep, not inited here */ + { 0,}, + +#ifdef DB_NO_ELF + { 0,}, +#else /* DB_NO_ELF */ + { dummy_db_sym_init, elf_db_lookup, elf_db_search_symbol, + elf_db_line_at_pc, elf_db_symbol_values, dummy_db_free_symbol }, +#endif /* DB_NO_ELF */ + +}; + +#endif /* MACH_KDB */ diff --git a/ddb/db_sym.h b/ddb/db_sym.h new file mode 100644 index 0000000..f4fb528 --- /dev/null +++ b/ddb/db_sym.h @@ -0,0 +1,264 @@ +/* + * 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. + */ +/* + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 8/90 + */ + +#ifndef _DDB_DB_SYM_H_ +#define _DDB_DB_SYM_H_ + +#include +#include +#include + +/* + * This module can handle multiple symbol tables, + * of multiple types, at the same time + */ +#define SYMTAB_NAME_LEN 32 + +typedef struct { + int type; +#define SYMTAB_AOUT 0 +#define SYMTAB_COFF 1 +#define SYMTAB_MACHDEP 2 +#define SYMTAB_ELF 3 + char *start; /* symtab location */ + char *end; + char *private; /* optional machdep pointer */ + char *map_pointer; /* symbols are for this map only, + if not null */ + char name[SYMTAB_NAME_LEN]; + /* symtab name */ +} 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 concerned 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 + * ..but the machine has nothing + * else to do, now does it ? */ + +/* + * Functions exported by the symtable module + */ + +/* extend the list of symbol tables */ + +extern boolean_t db_add_symbol_table( int type, + char * start, + char * end, + const char *name, + char *ref, + char *map_pointer ); + +/* find symbol value given name */ + +extern int db_value_of_name( char* name, db_expr_t* valuep); + +/* find symbol given value */ + +extern db_sym_t db_search_task_symbol( db_addr_t val, + db_strategy_t strategy, + db_addr_t *offp, + task_t task ); + +/* return name and value of symbol */ + +extern void db_symbol_values( db_symtab_t *stab, + db_sym_t sym, + char** namep, + db_expr_t* valuep); + +/* find symbol in current task */ +#define db_search_symbol(val,strgy,offp) \ + db_search_task_symbol(val,strgy,offp,0) + +/* find name&value given approx val */ + +#define db_find_sym_and_offset(val,namep,offp) \ + do { \ + db_sym_t s; \ + db_symbol_values(0, s = db_search_symbol(val,DB_STGY_ANY,offp) \ + ,namep,0); \ + db_free_symbol(s); \ + } while(0); + + +/* ditto, but no locals */ +#define db_find_xtrn_sym_and_offset(val,namep,offp) \ + do { \ + db_sym_t s; \ + db_symbol_values(0, s = db_search_symbol(val,DB_STGY_XTRN,offp) \ + ,namep,0); \ + db_free_symbol(s); \ + } while(0); + +/* find name&value given approx val */ + +#define db_find_task_sym_and_offset(val,namep,offp,task) \ + do { \ + db_sym_t s; \ + db_symbol_values(0, s = db_search_task_symbol(val,DB_STGY_ANY \ + ,offp,task), \ + namep, 0); \ + db_free_symbol(s); \ + } while(0); + +/* ditto, but no locals */ +#define db_find_xtrn_task_sym_and_offset(val,namep,offp,task) \ + do { \ + db_sym_t s; \ + db_symbol_values(0, s = db_search_task_symbol(val,DB_STGY_XTRN \ + ,offp,task), \ + namep,0); \ + db_free_symbol(s); \ + } while(0); + +/* strcmp, modulo leading char */ +extern boolean_t db_eqname( const char* src, const char* dst, char c ); + +/* print closest symbol to a value */ +extern void db_task_printsym( db_addr_t off, + db_strategy_t strategy, + task_t task); + +/* print closest symbol to a value */ +extern void db_printsym( db_expr_t off, db_strategy_t strategy); + +/* free a symbol */ +extern void db_free_symbol(db_sym_t s); + + +/* + * Symbol table switch, defines the interface + * to symbol-table specific routines. + */ + +extern struct db_sym_switch { + + boolean_t (*init)( + char *start, + char *end, + const char *name, + char *task_addr + ); + + db_sym_t (*lookup)( + db_symtab_t *stab, + char *symstr + ); + db_sym_t (*search_symbol)( + db_symtab_t *stab, + db_addr_t off, + db_strategy_t strategy, + db_expr_t *diffp + ); + + boolean_t (*line_at_pc)( + db_symtab_t *stab, + db_sym_t sym, + char **file, + int *line, + db_addr_t pc + ); + + void (*symbol_values)( + db_symtab_t *stab, + db_sym_t sym, + char **namep, + db_expr_t *valuep + ); + + void (*free_symbol)( + db_sym_t sym + ); +} x_db[]; + +#ifndef symtab_type +#define symtab_type(s) SYMTAB_ELF +#endif + +#define X_db_sym_init(s,e,n,t) x_db[symtab_type(s)].init(s,e,n,t) +#define X_db_lookup(s,n) x_db[(s)->type].lookup(s,n) +#define X_db_search_symbol(s,o,t,d) x_db[(s)->type].search_symbol(s,o,t,d) +#define X_db_line_at_pc(s,p,f,l,a) x_db[(s)->type].line_at_pc(s,p,f,l,a) +#define X_db_symbol_values(s,p,n,v) x_db[(s)->type].symbol_values(s,p,n,v) +#define X_db_free_symbol(s,m) x_db[(s)->type].free_symbol(m) + +extern boolean_t db_line_at_pc( + db_sym_t sym, + char **filename, + int *linenum, + db_addr_t pc); + +extern boolean_t elf_db_sym_init ( + unsigned shdr_num, + vm_size_t shdr_size, + vm_offset_t shdr_addr, + unsigned shdr_shndx, + char *name, + char *task_addr); + +db_sym_t db_lookup(char *); + +db_sym_t +db_search_in_task_symbol( + db_addr_t val, + db_strategy_t strategy, + db_addr_t *offp, + task_t task); + +extern db_sym_t +db_sym_parse_and_lookup( + db_sym_t (*func) (db_symtab_t *, const char*, const char*, int), + db_symtab_t *symtab, + char *symstr); + +#endif /* _DDB_DB_SYM_H_ */ diff --git a/ddb/db_task_thread.c b/ddb/db_task_thread.c new file mode 100644 index 0000000..fe742c2 --- /dev/null +++ b/ddb/db_task_thread.c @@ -0,0 +1,326 @@ +/* + * 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. + */ + +#if MACH_KDB + +#include +#include +#include +#include +#include +#include +#include + + + +/* + * Following constants are used to prevent infinite loop of task + * or thread search due to the incorrect list. + */ +#define DB_MAX_TASKID 0x10000 /* max # of tasks */ +#define DB_MAX_THREADID 0x10000 /* max # of threads in a task */ +#define DB_MAX_PSETS 0x10000 /* max # of processor sets */ + +task_t db_default_task; /* default target task */ +thread_t db_default_thread; /* default target thread */ + +/* + * search valid task queue, and return the queue position as the task id + */ +int +db_lookup_task(const task_t target_task) +{ + task_t task; + int task_id; + processor_set_t pset; + int npset = 0; + + task_id = 0; + if (queue_first(&all_psets) == 0) + return(-1); + queue_iterate(&all_psets, pset, processor_set_t, all_psets) { + if (npset++ >= DB_MAX_PSETS) + return(-1); + if (queue_first(&pset->tasks) == 0) + continue; + queue_iterate(&pset->tasks, task, task_t, pset_tasks) { + if (target_task == task) + return(task_id); + if (task_id++ >= DB_MAX_TASKID) + return(-1); + } + } + return(-1); +} + +/* + * search thread queue of the task, and return the queue position + */ +int +db_lookup_task_thread(const task_t task, const thread_t target_thread) +{ + thread_t thread; + int thread_id; + + thread_id = 0; + if (queue_first(&task->thread_list) == 0) + return(-1); + queue_iterate(&task->thread_list, thread, thread_t, thread_list) { + if (target_thread == thread) + return(thread_id); + if (thread_id++ >= DB_MAX_THREADID) + return(-1); + } + return(-1); +} + +/* + * search thread queue of every valid task, and return the queue position + * as the thread id. + */ +int +db_lookup_thread(const thread_t target_thread) +{ + int thread_id; + task_t task; + processor_set_t pset; + int ntask = 0; + int npset = 0; + + if (queue_first(&all_psets) == 0) + return(-1); + queue_iterate(&all_psets, pset, processor_set_t, all_psets) { + if (npset++ >= DB_MAX_PSETS) + return(-1); + if (queue_first(&pset->tasks) == 0) + continue; + queue_iterate(&pset->tasks, task, task_t, pset_tasks) { + if (ntask++ > DB_MAX_TASKID) + return(-1); + if (task->thread_count == 0) + continue; + thread_id = db_lookup_task_thread(task, target_thread); + if (thread_id >= 0) + return(thread_id); + } + } + return(-1); +} + +/* + * check the address is a valid thread address + */ +boolean_t +db_check_thread_address_valid(const thread_t thread) +{ + if (db_lookup_thread(thread) < 0) { + db_printf("Bad thread address 0x%x\n", thread); + db_flush_lex(); + return(FALSE); + } else + return(TRUE); +} + +/* + * convert task_id(queue position) to task address + */ +static task_t +db_lookup_task_id(int task_id) +{ + task_t task; + processor_set_t pset; + int npset = 0; + + if (task_id > DB_MAX_TASKID) + return(TASK_NULL); + if (queue_first(&all_psets) == 0) + return(TASK_NULL); + queue_iterate(&all_psets, pset, processor_set_t, all_psets) { + if (npset++ >= DB_MAX_PSETS) + return(TASK_NULL); + if (queue_first(&pset->tasks) == 0) + continue; + queue_iterate(&pset->tasks, task, task_t, pset_tasks) { + if (task_id-- <= 0) + return(task); + } + } + return(TASK_NULL); +} + +/* + * convert (task_id, thread_id) pair to thread address + */ +static thread_t +db_lookup_thread_id( + task_t task, + int thread_id) +{ + thread_t thread; + + + if (thread_id > DB_MAX_THREADID) + return(THREAD_NULL); + if (queue_first(&task->thread_list) == 0) + return(THREAD_NULL); + queue_iterate(&task->thread_list, thread, thread_t, thread_list) { + if (thread_id-- <= 0) + return(thread); + } + return(THREAD_NULL); +} + +/* + * get next parameter from a command line, and check it as a valid + * thread address + */ +boolean_t +db_get_next_thread( + thread_t *threadp, + int position) +{ + db_expr_t value; + thread_t thread; + + *threadp = THREAD_NULL; + if (db_expression(&value)) { + thread = (thread_t) value; + if (!db_check_thread_address_valid(thread)) { + db_flush_lex(); + return(FALSE); + } + } else if (position <= 0) { + thread = db_default_thread; + } else + return(FALSE); + *threadp = thread; + return(TRUE); +} + +/* + * check the default thread is still valid + * ( it is called in entering DDB session ) + */ +void +db_init_default_thread(void) +{ + if (db_lookup_thread(db_default_thread) < 0) { + db_default_thread = THREAD_NULL; + db_default_task = TASK_NULL; + } else + db_default_task = db_default_thread->task; +} + +/* + * set or get default thread which is used when /t or :t option is specified + * in the command line + */ +/* ARGSUSED */ +void +db_set_default_thread( + struct db_variable *vp, + db_expr_t *valuep, + int flag, + db_var_aux_param_t ap) +{ + thread_t thread; + + if (flag != DB_VAR_SET) { + *valuep = (db_expr_t) db_default_thread; + return; + } + thread = (thread_t) *valuep; + if (thread != THREAD_NULL && !db_check_thread_address_valid(thread)) + db_error(0); + /* NOTREACHED */ + db_default_thread = thread; + if (thread) + db_default_task = thread->task; + return; +} + +/* + * convert $taskXXX[.YYY] type DDB variable to task or thread address + */ +void +db_get_task_thread( + struct db_variable *vp, + db_expr_t *valuep, + int flag, + db_var_aux_param_t ap) +{ + task_t task; + thread_t thread; + + if (flag != DB_VAR_GET) { + db_error("Cannot set to $task variable\n"); + /* NOTREACHED */ + } + if ((task = db_lookup_task_id(ap->suffix[0])) == TASK_NULL) { + db_printf("no such task($task%d)\n", ap->suffix[0]); + db_error(0); + /* NOTREACHED */ + } + if (ap->level <= 1) { + *valuep = (db_expr_t) task; + return; + } + if ((thread = db_lookup_thread_id(task, ap->suffix[1])) == THREAD_NULL){ + db_printf("no such thread($task%d.%d)\n", + ap->suffix[0], ap->suffix[1]); + db_error(0); + /* NOTREACHED */ + } + *valuep = (db_expr_t) thread; + return; +} + +/* + * convert $mapXXX type DDB variable to map address + */ +void +db_get_map(struct db_variable *vp, + db_expr_t *valuep, + int flag, + db_var_aux_param_t ap) +{ + task_t task; + + if (flag != DB_VAR_GET) { + db_error("Cannot set to $map variable\n"); + /* NOTREACHED */ + } + + if ((task = db_lookup_task_id(ap->suffix[0])) == TASK_NULL) { + db_printf("no such map($map%d)\n", ap->suffix[0]); + db_error(0); + /* NOTREACHED */ + } + + *valuep = (db_expr_t) task->map; +} + +#endif /* MACH_KDB */ diff --git a/ddb/db_task_thread.h b/ddb/db_task_thread.h new file mode 100644 index 0000000..55ab4f5 --- /dev/null +++ b/ddb/db_task_thread.h @@ -0,0 +1,73 @@ +/* + * 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. + */ + +#ifndef _DDB_DB_TASK_THREAD_H_ +#define _DDB_DB_TASK_THREAD_H_ + +#include + +#include +#include + +#define db_current_task() \ + ((current_thread())? current_thread()->task: TASK_NULL) +#define db_target_space(thread, user_space) \ + ((!(user_space))? TASK_NULL: \ + (thread)? (thread)->task: db_current_task()) +#define db_is_current_task(task) \ + ((task) == TASK_NULL || (task) == db_current_task()) + +extern task_t db_default_task; /* default target task */ +extern thread_t db_default_thread; /* default target thread */ + +extern int db_lookup_task(const task_t); +extern int db_lookup_thread(const thread_t); +extern int db_lookup_task_thread(const task_t, const thread_t); +extern boolean_t db_check_thread_address_valid(const thread_t); +extern boolean_t db_get_next_thread(thread_t *, int); +extern void db_init_default_thread(void); + +extern void +db_set_default_thread( + struct db_variable *vp, + db_expr_t *valuep, + int flag, + db_var_aux_param_t ap); + +extern void +db_get_task_thread( + struct db_variable *vp, + db_expr_t *valuep, + int flag, + db_var_aux_param_t ap); + +extern void +db_get_map(struct db_variable *vp, + db_expr_t *valuep, + int flag, + db_var_aux_param_t ap); + +#endif /* _DDB_DB_TASK_THREAD_H_ */ diff --git a/ddb/db_trap.c b/ddb/db_trap.c new file mode 100644 index 0000000..cbb6bde --- /dev/null +++ b/ddb/db_trap.c @@ -0,0 +1,115 @@ +/* + * 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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 7/90 + */ + +#if MACH_KDB + +/* + * Trap entry point to kernel debugger. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +extern jmp_buf_t *db_recover; + +extern int db_inst_count; +extern int db_load_count; +extern int db_store_count; + +void +db_task_trap( + int type, + int code, + boolean_t user_space) +{ + jmp_buf_t db_jmpbuf; + jmp_buf_t *prev; + boolean_t bkpt; + boolean_t watchpt; + task_t task_space; + + check_simple_locks_disable(); + + task_space = db_target_space(current_thread(), user_space); + bkpt = IS_BREAKPOINT_TRAP(type, code); + watchpt = IS_WATCHPOINT_TRAP(type, code); + + db_init_default_thread(); + db_check_breakpoint_valid(); + if (db_stop_at_pc(&bkpt, task_space)) { + 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 "); + else if (watchpt) + db_printf("Watchpoint at "); + else + db_printf("Stopped at "); + db_dot = PC_REGS(DDB_REGS); + + prev = db_recover; + if (_setjmp(db_recover = &db_jmpbuf) == 0) + db_print_loc_and_inst(db_dot, task_space); + else + db_printf("Trouble printing location %#X.\n", db_dot); + + if (!bkpt && !watchpt && _setjmp(db_recover = &db_jmpbuf) == 0) + db_stack_trace_cmd(0, 0, -1, ""); + db_recover = prev; + + db_command_loop(); + } + + check_simple_locks_enable(); + db_restart_at_pc(watchpt, task_space); +} + +void +db_trap( + int type, + int code) +{ + db_task_trap(type, code, !DB_VALID_KERN_ADDR(PC_REGS(DDB_REGS))); +} + +#endif /* MACH_KDB */ diff --git a/ddb/db_trap.h b/ddb/db_trap.h new file mode 100644 index 0000000..933fcd3 --- /dev/null +++ b/ddb/db_trap.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2007 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Barry deFreese. + */ + +#ifndef _DDB_DB_TRAP_H_ +#define _DDB_DB_TRAP_H_ + +#include +#include + +extern void db_task_trap ( + int type, + int code, + boolean_t user_space); + +extern void db_trap (int type, int code); + +#endif /* _DDB_DB_TRAP_H_ */ diff --git a/ddb/db_variables.c b/ddb/db_variables.c new file mode 100644 index 0000000..40f2d4d --- /dev/null +++ b/ddb/db_variables.c @@ -0,0 +1,224 @@ +/* + * 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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 7/90 + */ + +#if MACH_KDB + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +extern unsigned long db_maxoff; + +extern db_expr_t db_radix; +extern db_expr_t db_max_width; +extern db_expr_t db_tab_stop_width; +extern db_expr_t db_max_line; + +#define DB_NWORK 32 /* number of work variable */ + +db_expr_t db_work[DB_NWORK]; /* work variable */ + +struct db_variable db_vars[] = { + { "radix", &db_radix, FCN_NULL }, + { "maxoff", (db_expr_t*)&db_maxoff, FCN_NULL }, + { "maxwidth", &db_max_width, FCN_NULL }, + { "tabstops", &db_tab_stop_width, FCN_NULL }, + { "lines", &db_max_line, FCN_NULL }, + { "thread", 0, db_set_default_thread }, + { "task", 0, db_get_task_thread, + 1, 2, -1, -1 }, + { "map", 0, db_get_map, + 1, 1, -1, -1 }, + { "work", &db_work[0], FCN_NULL, + 1, 1, 0, DB_NWORK-1 }, + { "arg", 0, db_arg_variable, + 1, 1, -1, -1 }, +}; +struct db_variable *db_evars = db_vars + sizeof(db_vars)/sizeof(db_vars[0]); + +static const char * +db_get_suffix( + const char *suffix, + short *suffix_value) +{ + int value; + + for (value = 0; *suffix && *suffix != '.' && *suffix != ':'; suffix++) { + if (*suffix < '0' || *suffix > '9') + return(0); + value = value*10 + *suffix - '0'; + } + *suffix_value = value; + if (*suffix == '.') + suffix++; + return(suffix); +} + +static boolean_t +db_cmp_variable_name( + struct db_variable *vp, + char *name, + const db_var_aux_param_t ap) +{ + char *var_np; + const char *np; + int level; + + for (np = name, var_np = vp->name; *var_np; ) { + if (*np++ != *var_np++) + return(FALSE); + } + for (level = 0; *np && *np != ':' && level < vp->max_level; level++){ + if ((np = db_get_suffix(np, &ap->suffix[level])) == 0) + return(FALSE); + } + if ((*np && *np != ':') || level < vp->min_level + || (level > 0 && (ap->suffix[0] < vp->low + || (vp->high >= 0 && ap->suffix[0] > vp->high)))) + return(FALSE); + db_strcpy(ap->modif, (*np)? np+1: ""); + ap->thread = (db_option(ap->modif, 't')?db_default_thread: THREAD_NULL); + ap->level = level; + return(TRUE); +} + +static int +db_find_variable( + struct db_variable **varp, + db_var_aux_param_t ap) +{ + int t; + struct db_variable *vp; + + t = db_read_token(); + if (t == tIDENT) { + for (vp = db_vars; vp < db_evars; vp++) { + if (db_cmp_variable_name(vp, db_tok_string, ap)) { + *varp = vp; + return (1); + } + } + for (vp = db_regs; vp < db_eregs; vp++) { + if (db_cmp_variable_name(vp, db_tok_string, ap)) { + *varp = vp; + return (1); + } + } + } + db_printf("Unknown variable \"$%s\"\n", db_tok_string); + db_error(0); + return (0); +} + +int +db_get_variable(db_expr_t *valuep) +{ + struct db_variable *vp; + struct db_var_aux_param aux_param; + char modif[TOK_STRING_SIZE]; + + aux_param.modif = modif; + if (!db_find_variable(&vp, &aux_param)) + return (0); + + db_read_write_variable(vp, valuep, DB_VAR_GET, &aux_param); + + return (1); +} + +void +db_read_write_variable( + struct db_variable *vp, + db_expr_t *valuep, + int rw_flag, + db_var_aux_param_t ap) +{ + void (*func)(struct db_variable *, db_expr_t *, int, db_var_aux_param_t) = vp->fcn; + struct db_var_aux_param aux_param; + + if (ap == 0) { + ap = &aux_param; + ap->modif = ""; + ap->level = 0; + ap->thread = THREAD_NULL; + } + if (func == FCN_NULL) { + if (rw_flag == DB_VAR_SET) + vp->valuep[(ap->level)? (ap->suffix[0] - vp->low): 0] = *valuep; + else + *valuep = vp->valuep[(ap->level)? (ap->suffix[0] - vp->low): 0]; + } else + (*func)(vp, valuep, rw_flag, ap); +} + +void +db_set_cmd(void) +{ + db_expr_t value; + int t; + struct db_variable *vp; + struct db_var_aux_param aux_param; + char modif[TOK_STRING_SIZE]; + + aux_param.modif = modif; + t = db_read_token(); + if (t != tDOLLAR) { + db_error("Variable name should be prefixed with $\n"); + return; + } + if (!db_find_variable(&vp, &aux_param)) { + 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 ((t = db_read_token()) == tSEMI_COLON) + db_unread_token(t); + else if (t != tEOL) + db_error("?\n"); + + db_read_write_variable(vp, &value, DB_VAR_SET, &aux_param); +} + +#endif /* MACH_KDB */ diff --git a/ddb/db_variables.h b/ddb/db_variables.h new file mode 100644 index 0000000..9880d50 --- /dev/null +++ b/ddb/db_variables.h @@ -0,0 +1,88 @@ +/* + * 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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 7/90 + */ + +#ifndef _DB_VARIABLES_H_ +#define _DB_VARIABLES_H_ + +#include +#include + +/* + * Debugger variables. + */ +struct db_var_aux_param; /* forward */ +typedef struct db_var_aux_param *db_var_aux_param_t; /* forward */ +struct db_variable { + char *name; /* Name of variable */ + db_expr_t *valuep; /* pointer to value of variable */ + /* function to call when reading/writing */ + void (*fcn)(struct db_variable *, db_expr_t *, int, db_var_aux_param_t); + short min_level; /* number of minimum suffix levels */ + short max_level; /* number of maximum suffix levels */ + short low; /* low value of level 1 suffix */ + short high; /* high value of level 1 suffix */ +#define DB_VAR_GET 0 +#define DB_VAR_SET 1 +}; +#define FCN_NULL ((void (*)())0) + +#define DB_VAR_LEVEL 3 /* maximum number of suffix level */ + +#define db_read_variable(vp, valuep) \ + db_read_write_variable(vp, valuep, DB_VAR_GET, 0) +#define db_write_variable(vp, valuep) \ + db_read_write_variable(vp, valuep, DB_VAR_SET, 0) + +/* + * auxiliary parameters passed to a variable handler + */ +struct db_var_aux_param { + char *modif; /* option strings */ + short level; /* number of levels */ + short suffix[DB_VAR_LEVEL]; /* suffix */ + thread_t thread; /* target task */ +}; + +/* Already defined above. */ +/* typedef struct db_var_aux_param *db_var_aux_param_t; */ + + +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 int db_get_variable(db_expr_t *valuep); + +void db_set_cmd(void); + +void db_read_write_variable(struct db_variable *, db_expr_t *, int, struct db_var_aux_param *); + +#endif /* _DB_VARIABLES_H_ */ diff --git a/ddb/db_watch.c b/ddb/db_watch.c new file mode 100644 index 0000000..c3d2835 --- /dev/null +++ b/ddb/db_watch.c @@ -0,0 +1,329 @@ +/* + * 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. + */ +/* + * Author: Richard P. Draves, Carnegie Mellon University + * Date: 10/90 + */ + +#if MACH_KDB + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +/* + * Watchpoints. + */ + +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; + +extern vm_map_t kernel_map; + +static db_watchpoint_t +db_watchpoint_alloc(void) +{ + 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); +} + +static void +db_watchpoint_free(db_watchpoint_t watch) +{ + watch->link = db_free_watchpoints; + db_free_watchpoints = watch; +} + +void +db_set_watchpoint( + const task_t task, + db_addr_t addr, + vm_size_t size) +{ + db_watchpoint_t watch; + + /* + * Should we do anything fancy with overlapping regions? + */ + + for (watch = db_watchpoint_list; watch != 0; watch = watch->link) { + if (watch->task == task && + (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->task = task; + watch->loaddr = addr; + watch->hiaddr = addr+size; + + watch->link = db_watchpoint_list; + db_watchpoint_list = watch; + + db_watchpoints_inserted = FALSE; +} + +void +db_delete_watchpoint(const task_t task, db_addr_t addr) +{ + db_watchpoint_t watch; + db_watchpoint_t *prev; + + for (prev = &db_watchpoint_list; (watch = *prev) != 0; + prev = &watch->link) { + if (watch->task == task && + (watch->loaddr <= addr) && + (addr < watch->hiaddr)) { + *prev = watch->link; + db_watchpoint_free(watch); + return; + } + } + + db_printf("Not set.\n"); +} + +void +db_list_watchpoints(void) +{ + db_watchpoint_t watch; + int task_id; + + if (db_watchpoint_list == 0) { + db_printf("No watchpoints set\n"); + return; + } + + db_printf("Space Address Size\n"); + for (watch = db_watchpoint_list; watch != 0; watch = watch->link) { + if (watch->task == TASK_NULL) + db_printf("kernel "); + else { + task_id = db_lookup_task(watch->task); + if (task_id < 0) + db_printf("%*X", 2*sizeof(vm_offset_t), watch->task); + else + db_printf("task%-3d ", task_id); + } + db_printf(" %*X %X\n", 2*sizeof(vm_offset_t), watch->loaddr, + watch->hiaddr - watch->loaddr); + } +} + +static int +db_get_task(const char *modif, task_t *taskp, db_addr_t addr) +{ + task_t task = TASK_NULL; + db_expr_t value; + boolean_t user_space; + + user_space = db_option(modif, 'T'); + if (user_space) { + if (db_expression(&value)) { + task = (task_t)value; + if (db_lookup_task(task) < 0) { + db_printf("bad task address %X\n", task); + return(-1); + } + } else { + task = db_default_task; + if (task == TASK_NULL) { + if ((task = db_current_task()) == TASK_NULL) { + db_printf("no task\n"); + return(-1); + } + } + } + } + if (!DB_VALID_ADDRESS(addr, user_space)) { + db_printf("Address %#X is not in %s space\n", addr, + (user_space)? "user": "kernel"); + return(-1); + } + *taskp = task; + return(0); +} + +/* Delete watchpoint */ +/*ARGSUSED*/ +void +db_deletewatch_cmd( + db_expr_t addr, + int have_addr, + db_expr_t count, + const char * modif) +{ + task_t task; + + if (db_get_task(modif, &task, addr) < 0) + return; + db_delete_watchpoint(task, addr); +} + +/* Set watchpoint */ +/*ARGSUSED*/ +void +db_watchpoint_cmd( + db_expr_t addr, + int have_addr, + db_expr_t count, + const char * modif) +{ + vm_size_t size; + db_expr_t value; + task_t task; + + if (db_get_task(modif, &task, addr) < 0) + return; + if (db_expression(&value)) + size = (vm_size_t) value; + else + size = sizeof(int); + db_set_watchpoint(task, addr, size); +} + +/* list watchpoints */ +void +db_listwatch_cmd( + db_expr_t addr, + int have_addr, + db_expr_t count, + const char * modif) +{ + db_list_watchpoints(); +} + +void +db_set_watchpoints(void) +{ + db_watchpoint_t watch; + vm_map_t map; + unsigned hw_idx = 0; + + if (!db_watchpoints_inserted) { + for (watch = db_watchpoint_list; watch != 0; watch = watch->link) { + if (db_set_hw_watchpoint(watch, hw_idx)) { + hw_idx++; + continue; + } + map = (watch->task)? watch->task->map: kernel_map; + pmap_protect(map->pmap, + trunc_page(watch->loaddr), + round_page(watch->hiaddr), + VM_PROT_READ); + } + db_watchpoints_inserted = TRUE; + } +} + +void +db_clear_watchpoints(void) +{ + unsigned hw_idx = 0; + + while (db_clear_hw_watchpoint(hw_idx)) + hw_idx++; + + db_watchpoints_inserted = FALSE; +} + +boolean_t +db_find_watchpoint( + vm_map_t map, + db_addr_t addr, + db_regs_t *regs) +{ + db_watchpoint_t watch; + db_watchpoint_t found = 0; + task_t task_space; + + task_space = (map == kernel_map)? TASK_NULL: db_current_task(); + for (watch = db_watchpoint_list; watch != 0; watch = watch->link) { + if (watch->task == task_space) { + 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, task_space); + } + + return (FALSE); +} + +#endif /* MACH_KDB */ diff --git a/ddb/db_watch.h b/ddb/db_watch.h new file mode 100644 index 0000000..86f07fb --- /dev/null +++ b/ddb/db_watch.h @@ -0,0 +1,80 @@ +/* + * 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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 10/90 + */ + +#if MACH_KDB + +#ifndef _DDB_DB_WATCH_ +#define _DDB_DB_WATCH_ + +#include +#include +#include + +/* + * Watchpoint. + */ + +typedef struct db_watchpoint { + task_t task; /* 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(void); +extern void db_clear_watchpoints(void); + +extern void db_set_watchpoint(const task_t task, db_addr_t addr, vm_size_t size); +extern void db_delete_watchpoint(const task_t task, db_addr_t addr); +extern void db_list_watchpoints(void); + +void db_listwatch_cmd( + db_expr_t addr, + boolean_t have_addr, + db_expr_t count, + const char * modif); + +void db_deletewatch_cmd( + db_expr_t addr, + int have_addr, + db_expr_t count, + const char * modif); + +void db_watchpoint_cmd( + db_expr_t addr, + int have_addr, + db_expr_t count, + const char * modif); + +#endif /* _DDB_DB_WATCH_ */ + +#endif /* MACH_KDB */ diff --git a/ddb/db_write_cmd.c b/ddb/db_write_cmd.c new file mode 100644 index 0000000..cfc2b70 --- /dev/null +++ b/ddb/db_write_cmd.c @@ -0,0 +1,111 @@ +/* + * Mach Operating System + * Copyright (c) 1992,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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 7/90 + */ + +#if MACH_KDB + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + + +/* + * Write to file. + */ +/*ARGSUSED*/ +void +db_write_cmd( + db_expr_t address, + boolean_t have_addr, + db_expr_t count, + const char * modif) +{ + db_addr_t addr; + db_expr_t old_value; + db_expr_t new_value; + int size; + boolean_t wrote_one = FALSE; + boolean_t t_opt, u_opt; + thread_t thread; + task_t task; + + addr = (db_addr_t) address; + + size = db_size_option(modif, &u_opt, &t_opt); + if (t_opt) + { + if (!db_get_next_thread(&thread, 0)) + return; + task = thread->task; + } + else + task = db_current_task(); + + /* if user space is not explicitly specified, + look in the kernel */ + if (!u_opt) + task = TASK_NULL; + + if (!DB_VALID_ADDRESS(addr, u_opt)) { + db_printf("Bad address %#*X\n", 2*sizeof(vm_offset_t), addr); + return; + } + + while (db_expression(&new_value)) { + old_value = db_get_task_value(addr, size, FALSE, task); + db_task_printsym(addr, DB_STGY_ANY, task); + db_printf("\t\t%#*N\t=\t%#*N\n", + 2*sizeof(db_expr_t), old_value, + 2*sizeof(db_expr_t), new_value); + db_put_task_value(addr, size, new_value, task); + addr += size; + + wrote_one = TRUE; + } + + if (!wrote_one) + db_error("Nothing written.\n"); + + db_next = addr; + db_prev = addr - size; +} + +#endif /* MACH_KDB */ diff --git a/ddb/db_write_cmd.h b/ddb/db_write_cmd.h new file mode 100644 index 0000000..3a1d057 --- /dev/null +++ b/ddb/db_write_cmd.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2013 Free Software Foundation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _DDB_DB_WRITE_CMD_H_ +#define _DDB_DB_WRITE_CMD_H_ + +#include +#include + +/* Prototypes for functions exported by this module. + */ + +void db_write_cmd( + db_expr_t address, + boolean_t have_addr, + db_expr_t count, + const char * modif); + +#endif /* !_DDB_DB_WRITE_CMD_H_ */ diff --git a/ddb/nlist.h b/ddb/nlist.h new file mode 100644 index 0000000..b948dfd --- /dev/null +++ b/ddb/nlist.h @@ -0,0 +1,63 @@ +/* + * Mach Operating System + * Copyright (c) 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. + */ +/* + * nlist.h - symbol table entry structure for an a.out file + * derived from FSF's a.out.gnu.h + * + */ + +#ifndef _DDB_NLIST_H_ +#define _DDB_NLIST_H_ + +struct nlist { + union n_un { + char *n_name; /* symbol name */ + long n_strx; /* index into file string table */ + } n_un; + unsigned char n_type; /* type flag, i.e. N_TEXT etc; see below */ + unsigned char n_other; /* machdep uses */ + short n_desc; /* see */ +#if alpha + int n_pad; /* alignment, used to carry framesize info */ +#endif + vm_offset_t n_value; /* value of this symbol (or sdb offset) */ +}; + +/* + * Simple values for n_type. + */ +#define N_UNDF 0 /* undefined */ +#define N_ABS 2 /* absolute */ +#define N_TEXT 4 /* text */ +#define N_DATA 6 /* data */ +#define N_BSS 8 /* bss */ +#define N_FN 0x1f /* file name symbol */ +#define N_EXT 1 /* external bit, or'ed in */ +#define N_TYPE 0x1e /* mask for all the type bits */ +#define N_STAB 0xe0 /* if any of these bits set, a SDB entry */ + + +#endif /* _DDB_NLIST_H_ */ diff --git a/ddb/stab.h b/ddb/stab.h new file mode 100644 index 0000000..55e9d45 --- /dev/null +++ b/ddb/stab.h @@ -0,0 +1,73 @@ +/*- + * 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. + * + * @(#)stab.h 5.2 (Berkeley) 4/4/91 + */ + +#ifndef _DDB_STAB_H_ +#define _DDB_STAB_H_ + +/* + * The following are symbols used by various debuggers and by the Pascal + * compiler. Each of them must have one (or more) of the bits defined by + * the N_STAB mask set. + */ + +#define N_GSYM 0x20 /* global symbol */ +#define N_FNAME 0x22 /* F77 function name */ +#define N_FUN 0x24 /* procedure name */ +#define N_STSYM 0x26 /* data segment variable */ +#define N_LCSYM 0x28 /* bss segment variable */ +#define N_MAIN 0x2a /* main function name */ +#define N_PC 0x30 /* global Pascal symbol */ +#define N_FRAME 0x34 /* stack frame descriptor */ +#define N_RSYM 0x40 /* register variable */ +#define N_SLINE 0x44 /* text segment line number */ +#define N_DSLINE 0x46 /* data segment line number */ +#define N_BSLINE 0x48 /* bss segment line number */ +#define N_SSYM 0x60 /* structure/union element */ +#define N_SO 0x64 /* main source file name */ +#define N_LSYM 0x80 /* stack variable */ +#define N_BINCL 0x82 /* include file beginning */ +#define N_SOL 0x84 /* included source file name */ +#define N_PSYM 0xa0 /* parameter variable */ +#define N_EINCL 0xa2 /* include file end */ +#define N_ENTRY 0xa4 /* alternate entry point */ +#define N_LBRAC 0xc0 /* left bracket */ +#define N_EXCL 0xc2 /* deleted include file */ +#define N_RBRAC 0xe0 /* right bracket */ +#define N_BCOMM 0xe2 /* begin common */ +#define N_ECOMM 0xe4 /* end common */ +#define N_ECOML 0xe8 /* end common (local name) */ +#define N_LENG 0xfe /* length of preceding entry */ + +#endif /* _DDB_STAB_H_ */ diff --git a/ddb/tr.h b/ddb/tr.h new file mode 100644 index 0000000..2d058ca --- /dev/null +++ b/ddb/tr.h @@ -0,0 +1,117 @@ +/* + * (c) Copyright 1992, 1993, 1994, 1995 OPEN SOFTWARE FOUNDATION, INC. + * ALL RIGHTS RESERVED + */ +/* + * OSF RI nmk19b2 5/2/95 + */ + +/* + * File: ddb/tr.h + * Author: Alan Langerman, Jeffrey Heller + * Date: 1992 + * + * Internal trace routines. Like old-style XPRs but + * less formatting. + */ + +#ifndef NDEBUG +#define MACH_ASSERT 1 +#else +#define MACH_ASSERT 0 +#endif + +#include + +/* + * Originally, we only wanted tracing when + * MACH_TR and MACH_ASSERT were turned on + * together. Now, there's no reason why + * MACH_TR and MACH_ASSERT can't be completely + * orthogonal. + */ +#define TRACE_BUFFER (MACH_TR) + +/* + * Log events in a circular trace buffer for future debugging. + * Events are unsigned integers. Each event has a descriptive + * message. + * + * TR_DECL must be used at the beginning of a routine using + * one of the tr calls. The macro should be passed the name + * of the function surrounded by quotation marks, e.g., + * TR_DECL("netipc_recv_intr"); + * and should be terminated with a semi-colon. The TR_DECL + * must be the *last* declaration in the variable declaration + * list, or syntax errors will be introduced when TRACE_BUFFER + * is turned off. + */ +#ifndef _DDB_TR_H_ +#define _DDB_TR_H_ + +#if TRACE_BUFFER + +#include + +#define __ui__ (unsigned int) +#define TR_INIT() tr_init() +#define TR_SHOW(a,b,c) show_tr((a),(b),(c)) +#define TR_DECL(funcname) char *__ntr_func_name__ = funcname +#define tr1(msg) \ + tr(__ntr_func_name__, __FILE__, __LINE__, (msg), \ + 0,0,0,0) +#define tr2(msg,tag1) \ + tr(__ntr_func_name__, __FILE__, __LINE__, (msg), \ + __ui__(tag1),0,0,0) +#define tr3(msg,tag1,tag2) \ + tr(__ntr_func_name__, __FILE__, __LINE__, (msg), \ + __ui__(tag1),__ui__(tag2),0,0) +#define tr4(msg,tag1,tag2,tag3) \ + tr(__ntr_func_name__, __FILE__, __LINE__, (msg), \ + __ui__(tag1),__ui__(tag2),__ui__(tag3),0) +#define tr5(msg,tag1,tag2,tag3,tag4) \ + tr(__ntr_func_name__, __FILE__, __LINE__, (msg), \ + __ui__(tag1),__ui__(tag2),__ui__(tag3),__ui__(tag4)) + +/* + * Adjust tr log indentation based on function + * call graph; this method is quick-and-dirty + * and only works safely on a uniprocessor. + */ +extern int tr_indent; +#define tr_start() tr_indent++ +#define tr_stop() tr_indent-- + +extern void tr_init(void); +extern void tr( + char *funcname, + char *file, + unsigned int lineno, + char *fmt, + unsigned int tag1, + unsigned int tag2, + unsigned int tag3, + unsigned int tag4); + +extern void db_show_tr( + db_expr_t addr, + boolean_t have_addr, + db_expr_t count, + char * modif); + +#else /* TRACE_BUFFER */ + +#define TR_INIT() +#define TR_SHOW(a,b,c) +#define TR_DECL(funcname) +#define tr1(msg) +#define tr2(msg, tag1) +#define tr3(msg, tag1, tag2) +#define tr4(msg, tag1, tag2, tag3) +#define tr5(msg, tag1, tag2, tag3, tag4) +#define tr_start() +#define tr_stop() + +#endif /* TRACE_BUFFER */ + +#endif /* _DDB_TR_H_ */ diff --git a/device/blkio.c b/device/blkio.c new file mode 100644 index 0000000..0dfa33c --- /dev/null +++ b/device/blkio.c @@ -0,0 +1,66 @@ +/* + * 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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 7/89 + * + * Block IO driven from generic kernel IO interface. + */ +#include + +#include +#include +#include +#include +#include +#include + + +/* + * 'standard' max_count routine. VM continuations mean that this + * code can cope with arbitrarily-sized write operations (they won't be + * atomic, but any caller that cares will do the op synchronously). + */ +#define MAX_PHYS (256 * 1024) + +void minphys(io_req_t ior) +{ + if ((ior->io_op & (IO_WRITE | IO_READ | IO_OPEN)) == IO_WRITE) + return; + + if (ior->io_count > MAX_PHYS) + ior->io_count = MAX_PHYS; +} + +/* + * Dummy routine placed in device switch entries to indicate that + * block device may be mapped. + */ +vm_offset_t block_io_mmap(dev_t dev, vm_offset_t off, int prot) +{ + return (0); +} + diff --git a/device/blkio.h b/device/blkio.h new file mode 100644 index 0000000..b188f38 --- /dev/null +++ b/device/blkio.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2013 Free Software Foundation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _DEVICE_BLKIO_H_ +#define _DEVICE_BLKIO_H_ + +#include + +extern vm_offset_t block_io_mmap(dev_t dev, vm_offset_t off, int prot); + +#endif /* _DEVICE_BLKIO_H_ */ diff --git a/device/buf.h b/device/buf.h new file mode 100644 index 0000000..7c8a436 --- /dev/null +++ b/device/buf.h @@ -0,0 +1,96 @@ +/* + * 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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 3/90 + * + * Definitions to make new IO structures look like old ones + */ + +#ifndef _DEVICE_BUF_H_ +#define _DEVICE_BUF_H_ + +/* + * io_req and fields + */ +#include + +#define buf io_req + +/* + * Redefine fields for drivers using old names + */ +#define b_flags io_op +#define b_bcount io_count +#define b_error io_error +#define b_dev io_unit +#define b_blkno io_recnum +#define b_resid io_residual +#define b_un io_un +#define b_addr data +#define av_forw io_next +#define av_back io_prev +#define b_physblock io_physrec +#define b_blocktotal io_rectotal + +/* + * Redefine fields for driver request list heads, using old names. + */ +#define b_actf io_next +#define b_actl io_prev +#define b_forw io_link +#define b_back io_rlink +#define b_active io_count +#define b_errcnt io_residual +#define b_bufsize io_alloc_size + +/* + * Redefine flags + */ +#define B_WRITE IO_WRITE +#define B_READ IO_READ +#define B_OPEN IO_OPEN +#define B_DONE IO_DONE +#define B_ERROR IO_ERROR +#define B_BUSY IO_BUSY +#define B_WANTED IO_WANTED +#define B_BAD IO_BAD +#define B_CALL IO_CALL + +#define B_MD1 IO_SPARE_START + +/* + * Export standard minphys routine. + */ +extern void minphys(io_req_t); + +/* + * Alternate name for iodone + */ +#define biodone iodone +#define biowait iowait + +#endif /* _DEVICE_BUF_H_ */ diff --git a/device/chario.c b/device/chario.c new file mode 100644 index 0000000..3fe93cc --- /dev/null +++ b/device/chario.c @@ -0,0 +1,1060 @@ +/* + * Mach Operating System + * Copyright (c) 1993-1988 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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 8/88 + * + * TTY io. + * Compatibility with old TTY device drivers. + */ + +#include +#include +#include +#include /* spl definitions */ + +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +/* If you change these, check that tty_outq_size and tty_inq_size + * is greater than largest tthiwat entry. + */ +short tthiwat[NSPEEDS] = + { 100,100,100,100,100,100,100,200,200,400,400,400,650,650,1300,2000, + 2000,2000 }; +short ttlowat[NSPEEDS] = + { 30, 30, 30, 30, 30, 30, 30, 50, 50,120,120,120,125,125, 125, 125, + 125,125 }; + +/* + * Fake 'line discipline' switch for the benefit of old code + * that wants to call through it. + */ +struct ldisc_switch linesw[] = { + { + char_read, + char_write, + ttyinput, + ttymodem, + tty_output + } +}; + +/* + * Sizes for input and output circular buffers. + */ +const unsigned int tty_inq_size = 4096; /* big nuf */ +const unsigned int tty_outq_size = 2048; /* Must be bigger that tthiwat */ +boolean_t pdma_default = TRUE; /* turn pseudo dma on by default */ + +/* + * compute pseudo-dma tables + */ + +int pdma_timeouts[NSPEEDS]; /* how many ticks in timeout */ +int pdma_water_mark[NSPEEDS]; + + +void chario_init(void) +{ + /* the basic idea with the timeouts is two allow enough + time for a character to show up if data is coming in at full data rate + plus a little slack. 2 ticks is considered slack + Below 300 baud we just glob a character at a time */ +#define _PR(x) ((hz/x) + 2) + + int i; + + for (i = B0; i < B300; i++) + pdma_timeouts[i] = 0; + + pdma_timeouts[B300] = _PR(30); + pdma_timeouts[B600] = _PR(60); + pdma_timeouts[B1200] = _PR(120); + pdma_timeouts[B1800] = _PR(180); + pdma_timeouts[B2400] = _PR(240); + pdma_timeouts[B4800] = _PR(480); + pdma_timeouts[B9600] = _PR(960); + pdma_timeouts[EXTA] = _PR(1440); /* >14400 baud */ + pdma_timeouts[EXTB] = _PR(1920); /* >19200 baud */ + pdma_timeouts[B57600] = _PR(5760); + pdma_timeouts[B115200] = _PR(11520); + + for (i = B0; i < B300; i++) + pdma_water_mark[i] = 0; + + /* for the slow speeds, we try to buffer 0.02 of the baud rate + (20% of the character rate). For the faster lines, + we try to buffer 1/2 the input queue size */ + +#undef _PR +#define _PR(x) (0.20 * x) + + pdma_water_mark[B300] = _PR(120); + pdma_water_mark[B600] = _PR(120); + pdma_water_mark[B1200] = _PR(120); + pdma_water_mark[B1800] = _PR(180); + pdma_water_mark[B2400] = _PR(240); + pdma_water_mark[B4800] = _PR(480); + i = tty_inq_size/2; + pdma_water_mark[B9600] = i; + pdma_water_mark[EXTA] = i; /* >14400 baud */ + pdma_water_mark[EXTB] = i; /* >19200 baud */ + pdma_water_mark[B57600] = i; + pdma_water_mark[B115200] = i; + + return; +} + +/* + * Open TTY, waiting for CARR_ON. + * No locks may be held. + * May run on any CPU. + */ +io_return_t char_open( + int dev, + struct tty * tp, + dev_mode_t mode, + io_req_t ior) +{ + spl_t s; + io_return_t rc = D_SUCCESS; + + s = simple_lock_irq(&tp->t_lock); + + tp->t_dev = dev; + + if (tp->t_mctl) + (*tp->t_mctl)(tp, TM_DTR, DMSET); + + if (pdma_default) + tp->t_state |= TS_MIN; + + if ((tp->t_state & TS_CARR_ON) == 0) { + /* + * No carrier. + */ + if (mode & D_NODELAY) { + tp->t_state |= TS_ONDELAY; + } + else { + /* + * Don`t return from open until carrier detected. + */ + tp->t_state |= TS_WOPEN; + + ior->io_dev_ptr = (char *)tp; + + queue_delayed_reply(&tp->t_delayed_open, ior, char_open_done); + rc = D_IO_QUEUED; + goto out; + } + } + tp->t_state |= TS_ISOPEN; + if (tp->t_mctl) + (*tp->t_mctl)(tp, TM_RTS, DMBIS); +out: + simple_unlock_irq(s, &tp->t_lock); + return rc; +} + +/* + * Retry wait for CARR_ON for open. + * No locks may be held. + * May run on any CPU. + */ +boolean_t char_open_done( + io_req_t ior) +{ + struct tty *tp = (struct tty *)ior->io_dev_ptr; + spl_t s; + + s = simple_lock_irq(&tp->t_lock); + if ((tp->t_state & TS_ISOPEN) == 0) { + queue_delayed_reply(&tp->t_delayed_open, ior, char_open_done); + simple_unlock_irq(s, &tp->t_lock); + return FALSE; + } + + tp->t_state |= TS_ISOPEN; + tp->t_state &= ~TS_WOPEN; + + if (tp->t_mctl) + (*tp->t_mctl)(tp, TM_RTS, DMBIS); + + simple_unlock_irq(s, &tp->t_lock); + + ior->io_error = D_SUCCESS; + (void) ds_open_done(ior); + return TRUE; +} + +static boolean_t tty_close_open_reply( + io_req_t ior) +{ + ior->io_error = D_DEVICE_DOWN; + (void) ds_open_done(ior); + return TRUE; +} + +/* + * Write to TTY. + * No locks may be held. + * Calls device start routine; must already be on master if + * device needs to run on master. + */ +io_return_t char_write( + struct tty * tp, + io_req_t ior) +{ + spl_t s; + int count; + char *data; + vm_offset_t addr = 0; + io_return_t rc = D_SUCCESS; + + data = ior->io_data; + count = ior->io_count; + if (count == 0) + return rc; + + if (!(ior->io_op & IO_INBAND)) { + /* + * Copy out-of-line data into kernel address space. + * Since data is copied as page list, it will be + * accessible. + */ + vm_map_copy_t copy = (vm_map_copy_t) data; + kern_return_t kr; + + kr = vm_map_copyout(device_io_map, &addr, copy); + if (kr != KERN_SUCCESS) + return kr; + data = (char *) addr; + } + + /* + * Check for tty operating. + */ + s = simple_lock_irq(&tp->t_lock); + + if ((tp->t_state & TS_CARR_ON) == 0) { + + if ((tp->t_state & TS_ONDELAY) == 0) { + /* + * No delayed writes - tell caller that device is down + */ + rc = D_IO_ERROR; + goto out; + } + + if (ior->io_mode & D_NOWAIT) { + rc = D_WOULD_BLOCK; + goto out; + } + } + + /* + * Copy data into the output buffer. + * Report the amount not copied. + */ + + ior->io_residual = b_to_q(data, count, &tp->t_outq); + + /* + * Start hardware output. + */ + + tp->t_state &= ~TS_TTSTOP; + tty_output(tp); + + if (tp->t_outq.c_cc > TTHIWAT(tp) || + (tp->t_state & TS_CARR_ON) == 0) { + + /* + * Do not send reply until some characters have been sent. + */ + ior->io_dev_ptr = (char *)tp; + queue_delayed_reply(&tp->t_delayed_write, ior, char_write_done); + + rc = D_IO_QUEUED; + } +out: + simple_unlock_irq(s, &tp->t_lock); + + if (!(ior->io_op & IO_INBAND)) + (void) vm_deallocate(device_io_map, addr, ior->io_count); + return rc; +} + +/* + * Retry wait for output queue emptied, for write. + * No locks may be held. + * May run on any CPU. + */ +boolean_t char_write_done( + io_req_t ior) +{ + struct tty *tp = (struct tty *)ior->io_dev_ptr; + spl_t s; + + s = simple_lock_irq(&tp->t_lock); + if (tp->t_outq.c_cc > TTHIWAT(tp) || + (tp->t_state & TS_CARR_ON) == 0) { + + queue_delayed_reply(&tp->t_delayed_write, ior, char_write_done); + simple_unlock_irq(s, &tp->t_lock); + return FALSE; + } + simple_unlock_irq(s, &tp->t_lock); + + if (IP_VALID(ior->io_reply_port)) { + (void) (*((ior->io_op & IO_INBAND) ? + ds_device_write_reply_inband : + ds_device_write_reply))(ior->io_reply_port, + ior->io_reply_port_type, + ior->io_error, + (int) (ior->io_total - + ior->io_residual)); + } + mach_device_deallocate(ior->io_device); + return TRUE; +} + +static boolean_t tty_close_write_reply( + io_req_t ior) +{ + ior->io_residual = ior->io_count; + ior->io_error = D_DEVICE_DOWN; + (void) ds_write_done(ior); + return TRUE; +} + +/* + * Read from TTY. + * No locks may be held. + * May run on any CPU - does not talk to device driver. + */ +io_return_t char_read( + struct tty *tp, + io_req_t ior) +{ + spl_t s; + kern_return_t rc; + + /* + * Allocate memory for read buffer. + */ + rc = device_read_alloc(ior, (vm_size_t)ior->io_count); + if (rc != KERN_SUCCESS) + return rc; + + s = simple_lock_irq(&tp->t_lock); + if ((tp->t_state & TS_CARR_ON) == 0) { + + if ((tp->t_state & TS_ONDELAY) == 0) { + /* + * No delayed writes - tell caller that device is down + */ + rc = D_IO_ERROR; + goto out; + } + + if (ior->io_mode & D_NOWAIT) { + rc = D_WOULD_BLOCK; + goto out; + } + + } + + if (tp->t_inq.c_cc <= 0 || + (tp->t_state & TS_CARR_ON) == 0) { + + ior->io_dev_ptr = (char *)tp; + queue_delayed_reply(&tp->t_delayed_read, ior, char_read_done); + rc = D_IO_QUEUED; + goto out; + } + + ior->io_residual = ior->io_count - q_to_b(&tp->t_inq, + ior->io_data, + (int)ior->io_count); + if (tp->t_state & TS_RTS_DOWN) { + (*tp->t_mctl)(tp, TM_RTS, DMBIS); + tp->t_state &= ~TS_RTS_DOWN; + } + + out: + simple_unlock_irq(s, &tp->t_lock); + return rc; +} + +/* + * Retry wait for characters, for read. + * No locks may be held. + * May run on any CPU - does not talk to device driver. + */ +boolean_t char_read_done( + io_req_t ior) +{ + struct tty *tp = (struct tty *)ior->io_dev_ptr; + spl_t s; + + s = simple_lock_irq(&tp->t_lock); + + if (tp->t_inq.c_cc <= 0 || + (tp->t_state & TS_CARR_ON) == 0) { + + queue_delayed_reply(&tp->t_delayed_read, ior, char_read_done); + simple_unlock_irq(s, &tp->t_lock); + return FALSE; + } + + ior->io_residual = ior->io_count - q_to_b(&tp->t_inq, + ior->io_data, + (int)ior->io_count); + if (tp->t_state & TS_RTS_DOWN) { + (*tp->t_mctl)(tp, TM_RTS, DMBIS); + tp->t_state &= ~TS_RTS_DOWN; + } + + simple_unlock_irq(s, &tp->t_lock); + + (void) ds_read_done(ior); + return TRUE; +} + +static boolean_t tty_close_read_reply( + io_req_t ior) +{ + ior->io_residual = ior->io_count; + ior->io_error = D_DEVICE_DOWN; + (void) ds_read_done(ior); + return TRUE; +} + +/* + * Close the tty. + * Tty must be locked (at spltty). + * Iff modem control should run on master. + */ +void ttyclose( + struct tty *tp) +{ + io_req_t ior; + + /* + * Flush the read and write queues. Signal + * the open queue so that those waiting for open + * to complete will see that the tty is closed. + */ + while ((ior = (io_req_t)dequeue_head(&tp->t_delayed_read)) != 0) { + ior->io_done = tty_close_read_reply; + iodone(ior); + } + while ((ior = (io_req_t)dequeue_head(&tp->t_delayed_write)) != 0) { + ior->io_done = tty_close_write_reply; + iodone(ior); + } + while ((ior = (io_req_t)dequeue_head(&tp->t_delayed_open)) != 0) { + ior->io_done = tty_close_open_reply; + iodone(ior); + } + + /* Close down modem */ + if (tp->t_mctl) { + (*tp->t_mctl)(tp, TM_BRK|TM_RTS, DMBIC); + if ((tp->t_state&(TS_HUPCLS|TS_WOPEN)) || (tp->t_state&TS_ISOPEN)==0) + (*tp->t_mctl)(tp, TM_HUP, DMSET); + } + + /* only save buffering bit, and carrier */ + tp->t_state = tp->t_state & (TS_MIN|TS_CARR_ON); +} + +/* + * Port-death routine to clean up reply messages. + */ +static boolean_t +tty_queue_clean( + queue_t q, + const ipc_port_t port, + boolean_t (*routine)(io_req_t) ) +{ + io_req_t ior; + + ior = (io_req_t)queue_first(q); + while (!queue_end(q, (queue_entry_t)ior)) { + if (ior->io_reply_port == port) { + remqueue(q, (queue_entry_t)ior); + ior->io_done = routine; + iodone(ior); + return TRUE; + } + ior = ior->io_next; + } + return FALSE; +} + +/* + * Handle port-death (dead reply port) for tty. + * No locks may be held. + * May run on any CPU. + */ +boolean_t +tty_portdeath( + struct tty * tp, + const ipc_port_t port) +{ + spl_t spl; + boolean_t result; + + spl = simple_lock_irq(&tp->t_lock); + + /* + * The queues may never have been initialized + */ + if (tp->t_delayed_read.next == 0) { + result = FALSE; + } + else { + result = + tty_queue_clean(&tp->t_delayed_read, port, + tty_close_read_reply) + || tty_queue_clean(&tp->t_delayed_write, port, + tty_close_write_reply) + || tty_queue_clean(&tp->t_delayed_open, port, + tty_close_open_reply); + } + simple_unlock_irq(spl, &tp->t_lock); + + return result; +} + +/* + * Get TTY status. + * No locks may be held. + * May run on any CPU. + */ +io_return_t tty_get_status( + struct tty *tp, + dev_flavor_t flavor, + int * data, /* pointer to OUT array */ + natural_t *count) /* out */ +{ + spl_t s; + + switch (flavor) { + case TTY_STATUS: + { + struct tty_status *tsp = + (struct tty_status *) data; + + if (*count < TTY_STATUS_COUNT) + return (D_INVALID_OPERATION); + + s = simple_lock_irq(&tp->t_lock); + + tsp->tt_ispeed = tp->t_ispeed; + tsp->tt_ospeed = tp->t_ospeed; + tsp->tt_breakc = tp->t_breakc; + tsp->tt_flags = tp->t_flags; + if (tp->t_state & TS_HUPCLS) + tsp->tt_flags |= TF_HUPCLS; + + simple_unlock_irq(s, &tp->t_lock); + + *count = TTY_STATUS_COUNT; + break; + + } + default: + return D_INVALID_OPERATION; + } + return D_SUCCESS; +} + +/* + * Set TTY status. + * No locks may be held. + * Calls device start or stop routines; must already be on master if + * device needs to run on master. + */ +io_return_t tty_set_status( + struct tty *tp, + dev_flavor_t flavor, + int * data, + natural_t count) +{ + int s; + + switch (flavor) { + case TTY_FLUSH: + { + int flags; + if (count < TTY_FLUSH_COUNT) + return D_INVALID_OPERATION; + + flags = *data; + if (flags == 0) + flags = D_READ | D_WRITE; + + s = simple_lock_irq(&tp->t_lock); + tty_flush(tp, flags); + simple_unlock_irq(s, &tp->t_lock); + + break; + } + case TTY_STOP: + /* stop output */ + s = simple_lock_irq(&tp->t_lock); + if ((tp->t_state & TS_TTSTOP) == 0) { + tp->t_state |= TS_TTSTOP; + (*tp->t_stop)(tp, 0); + } + simple_unlock_irq(s, &tp->t_lock); + break; + + case TTY_START: + /* start output */ + s = simple_lock_irq(&tp->t_lock); + if (tp->t_state & TS_TTSTOP) { + tp->t_state &= ~TS_TTSTOP; + tty_output(tp); + } + simple_unlock_irq(s, &tp->t_lock); + break; + + case TTY_STATUS: + /* set special characters and speed */ + { + struct tty_status *tsp; + + if (count < TTY_STATUS_COUNT) + return D_INVALID_OPERATION; + + tsp = (struct tty_status *)data; + + if (tsp->tt_ispeed < 0 || + tsp->tt_ispeed >= NSPEEDS || + tsp->tt_ospeed < 0 || + tsp->tt_ospeed >= NSPEEDS) + { + return D_INVALID_OPERATION; + } + + s = simple_lock_irq(&tp->t_lock); + + tp->t_ispeed = tsp->tt_ispeed; + tp->t_ospeed = tsp->tt_ospeed; + tp->t_breakc = tsp->tt_breakc; + tp->t_flags = tsp->tt_flags & ~TF_HUPCLS; + if (tsp->tt_flags & TF_HUPCLS) + tp->t_state |= TS_HUPCLS; + + simple_unlock_irq(s, &tp->t_lock); + break; + } + default: + return D_INVALID_OPERATION; + } + return D_SUCCESS; +} + + +/* + * [internal] + * Queue IOR on reply queue, to wait for TTY operation. + * TTY must be locked (at spltty). + */ +void queue_delayed_reply( + queue_t qh, + io_req_t ior, + boolean_t (*io_done)(io_req_t) ) +{ + ior->io_done = io_done; + enqueue_tail(qh, (queue_entry_t)ior); +} + +/* + * Retry delayed IO operations for TTY. + * TTY containing queue must be locked (at spltty). + */ +void tty_queue_completion( + queue_t qh) +{ + io_req_t ior; + + while ((ior = (io_req_t)dequeue_head(qh)) != 0) { + iodone(ior); + } +} + +/* + * Set the default special characters. + * Since this routine is called whenever a tty has never been opened, + * we can initialize the queues here. + */ +void ttychars( + struct tty *tp) +{ + if ((tp->t_flags & TS_INIT) == 0) { + /* + * Initialize queues + */ + queue_init(&tp->t_delayed_open); + queue_init(&tp->t_delayed_read); + queue_init(&tp->t_delayed_write); + + /* + * Initialize character buffers + */ + cb_alloc(&tp->t_inq, tty_inq_size); + + /* if we might do modem flow control */ + if (tp->t_mctl && tp->t_inq.c_hog > 30) + tp->t_inq.c_hog -= 30; + + cb_alloc(&tp->t_outq, tty_outq_size); + + /* + * Mark initialized + */ + tp->t_state |= TS_INIT; + } + + tp->t_breakc = 0; +} + +/* + * Flush all TTY queues. + * Called at spltty, tty already locked. + * Calls device STOP routine; must already be on master if + * device needs to run on master. + */ +void tty_flush( + struct tty *tp, + int rw) +{ + if (rw & D_READ) { + cb_clear(&tp->t_inq); + tty_queue_completion(&tp->t_delayed_read); + } + if (rw & D_WRITE) { + tp->t_state &= ~TS_TTSTOP; + (*tp->t_stop)(tp, rw); + cb_clear(&tp->t_outq); + tty_queue_completion(&tp->t_delayed_write); + } +} + +/* + * Restart character output after a delay timeout. + * Calls device start routine - must be on master CPU. + * + * Timeout routines are called only on master CPU. + * What if device runs on a different CPU? + */ +void ttrstrt( + struct tty *tp) +{ + spl_t s; + + s = simple_lock_irq(&tp->t_lock); + + tp->t_state &= ~TS_TIMEOUT; + ttstart (tp); + + simple_unlock_irq(s, &tp->t_lock); +} + +/* + * Start output on the typewriter. It is used from the top half + * after some characters have been put on the output queue, + * from the interrupt routine to transmit the next + * character, and after a timeout has finished. + * + * Called at spltty, tty already locked. + * Must be on master CPU if device runs on master. + */ +void ttstart(struct tty *tp) +{ + if ((tp->t_state & (TS_TIMEOUT|TS_TTSTOP|TS_BUSY)) == 0) { + /* + * Start up the hardware again + */ + (*tp->t_start)(tp); + + /* + * Wake up those waiting for write completion. + */ + if (tp->t_outq.c_cc <= TTLOWAT(tp)) + tty_queue_completion(&tp->t_delayed_write); + } +} + +/* + * Start character output, if the device is not busy or + * stopped or waiting for a timeout. + * + * Called at spltty, tty already locked. + * Must be on master CPU if device runs on master. + */ +void tty_output( + struct tty *tp) +{ + if ((tp->t_state & (TS_TIMEOUT|TS_TTSTOP|TS_BUSY)) == 0) { + /* + * Not busy. Start output. + */ + (*tp->t_start)(tp); + + /* + * Wake up those waiting for write completion. + */ + if (tp->t_outq.c_cc <= TTLOWAT(tp)) + tty_queue_completion(&tp->t_delayed_write); + } +} + +/* + * Send any buffered recvd chars up to user + */ +static void ttypush(void * _tp) +{ + struct tty *tp = _tp; + spl_t s; + int state; + + s = simple_lock_irq(&tp->t_lock); + + /* + The pdma timeout has gone off. + If no character has been received since the timeout + was set, push any pending characters up. + If any characters were received in the last interval + then just reset the timeout and the character received bit. + */ + + state = tp->t_state; + + if (state & TS_MIN_TO) + { + if (state & TS_MIN_TO_RCV) + { /* a character was received */ + tp->t_state = state & ~TS_MIN_TO_RCV; + timeout(ttypush, tp, pdma_timeouts[tp->t_ispeed]); + } + else + { + tp->t_state = state & ~TS_MIN_TO; + if (tp->t_inq.c_cc) /* pending characters */ + tty_queue_completion(&tp->t_delayed_read); + } + } + else + { + tp->t_state = state & ~TS_MIN_TO_RCV;/* sanity */ + } + + simple_unlock_irq(s, &tp->t_lock); +} + +/* + * Put input character on input queue. + * + * Called at spltty, tty already locked. + */ +void ttyinput( + unsigned int c, + struct tty *tp) +{ + if (tp->t_inq.c_cc >= tp->t_inq.c_hog) { + /* + * Do not want to overflow input queue + */ + if (tp->t_mctl) { + (*tp->t_mctl)(tp, TM_RTS, DMBIC); + tp->t_state |= TS_RTS_DOWN; + } + tty_queue_completion(&tp->t_delayed_read); + return; + + } + + c &= 0xff; + + (void) putc(c, &tp->t_inq); + if ((tp->t_state & TS_MIN) == 0 || + tp->t_inq.c_cc > pdma_water_mark[tp->t_ispeed]) + { + /* + * No input buffering, or input minimum exceeded. + * Grab a request from input queue and queue it + * to io_done thread. + */ + if (tp->t_state & TS_MIN_TO) { + tp->t_state &= ~(TS_MIN_TO|TS_MIN_TO_RCV); + untimeout(ttypush, tp); + } + tty_queue_completion(&tp->t_delayed_read); + } + else { + /* + * Not enough characters. + * If no timeout is set, initiate the timeout + * Otherwise set the character received during timeout interval + * flag. + * One alternative approach would be just to reset the timeout + * into the future, but this involves making a timeout/untimeout + * call on every character. + */ + int ptime = pdma_timeouts[tp->t_ispeed]; + if (ptime > 0) + { + if ((tp->t_state & TS_MIN_TO) == 0) + { + tp->t_state |= TS_MIN_TO; + timeout(ttypush, tp, ptime); + } + else + { + tp->t_state |= TS_MIN_TO_RCV; + } + } + } +} + +/* + * Put many characters on input queue. + * + * Called at spltty, tty already locked. + */ +void ttyinput_many( + struct tty *tp, + char *chars, + int count) +{ + /* + * Do not want to overflow input queue + */ + if (tp->t_inq.c_cc < tp->t_inq.c_hog) + count -= b_to_q(chars, count, &tp->t_inq); + + tty_queue_completion(&tp->t_delayed_read); +} + + +/* + * Handle modem control transition on a tty. + * Flag indicates new state of carrier. + * Returns FALSE if the line should be turned off. + * + * Called at spltty, tty already locked. + */ +boolean_t ttymodem( + struct tty * tp, + boolean_t carrier_up) +{ + if ((tp->t_state&TS_WOPEN) == 0 && (tp->t_flags & MDMBUF)) { + /* + * Flow control by carrier. Carrier down stops + * output; carrier up restarts output. + */ + if (carrier_up) { + tp->t_state &= ~TS_TTSTOP; + tty_output(tp); + } + else if ((tp->t_state&TS_TTSTOP) == 0) { + tp->t_state |= TS_TTSTOP; + (*tp->t_stop)(tp, 0); + } + } + else if (carrier_up) { + /* + * Carrier now on. + */ + tp->t_state |= TS_CARR_ON; + tt_open_wakeup(tp); + } + else { + /* + * Lost carrier. + */ + tp->t_state &= ~TS_CARR_ON; + if (tp->t_state & TS_ISOPEN && + (tp->t_flags & NOHANG) == 0) + { + /* + * Hang up TTY if carrier drops. + * Need to alert users, somehow... + */ + tty_flush(tp, D_READ|D_WRITE); + return FALSE; + } + } + return TRUE; +} + +/* + * Similarly, handle transitions on the ClearToSend + * signal. Nowadays, it is used by many modems as + * a flow-control device: they turn it down to stop + * us from sending more chars. We do the same with + * the RequestToSend signal. [Yes, that is exactly + * why those signals are defined in the standard.] + * + * Tty must be locked and on master. + */ +void +tty_cts( + struct tty * tp, + boolean_t cts_up) +{ + if (tp->t_state & TS_ISOPEN){ + if (cts_up) { + tp->t_state &= ~(TS_TTSTOP|TS_BUSY); + tty_output(tp); + } else { + tp->t_state |= (TS_TTSTOP|TS_BUSY); + (*tp->t_stop)(tp, D_WRITE); + } + } +} diff --git a/device/chario.h b/device/chario.h new file mode 100644 index 0000000..52105a2 --- /dev/null +++ b/device/chario.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2013 Free Software Foundation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _DEVICE_CHARIO_H_ +#define _DEVICE_CHARIO_H_ + +#include + +extern void chario_init(void); + +void queue_delayed_reply( + queue_t qh, + io_req_t ior, + boolean_t (*io_done)(io_req_t)); + +void tty_output(struct tty *tp); + +boolean_t char_open_done(io_req_t); +boolean_t char_read_done(io_req_t); +boolean_t char_write_done(io_req_t); + +#endif /* _DEVICE_CHARIO_H_ */ diff --git a/device/cirbuf.c b/device/cirbuf.c new file mode 100644 index 0000000..ed09f3d --- /dev/null +++ b/device/cirbuf.c @@ -0,0 +1,277 @@ +/* + * Mach Operating System + * Copyright (c) 1992,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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 7/90 + * + * Circular buffers for TTY + */ + +#include +#include +#include +#include + + + +/* read at c_cf, write at c_cl */ +/* if c_cf == c_cl, buffer is empty */ +/* if c_cl == c_cf - 1, buffer is full */ + +#if DEBUG +#include + +boolean_t cb_check_enable = FALSE; +#define CB_CHECK(cb) if (cb_check_enable) cb_check(cb) + +void +cb_check(struct cirbuf *cb) +{ + if (!(cb->c_cf >= cb->c_start && cb->c_cf < cb->c_end)) + panic("cf %p out of range [%p..%p)", + cb->c_cf, cb->c_start, cb->c_end); + if (!(cb->c_cl >= cb->c_start && cb->c_cl < cb->c_end)) + panic("cl %p out of range [%p..%p)", + cb->c_cl, cb->c_start, cb->c_end); + if (cb->c_cf <= cb->c_cl) { + if (!(cb->c_cc == cb->c_cl - cb->c_cf)) + panic("cc %x should be %x", + cb->c_cc, + cb->c_cl - cb->c_cf); + } + else { + if (!(cb->c_cc == cb->c_end - cb->c_cf + + cb->c_cl - cb->c_start)) + panic("cc %x should be %x", + cb->c_cc, + cb->c_end - cb->c_cf + + cb->c_cl - cb->c_start); + } +} +#else /* DEBUG */ +#define CB_CHECK(cb) +#endif /* DEBUG */ + +/* + * Put one character in circular buffer. + */ +int putc( + int c, + struct cirbuf *cb) +{ + char *ow, *nw; + + ow = cb->c_cl; + nw = ow+1; + if (nw == cb->c_end) + nw = cb->c_start; + if (nw == cb->c_cf) + return 1; /* not entered */ + *ow = c; + cb->c_cl = nw; + + cb->c_cc++; + + CB_CHECK(cb); + + return 0; +} + +/* + * Get one character from circular buffer. + */ +int getc(struct cirbuf *cb) +{ + unsigned char *nr; + int c; + + nr = (unsigned char *)cb->c_cf; + if (nr == (unsigned char *)cb->c_cl) { + CB_CHECK(cb); + return -1; /* empty */ + } + c = *nr; + nr++; + if (nr == (unsigned char *)cb->c_end) + nr = (unsigned char *)cb->c_start; + cb->c_cf = (char *)nr; + + cb->c_cc--; + + CB_CHECK(cb); + + return c; +} + +/* + * Get lots of characters. + * Return number moved. + */ +int +q_to_b( struct cirbuf *cb, + char *cp, + int count) +{ + char *ocp = cp; + int i; + + while (count != 0) { + if (cb->c_cl == cb->c_cf) + break; /* empty */ + if (cb->c_cl < cb->c_cf) + i = cb->c_end - cb->c_cf; + else + i = cb->c_cl - cb->c_cf; + if (i > count) + i = count; + memcpy(cp, cb->c_cf, i); + cp += i; + count -= i; + cb->c_cf += i; + cb->c_cc -= i; + if (cb->c_cf == cb->c_end) + cb->c_cf = cb->c_start; + + CB_CHECK(cb); + } + CB_CHECK(cb); + + return cp - ocp; +} + +/* + * Add character array to buffer and return number of characters + * NOT entered. + */ +int +b_to_q( char *cp, + int count, + struct cirbuf *cb) +{ + int i; + char *lim; + + while (count != 0) { + lim = cb->c_cf - 1; + if (lim < cb->c_start) + lim = cb->c_end - 1; + + if (cb->c_cl == lim) + break; + if (cb->c_cl < lim) + i = lim - cb->c_cl; + else + i = cb->c_end - cb->c_cl; + + if (i > count) + i = count; + memcpy(cb->c_cl, cp, i); + cp += i; + count -= i; + cb->c_cc += i; + cb->c_cl += i; + if (cb->c_cl == cb->c_end) + cb->c_cl = cb->c_start; + + CB_CHECK(cb); + } + CB_CHECK(cb); + return count; +} + +/* + * Flush characters from circular buffer. + */ +void +ndflush(struct cirbuf *cb, + int count) +{ + int i; + + while (count != 0) { + if (cb->c_cl == cb->c_cf) + break; /* empty */ + if (cb->c_cl < cb->c_cf) + i = cb->c_end - cb->c_cf; + else + i = cb->c_cl - cb->c_cf; + if (i > count) + i = count; + count -= i; + cb->c_cf += i; + cb->c_cc -= i; + if (cb->c_cf == cb->c_end) + cb->c_cf = cb->c_start; + CB_CHECK(cb); + } + + CB_CHECK(cb); +} + +/* + * Empty a circular buffer. + */ +void cb_clear(struct cirbuf *cb) +{ + cb->c_cf = cb->c_start; + cb->c_cl = cb->c_start; + cb->c_cc = 0; +} + +/* + * Allocate character space for a circular buffer. + */ +void +cb_alloc( + struct cirbuf *cb, + vm_size_t buf_size) +{ + char *buf; + + buf = (char *)kalloc(buf_size); + + cb->c_start = buf; + cb->c_end = buf + buf_size; + cb->c_cf = buf; + cb->c_cl = buf; + cb->c_cc = 0; + cb->c_hog = buf_size - 1; + + CB_CHECK(cb); +} + +/* + * Free character space for a circular buffer. + */ +void +cb_free(struct cirbuf *cb) +{ + vm_size_t size; + + size = cb->c_end - cb->c_start; + kfree((vm_offset_t)cb->c_start, size); +} + diff --git a/device/cirbuf.h b/device/cirbuf.h new file mode 100644 index 0000000..64771ce --- /dev/null +++ b/device/cirbuf.h @@ -0,0 +1,61 @@ +/* + * 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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 7/90 + */ + +#ifndef _DEVICE_CIRBUF_H_ +#define _DEVICE_CIRBUF_H_ + +/* + * Circular buffers for TTY + */ + +struct cirbuf { + char * c_start; /* start of buffer */ + char * c_end; /* end of buffer + 1*/ + char * c_cf; /* read pointer */ + char * c_cl; /* write pointer */ + short c_cc; /* current number of characters + (compatibility) */ + short c_hog; /* max ever */ +}; + +/* + * Exported routines + */ +extern int putc(int, struct cirbuf *); +extern int getc(struct cirbuf *); +extern int q_to_b(struct cirbuf *, char *, int); +extern int b_to_q(char *, int, struct cirbuf *); +extern void ndflush(struct cirbuf *, int); +extern void cb_clear(struct cirbuf *); + +extern void cb_alloc(struct cirbuf *, vm_size_t); +extern void cb_free(struct cirbuf *); + +#endif /* _DEVICE_CIRBUF_H_ */ diff --git a/device/conf.h b/device/conf.h new file mode 100644 index 0000000..8177966 --- /dev/null +++ b/device/conf.h @@ -0,0 +1,127 @@ +/* + * Mach Operating System + * Copyright (c) 1993,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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 8/88 + */ + +#ifndef _DEVICE_CONF_H_ +#define _DEVICE_CONF_H_ + +#include +#include +#include +#include +#include +#include + +struct io_req; +typedef struct io_req *io_req_t; + +typedef int io_return_t; + +/* + * Operations list for major device types. + */ +struct dev_ops { + char * d_name; /* name for major device */ + int (*d_open)(dev_t, int, io_req_t);/* open device */ + void (*d_close)(dev_t, int); /* close device */ + int (*d_read)(dev_t, io_req_t); /* read */ + int (*d_write)(dev_t, io_req_t); /* write */ + int (*d_getstat)(dev_t, dev_flavor_t, dev_status_t, mach_msg_type_number_t *); /* get status/control */ + int (*d_setstat)(dev_t, dev_flavor_t, dev_status_t, mach_msg_type_number_t); /* set status/control */ + vm_offset_t (*d_mmap)(dev_t, vm_offset_t, vm_prot_t); /* map memory */ + int (*d_async_in)(dev_t, const ipc_port_t, int, filter_t*, unsigned int); /* asynchronous input setup */ + int (*d_reset)(dev_t); /* reset device */ + int (*d_port_death)(dev_t, mach_port_t); + /* clean up reply ports */ + int d_subdev; /* number of sub-devices per + unit */ + int (*d_dev_info)(dev_t, int, int*); /* driver info for kernel */ +}; +typedef struct dev_ops *dev_ops_t; + +/* + * Routines for null entries. + */ +extern int nulldev_reset(dev_t dev); +extern int nulldev_open(dev_t dev, int flag, io_req_t ior); +extern void nulldev_close(dev_t dev, int flags); +extern int nulldev_read(dev_t dev, io_req_t ior); +extern int nulldev_write(dev_t dev, io_req_t ior); +extern io_return_t nulldev_getstat(dev_t dev, dev_flavor_t flavor, dev_status_t data, mach_msg_type_number_t *count); +extern io_return_t nulldev_setstat(dev_t dev, dev_flavor_t flavor, dev_status_t data, mach_msg_type_number_t count); +extern io_return_t nulldev_portdeath(dev_t dev, mach_port_t port); +extern int nodev_async_in(dev_t, const ipc_port_t, int, filter_t*, unsigned int); /* no operation - error */ +extern int nodev_info(dev_t, int, int*); /* no operation - error */ +extern vm_offset_t nomap(dev_t dev, vm_offset_t off, int prot); /* no operation - error */ + +/* + * Flavor constants for d_dev_info routine + */ +#define D_INFO_BLOCK_SIZE 1 + +/* + * Head of list of attached devices + */ +extern struct dev_ops dev_name_list[]; +extern int dev_name_count; + +/* + * Macro to search device list + */ +#define dev_search(dp) \ + for (dp = dev_name_list; \ + dp < &dev_name_list[dev_name_count]; \ + dp++) + +/* + * Indirection vectors for certain devices. + */ +struct dev_indirect { + char * d_name; /* name for device */ + dev_ops_t d_ops; /* operations (major device) */ + int d_unit; /* and unit number */ +}; +typedef struct dev_indirect *dev_indirect_t; + +/* + * List of indirect devices. + */ +extern struct dev_indirect dev_indirect_list[]; +extern int dev_indirect_count; + +/* + * Macro to search indirect list + */ +#define dev_indirect_search(di) \ + for (di = dev_indirect_list; \ + di < &dev_indirect_list[dev_indirect_count]; \ + di++) + +#endif /* _DEVICE_CONF_H_ */ + diff --git a/device/cons.c b/device/cons.c new file mode 100644 index 0000000..3f7cb9d --- /dev/null +++ b/device/cons.c @@ -0,0 +1,183 @@ +/* + * Copyright (c) 1988-1994, The University of Utah and + * the Computer Systems Laboratory (CSL). All rights reserved. + * + * Permission to use, copy, modify and distribute this software is hereby + * granted provided that (1) source code retains these copyright, permission, + * and disclaimer notices, and (2) redistributions including binaries + * reproduce the notices in supporting documentation, and (3) all advertising + * materials mentioning features or use of this software display the following + * acknowledgement: ``This product includes software developed by the + * Computer Systems Laboratory at the University of Utah.'' + * + * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS + * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF + * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * CSL requests users of this software to return to csl-dist@cs.utah.edu any + * improvements that they make and grant CSL redistribution rights. + * + * Utah $Hdr: cons.c 1.14 94/12/14$ + */ + +#include +#include +#include +#include +#include +#include + +#ifdef MACH_KMSG +#include +#include +#endif /* MACH_KMSG */ + +static boolean_t cn_inited = FALSE; +static struct consdev *cn_tab = 0; /* physical console device info */ + +/* + * ROM getc/putc primitives. + * On some architectures, the boot ROM provides basic character input/output + * routines that can be used before devices are configured or virtual memory + * is enabled. This can be useful to debug (or catch panics from) code early + * in the bootstrap procedure. + */ +int (*romgetc)(char c) = 0; +void (*romputc)(char c) = 0; + +#if CONSBUFSIZE > 0 +/* + * Temporary buffer to store console output before a console is selected. + * This is statically allocated so it can be called before malloc/kmem_alloc + * have been initialized. It is initialized so it won't be clobbered as + * part of the zeroing of BSS (on PA/Mach). + */ +static char consbuf[CONSBUFSIZE] = { 0 }; +static char *consbp = consbuf; +static boolean_t consbufused = FALSE; +#endif /* CONSBUFSIZE > 0 */ + +void +cninit(void) +{ + struct consdev *cp; + dev_ops_t cn_ops; + int x; + + if (cn_inited) + return; + + /* + * 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; + } + + /* + * Found a console, initialize it. + */ + if ((cp = cn_tab)) { + /* + * Initialize as console + */ + (*cp->cn_init)(cp); + /* + * Look up its dev_ops pointer in the device table and + * place it in the device indirection table. + */ + if (dev_name_lookup(cp->cn_name, &cn_ops, &x) == FALSE) + panic("cninit: dev_name_lookup failed"); + dev_set_indirection("console", cn_ops, minor(cp->cn_dev)); +#if CONSBUFSIZE > 0 + /* + * Now that the console is initialized, dump any chars in + * the temporary console buffer. + */ + if (consbufused) { + char *cbp = consbp; + do { + if (*cbp) + cnputc(*cbp); + if (++cbp == &consbuf[CONSBUFSIZE]) + cbp = consbuf; + } while (cbp != consbp); + consbufused = FALSE; + } +#endif /* CONSBUFSIZE > 0 */ + cn_inited = TRUE; + return; + } + /* + * No console device found, not a problem for BSD, fatal for Mach + */ + panic("can't find a console device"); +} + + +int +cngetc(void) +{ + if (cn_tab) + return ((*cn_tab->cn_getc)(cn_tab->cn_dev, 1)); + if (romgetc) + return ((*romgetc)(1)); + return (0); +} + +int +cnmaygetc(void) +{ + if (cn_tab) + return((*cn_tab->cn_getc)(cn_tab->cn_dev, 0)); + if (romgetc) + return ((*romgetc)(0)); + return (0); +} + +void +cnputc(char c) +{ + if (c == 0) + return; + +#ifdef MACH_KMSG + /* XXX: Assume that All output routines always use cnputc. */ + kmsg_putchar (c); +#endif + +#if defined(MACH_HYP) && 0 + { + /* Also output on hypervisor's emergency console, for + * debugging */ + unsigned char d = c; + hyp_console_write(&d, 1); + } +#endif /* MACH_HYP */ + + if (cn_tab) { + (*cn_tab->cn_putc)(cn_tab->cn_dev, c); + if (c == '\n') + (*cn_tab->cn_putc)(cn_tab->cn_dev, '\r'); + } else if (romputc) { + (*romputc)(c); + if (c == '\n') + (*romputc)('\r'); + } +#if CONSBUFSIZE > 0 + else { + if (consbufused == FALSE) { + consbp = consbuf; + consbufused = TRUE; + memset(consbuf, 0, CONSBUFSIZE); + } + *consbp++ = c; + if (consbp >= &consbuf[CONSBUFSIZE]) + consbp = consbuf; + } +#endif /* CONSBUFSIZE > 0 */ +} diff --git a/device/cons.h b/device/cons.h new file mode 100644 index 0000000..34f3bc5 --- /dev/null +++ b/device/cons.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 1988-1994, The University of Utah and + * the Computer Systems Laboratory at the University of Utah (CSL). + * All rights reserved. + * + * Permission to use, copy, modify and distribute this software is hereby + * granted provided that (1) source code retains these copyright, permission, + * and disclaimer notices, and (2) redistributions including binaries + * reproduce the notices in supporting documentation, and (3) all advertising + * materials mentioning features or use of this software display the following + * acknowledgement: ``This product includes software developed by the + * Computer Systems Laboratory at the University of Utah.'' + * + * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS + * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF + * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * CSL requests users of this software to return to csl-dist@cs.utah.edu any + * improvements that they make and grant CSL redistribution rights. + * + * Utah $Hdr: cons.h 1.10 94/12/14$ + */ + +#ifndef _DEVICE_CONS_H +#define _DEVICE_CONS_H +#include + +struct consdev { + char *cn_name; /* name of device in dev_name_list */ + int (*cn_probe)(struct consdev *cp); /* probe hardware and fill in consdev info */ + int (*cn_init)(struct consdev *cp); /* turn on as console */ + int (*cn_getc)(dev_t dev, int wait); /* kernel getchar interface */ + int (*cn_putc)(dev_t dev, int c); /* kernel putchar interface */ + 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 */ + +#define CONSBUFSIZE 1024 + +#ifdef KERNEL +extern struct consdev constab[]; +#endif + +extern void cninit(void); + +extern int cngetc(void); + +extern int cnmaygetc(void); + +extern void cnputc(char); + +/* + * ROM getc/putc primitives. + * On some architectures, the boot ROM provides basic character input/output + * routines that can be used before devices are configured or virtual memory + * is enabled. This can be useful to debug (or catch panics from) code early + * in the bootstrap procedure. + */ +extern int (*romgetc)(char c); +extern void (*romputc)(char c); + +#endif /* _DEVICE_CONS_H */ diff --git a/device/dev_hdr.h b/device/dev_hdr.h new file mode 100644 index 0000000..ac6ce7e --- /dev/null +++ b/device/dev_hdr.h @@ -0,0 +1,160 @@ +/* + * 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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 3/89 + */ + +/* + * Mach device emulation definitions (i386at version). + * + * Copyright (c) 1996 The University of Utah and + * the Computer Systems Laboratory at the University of Utah (CSL). + * All rights reserved. + * + * Permission to use, copy, modify and distribute this software is hereby + * granted provided that (1) source code retains these copyright, permission, + * and disclaimer notices, and (2) redistributions including binaries + * reproduce the notices in supporting documentation, and (3) all advertising + * materials mentioning features or use of this software display the following + * acknowledgement: ``This product includes software developed by the + * Computer Systems Laboratory at the University of Utah.'' + * + * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS + * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF + * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * CSL requests users of this software to return to csl-dist@cs.utah.edu any + * improvements that they make and grant CSL redistribution rights. + * + * Author: Shantanu Goel, University of Utah CSL + */ + +#ifndef _DEVICE_DEV_HDR_H_ +#define _DEVICE_DEV_HDR_H_ + +#include +#include +#include +#include + +typedef struct dev_ops *dev_ops_t; + +/* This structure is associated with each open device port. + The port representing the device points to this structure. */ +struct device +{ + struct device_emulation_ops *emul_ops; + void *emul_data; +}; + +typedef struct device *device_t; + +#define DEVICE_NULL ((device_t) 0) + +/* + * Generic device header. May be allocated with the device, + * or built when the device is opened. + */ +struct mach_device { + decl_simple_lock_data(,ref_lock)/* lock for reference count */ + int ref_count; /* reference count */ + decl_simple_lock_data(, lock) /* lock for rest of state */ + short state; /* state: */ +#define DEV_STATE_INIT 0 /* not open */ +#define DEV_STATE_OPENING 1 /* being opened */ +#define DEV_STATE_OPEN 2 /* open */ +#define DEV_STATE_CLOSING 3 /* being closed */ + short flag; /* random flags: */ +#define D_EXCL_OPEN 0x0001 /* open only once */ + short open_count; /* number of times open */ + short io_in_progress; /* number of IOs in progress */ + boolean_t io_wait; /* someone waiting for IO to finish */ + + struct ipc_port *port; /* open port */ + queue_chain_t number_chain; /* chain for lookup by number */ + int dev_number; /* device number */ + int bsize; /* replacement for DEV_BSIZE */ + struct dev_ops *dev_ops; /* and operations vector */ + struct device dev; /* the real device structure */ +}; +typedef struct mach_device *mach_device_t; +#define MACH_DEVICE_NULL ((mach_device_t)0) + +/* + * To find and remove device entries + */ +mach_device_t device_lookup(const char *); /* by name */ + +void mach_device_reference(mach_device_t); +void mach_device_deallocate(mach_device_t); + +/* + * To find and remove port-to-device mappings + */ +device_t dev_port_lookup(ipc_port_t); +void dev_port_enter(mach_device_t); +void dev_port_remove(mach_device_t); + +typedef boolean_t (*dev_map_fn)(mach_device_t, mach_port_t); + +/* + * To call a routine on each device + */ +boolean_t dev_map(dev_map_fn, mach_port_t); + +/* + * To lock and unlock state and open-count + */ +#define device_lock(device) simple_lock(&(device)->lock) +#define device_unlock(device) simple_unlock(&(device)->lock) + +/* + * device name lookup + */ +extern boolean_t dev_name_lookup( + const char * name, + dev_ops_t *ops, /* out */ + int *unit); /* out */ + +/* + * Change an entry in the indirection list. + */ +extern void dev_set_indirection( + const char *name, + dev_ops_t ops, + int unit); + +/* + * compare device name + */ +extern boolean_t __attribute__ ((pure)) +name_equal( + const char *src, + int len, + const char *target); + +#endif /* _DEVICE_DEV_HDR_H_ */ diff --git a/device/dev_lookup.c b/device/dev_lookup.c new file mode 100644 index 0000000..c9c39f8 --- /dev/null +++ b/device/dev_lookup.c @@ -0,0 +1,364 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988 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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 3/89 + */ + +#include +#include + +#include +#include + +#include +#include +#include +#include /* DEV_BSIZE, as default */ + +#include +#include + +#include +#include + +/* + * Device structure routines: reference counting, port->device. + */ + +/* + * Lookup/enter by device number. + */ +#define NDEVHASH 8 +#define DEV_NUMBER_HASH(dev) ((dev) & (NDEVHASH-1)) +queue_head_t dev_number_hash_table[NDEVHASH]; + +/* + * Lock for device-number to device lookup. + * Must be held before device-ref_count lock. + */ +def_simple_lock_data(static, dev_number_lock) + +struct kmem_cache dev_hdr_cache; + +/* + * Enter device in the number lookup table. + * The number table lock must be held. + */ +static void +dev_number_enter(const mach_device_t device) +{ + queue_t q; + + q = &dev_number_hash_table[DEV_NUMBER_HASH(device->dev_number)]; + queue_enter(q, device, mach_device_t, number_chain); +} + +/* + * Remove device from the device-number lookup table. + * The device-number table lock must be held. + */ +static void +dev_number_remove(const mach_device_t device) +{ + queue_t q; + + q = &dev_number_hash_table[DEV_NUMBER_HASH(device->dev_number)]; + queue_remove(q, device, mach_device_t, number_chain); +} + +/* + * Lookup a device by device operations and minor number. + * The number table lock must be held. + */ +static mach_device_t +dev_number_lookup(const dev_ops_t ops, int devnum) +{ + queue_t q; + mach_device_t device; + + q = &dev_number_hash_table[DEV_NUMBER_HASH(devnum)]; + queue_iterate(q, device, mach_device_t, number_chain) { + if (device->dev_ops == ops && device->dev_number == devnum) { + return (device); + } + } + return (MACH_DEVICE_NULL); +} + +/* + * Look up a device by name, and create the device structure + * if it does not exist. Enter it in the dev_number lookup + * table. + */ +mach_device_t +device_lookup(const char *name) +{ + dev_ops_t dev_ops; + int dev_minor; + mach_device_t device; + mach_device_t new_device; + + /* + * Get the device and unit number from the name. + */ + if (!dev_name_lookup(name, &dev_ops, &dev_minor)) + return (MACH_DEVICE_NULL); + + /* + * Look up the device in the hash table. If it is + * not there, enter it. + */ + new_device = MACH_DEVICE_NULL; + simple_lock(&dev_number_lock); + while ((device = dev_number_lookup(dev_ops, dev_minor)) + == MACH_DEVICE_NULL) { + /* + * Must unlock to allocate the structure. If + * the structure has appeared after we have allocated, + * release the new structure. + */ + if (new_device != MACH_DEVICE_NULL) + break; /* allocated */ + + simple_unlock(&dev_number_lock); + + new_device = (mach_device_t) kmem_cache_alloc(&dev_hdr_cache); + simple_lock_init(&new_device->ref_lock); + new_device->ref_count = 1; + simple_lock_init(&new_device->lock); + new_device->state = DEV_STATE_INIT; + new_device->flag = 0; + new_device->open_count = 0; + new_device->io_in_progress = 0; + new_device->io_wait = FALSE; + new_device->port = IP_NULL; + new_device->dev_ops = dev_ops; + new_device->dev_number = dev_minor; + new_device->bsize = DEV_BSIZE; /* change later */ + + simple_lock(&dev_number_lock); + } + + if (device == MACH_DEVICE_NULL) { + /* + * No existing device structure. Insert the + * new one. + */ + assert(new_device != MACH_DEVICE_NULL); + device = new_device; + + dev_number_enter(device); + simple_unlock(&dev_number_lock); + } + else { + /* + * Have existing device. + */ + mach_device_reference(device); + simple_unlock(&dev_number_lock); + + if (new_device != MACH_DEVICE_NULL) + kmem_cache_free(&dev_hdr_cache, (vm_offset_t)new_device); + } + + return (device); +} + +/* + * Add a reference to the device. + */ +void +mach_device_reference(mach_device_t device) +{ + simple_lock(&device->ref_lock); + device->ref_count++; + simple_unlock(&device->ref_lock); +} + +/* + * Remove a reference to the device, and deallocate the + * structure if no references are left. + */ +void +mach_device_deallocate(mach_device_t device) +{ + simple_lock(&device->ref_lock); + if (--device->ref_count > 0) { + simple_unlock(&device->ref_lock); + return; + } + device->ref_count = 1; + simple_unlock(&device->ref_lock); + + simple_lock(&dev_number_lock); + simple_lock(&device->ref_lock); + if (--device->ref_count > 0) { + simple_unlock(&device->ref_lock); + simple_unlock(&dev_number_lock); + return; + } + + dev_number_remove(device); + simple_unlock(&device->ref_lock); + simple_unlock(&dev_number_lock); + + kmem_cache_free(&dev_hdr_cache, (vm_offset_t)device); +} + +/* + + */ +/* + * port-to-device lookup routines. + */ + +/* + * Enter a port-to-device mapping. + */ +void +dev_port_enter(mach_device_t device) +{ + mach_device_reference(device); + + ipc_kobject_set(device->port, + (ipc_kobject_t) &device->dev, IKOT_DEVICE); + device->dev.emul_data = device; + { + extern struct device_emulation_ops mach_device_emulation_ops; + + device->dev.emul_ops = &mach_device_emulation_ops; + } +} + +/* + * Remove a port-to-device mapping. + */ +void +dev_port_remove(mach_device_t device) +{ + ipc_kobject_set(device->port, IKO_NULL, IKOT_NONE); + mach_device_deallocate(device); +} + +/* + * Lookup a device by its port. + * Doesn't consume the naked send right; produces a device reference. + */ +device_t +dev_port_lookup(ipc_port_t port) +{ + device_t device; + + if (!IP_VALID(port)) + return (DEVICE_NULL); + + ip_lock(port); + if (ip_active(port) && (ip_kotype(port) == IKOT_DEVICE)) { + device = (device_t) port->ip_kobject; + if (device->emul_ops->reference) + (*device->emul_ops->reference)(device->emul_data); + } + else + device = DEVICE_NULL; + + ip_unlock(port); + return (device); +} + +/* + * Get the port for a device. + * Consumes a device reference; produces a naked send right. + */ +ipc_port_t +convert_device_to_port(const device_t device) +{ + if (device == DEVICE_NULL) + return IP_NULL; + + return (*device->emul_ops->dev_to_port) (device->emul_data); +} + +/* + * Call a supplied routine on each device, passing it + * the port as an argument. If the routine returns TRUE, + * stop the search and return TRUE. If none returns TRUE, + * return FALSE. + */ +boolean_t +dev_map( + dev_map_fn routine, + mach_port_t port) +{ + int i; + queue_t q; + mach_device_t dev, prev_dev; + + for (i = 0, q = &dev_number_hash_table[0]; + i < NDEVHASH; + i++, q++) { + prev_dev = MACH_DEVICE_NULL; + simple_lock(&dev_number_lock); + queue_iterate(q, dev, mach_device_t, number_chain) { + mach_device_reference(dev); + simple_unlock(&dev_number_lock); + if (prev_dev != MACH_DEVICE_NULL) + mach_device_deallocate(prev_dev); + + if ((*routine)(dev, port)) { + /* + * Done + */ + mach_device_deallocate(dev); + return (TRUE); + } + + simple_lock(&dev_number_lock); + prev_dev = dev; + } + simple_unlock(&dev_number_lock); + if (prev_dev != MACH_DEVICE_NULL) + mach_device_deallocate(prev_dev); + } + return (FALSE); +} + +/* + * Initialization + */ +void +dev_lookup_init(void) +{ + int i; + + simple_lock_init(&dev_number_lock); + + for (i = 0; i < NDEVHASH; i++) + queue_init(&dev_number_hash_table[i]); + + kmem_cache_init(&dev_hdr_cache, "mach_device", + sizeof(struct mach_device), 0, NULL, 0); +} diff --git a/device/dev_master.h b/device/dev_master.h new file mode 100644 index 0000000..70d4c63 --- /dev/null +++ b/device/dev_master.h @@ -0,0 +1,65 @@ +/* + * 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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 11/89 + * + * Bind an IO operation to the master CPU. + */ + +#ifndef _DEVICE_DEV_MASTER_H_ +#define _DEVICE_DEV_MASTER_H_ + +#include + +#if NCPUS > 1 + +#include +#include +#include +#include +#include + +#define io_grab_master() \ + MACRO_BEGIN \ + thread_bind(current_thread(), master_processor); \ + if (current_processor() != master_processor) \ + thread_block((void (*)()) 0); \ + MACRO_END + +#define io_release_master() \ + MACRO_BEGIN \ + thread_bind(current_thread(), PROCESSOR_NULL); \ + MACRO_END + +#else NCPUS > 1 + +#define io_grab_master() +#define io_release_master() + +#endif NCPUS > 1 + +#endif /* _DEVICE_DEV_MASTER_H_ */ diff --git a/device/dev_name.c b/device/dev_name.c new file mode 100644 index 0000000..abd525c --- /dev/null +++ b/device/dev_name.c @@ -0,0 +1,242 @@ +/* + * 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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 8/89 + */ + +#include +#include +#include +#include +#include + + + +/* + * Routines placed in empty entries in the device tables + */ +int nulldev_reset(dev_t dev) +{ + return (D_SUCCESS); +} + +int nulldev_open(dev_t dev, int flags, io_req_t ior) +{ + return (D_SUCCESS); +} + +void nulldev_close(dev_t dev, int flags) +{ +} + +int nulldev_read(dev_t dev, io_req_t ior) +{ + return (D_SUCCESS); +} + +int nulldev_write(dev_t dev, io_req_t ior) +{ + return (D_SUCCESS); +} + +io_return_t nulldev_getstat(dev_t dev, dev_flavor_t flavor, dev_status_t data, mach_msg_type_number_t *count) +{ + return (D_SUCCESS); +} + +io_return_t nulldev_setstat(dev_t dev, dev_flavor_t flavor, dev_status_t data, mach_msg_type_number_t count) +{ + return (D_SUCCESS); +} + +int nulldev_portdeath(dev_t dev, mach_port_t port) +{ + return (D_SUCCESS); +} + +int nodev_async_in(dev_t dev, const ipc_port_t port, int x, filter_t* filter, unsigned int j) +{ + return (D_INVALID_OPERATION); +} + +int nodev_info(dev_t dev, int a, int* b) +{ + return (D_INVALID_OPERATION); +} + +vm_offset_t +nomap(dev_t dev, vm_offset_t off, int prot) +{ + return -1; +} + +/* + * Name comparison routine. + * Compares first 'len' characters of 'src' + * with 'target', which is zero-terminated. + * Returns TRUE if strings are equal: + * src and target are equal in first 'len' characters + * next character of target is 0 (end of string). + */ +boolean_t __attribute__ ((pure)) +name_equal(const char *src, + int len, + const char *target) +{ + while (--len >= 0) + if (*src++ != *target++) + return FALSE; + return *target == 0; +} + +/* + * device name lookup + */ +boolean_t dev_name_lookup( + const char *name, + dev_ops_t *ops, /* out */ + int *unit) /* out */ +{ + /* + * Assume that block device names are of the form + * + * [[]] + * + * where + * is the name in the device table + * is an integer + * * is 's' followed by a number (disks only!) + * is a letter in [a-h] (disks only?) + */ + + const char *cp = name; + int len; + int j = 0; + int c; + dev_ops_t dev; + boolean_t found; + + int slice_num = 0; + + /* + * Find device type name (characters before digit) + */ + while ((c = *cp) != '\0' && + !(c >= '0' && c <= '9')) + cp++; + + len = cp - name; + if (c != '\0') { + /* + * Find unit number + */ + while ((c = *cp) != '\0' && + c >= '0' && c <= '9') { + j = j * 10 + (c - '0'); + cp++; + } + } + + found = FALSE; + dev_search(dev) { + if (name_equal(name, len, dev->d_name)) { + found = TRUE; + break; + } + } + if (!found) { + /* name not found - try indirection list */ + dev_indirect_t di; + + dev_indirect_search(di) { + if (name_equal(name, len, di->d_name)) { + /* + * Return device and unit from indirect vector. + */ + *ops = di->d_ops; + *unit = di->d_unit; + return (TRUE); + } + } + /* Not found in either list. */ + return (FALSE); + } + + *ops = dev; + *unit = j; + + /* + * Find sub-device number + */ + + j = dev->d_subdev; + if (j > 0) { + /* if no slice string, slice num = 0 */ + + /* *unit + *16 -- I know it's bad */ + *unit *= j; + + /* find slice ? */ + if (c == 's') { + cp++; + while ((c = *cp) != '\0' && + c >= '0' && c <= '9') { + slice_num = slice_num * 10 + (c - '0'); + cp++; + } + } + + *unit += (slice_num << 4); + /* if slice==0, it is either compatibility or whole device */ + + if (c >= 'a' && c < 'a' + j) { /* note: w/o this -> whole slice */ + /* + * Minor number is *unit + letter. + * NOW it is slice result + letter + */ + *unit += (c - 'a' +1); + } + } + return (TRUE); +} + +/* + * Change an entry in the indirection list. + */ +void +dev_set_indirection(const char *name, dev_ops_t ops, int unit) +{ + dev_indirect_t di; + + dev_indirect_search(di) { + if (!strcmp(di->d_name, name)) { + di->d_ops = ops; + di->d_unit = unit; + break; + } + } +} diff --git a/device/dev_pager.c b/device/dev_pager.c new file mode 100644 index 0000000..1cd7406 --- /dev/null +++ b/device/dev_pager.c @@ -0,0 +1,662 @@ +/* + * Mach Operating System + * Copyright (c) 1993-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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 3/89 + * + * Device pager. + */ + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * The device pager routines are called directly from the message + * system (via mach_msg), and thus run in the kernel-internal + * environment. All ports are in internal form (ipc_port_t), + * and must be correctly reference-counted in order to be saved + * in other data structures. Kernel routines may be called + * directly. Kernel types are used for data objects (tasks, + * memory objects, ports). The only IPC routines that may be + * called are ones that masquerade as the kernel task (via + * msg_send_from_kernel). + * + * Port rights and references are maintained as follows: + * Memory object port: + * The device_pager task has all rights. + * Memory object control port: + * The device_pager task has only send rights. + * Memory object name port: + * The device_pager task has only send rights. + * The name port is not even recorded. + * Regardless how the object is created, the control and name + * ports are created by the kernel and passed through the memory + * management interface. + * + * The device_pager assumes that access to its memory objects + * will not be propagated to more that one host, and therefore + * provides no consistency guarantees beyond those made by the + * kernel. + * + * In the event that more than one host attempts to use a device + * memory object, the device_pager will only record the last set + * of port names. [This can happen with only one host if a new + * mapping is being established while termination of all previous + * mappings is taking place.] Currently, the device_pager assumes + * that its clients adhere to the initialization and termination + * protocols in the memory management interface; otherwise, port + * rights or out-of-line memory from erroneous messages may be + * allowed to accumulate. + * + * [The phrase "currently" has been used above to denote aspects of + * the implementation that could be altered without changing the rest + * of the basic documentation.] + */ + +/* + * Basic device pager structure. + */ +struct dev_pager { + decl_simple_lock_data(, lock) /* lock for reference count */ + int ref_count; /* reference count */ + int client_count; /* How many memory_object_create + * calls have we received */ + ipc_port_t pager; /* pager port */ + ipc_port_t pager_request; /* Known request port */ + ipc_port_t pager_name; /* Known name port */ + mach_device_t device; /* Device handle */ + vm_offset_t offset; /* offset within the pager, in bytes*/ + int type; /* to distinguish */ +#define DEV_PAGER_TYPE 0 +#define CHAR_PAGER_TYPE 1 + /* char pager specifics */ + int prot; + vm_size_t size; +}; +typedef struct dev_pager *dev_pager_t; +#define DEV_PAGER_NULL ((dev_pager_t)0) + + +struct kmem_cache dev_pager_cache; + +static void dev_pager_reference(dev_pager_t ds) +{ + simple_lock(&ds->lock); + ds->ref_count++; + simple_unlock(&ds->lock); +} + +static void dev_pager_deallocate(dev_pager_t ds) +{ + simple_lock(&ds->lock); + if (--ds->ref_count > 0) { + simple_unlock(&ds->lock); + return; + } + + simple_unlock(&ds->lock); + kmem_cache_free(&dev_pager_cache, (vm_offset_t)ds); +} + +/* + * A hash table of ports for device_pager backed objects. + */ + +#define DEV_HASH_COUNT 127 + +struct dev_pager_entry { + queue_chain_t links; + ipc_port_t name; + dev_pager_t pager_rec; +}; +typedef struct dev_pager_entry *dev_pager_entry_t; + +/* + * Indexed by port name, each element contains a queue of all dev_pager_entry_t + * which name shares the same hash + */ +queue_head_t dev_pager_hashtable[DEV_HASH_COUNT]; +struct kmem_cache dev_pager_hash_cache; +def_simple_lock_data(static, dev_pager_hash_lock) + +struct dev_device_entry { + queue_chain_t links; + mach_device_t device; + vm_offset_t offset; + dev_pager_t pager_rec; +}; +typedef struct dev_device_entry *dev_device_entry_t; + +/* + * Indexed by device + offset, each element contains a queue of all + * dev_device_entry_t which device + offset shares the same hash + */ +queue_head_t dev_device_hashtable[DEV_HASH_COUNT]; +struct kmem_cache dev_device_hash_cache; +def_simple_lock_data(static, dev_device_hash_lock) + +#define dev_hash(name_port) \ + (((vm_offset_t)(name_port) & 0xffffff) % DEV_HASH_COUNT) + +static void dev_pager_hash_init(void) +{ + int i; + vm_size_t size; + + size = sizeof(struct dev_pager_entry); + kmem_cache_init(&dev_pager_hash_cache, "dev_pager_entry", size, 0, + NULL, 0); + for (i = 0; i < DEV_HASH_COUNT; i++) + queue_init(&dev_pager_hashtable[i]); + simple_lock_init(&dev_pager_hash_lock); +} + +static void dev_pager_hash_insert( + const ipc_port_t name_port, + const dev_pager_t rec) +{ + dev_pager_entry_t new_entry; + + new_entry = (dev_pager_entry_t) kmem_cache_alloc(&dev_pager_hash_cache); + new_entry->name = name_port; + new_entry->pager_rec = rec; + + simple_lock(&dev_pager_hash_lock); + queue_enter(&dev_pager_hashtable[dev_hash(name_port)], + new_entry, dev_pager_entry_t, links); + simple_unlock(&dev_pager_hash_lock); +} + +static void dev_pager_hash_delete(const ipc_port_t name_port) +{ + queue_t bucket; + dev_pager_entry_t entry; + + bucket = &dev_pager_hashtable[dev_hash(name_port)]; + + simple_lock(&dev_pager_hash_lock); + for (entry = (dev_pager_entry_t)queue_first(bucket); + !queue_end(bucket, &entry->links); + entry = (dev_pager_entry_t)queue_next(&entry->links)) { + if (entry->name == name_port) { + queue_remove(bucket, entry, dev_pager_entry_t, links); + break; + } + } + simple_unlock(&dev_pager_hash_lock); + if (!queue_end(bucket, &entry->links)) + kmem_cache_free(&dev_pager_hash_cache, (vm_offset_t)entry); +} + +static dev_pager_t dev_pager_hash_lookup(const ipc_port_t name_port) +{ + queue_t bucket; + dev_pager_entry_t entry; + dev_pager_t pager; + + bucket = &dev_pager_hashtable[dev_hash(name_port)]; + + simple_lock(&dev_pager_hash_lock); + for (entry = (dev_pager_entry_t)queue_first(bucket); + !queue_end(bucket, &entry->links); + entry = (dev_pager_entry_t)queue_next(&entry->links)) { + if (entry->name == name_port) { + pager = entry->pager_rec; + dev_pager_reference(pager); + simple_unlock(&dev_pager_hash_lock); + return (pager); + } + } + simple_unlock(&dev_pager_hash_lock); + return (DEV_PAGER_NULL); +} + +static void dev_device_hash_init(void) +{ + int i; + vm_size_t size; + + size = sizeof(struct dev_device_entry); + kmem_cache_init(&dev_device_hash_cache, "dev_device_entry", size, 0, + NULL, 0); + for (i = 0; i < DEV_HASH_COUNT; i++) { + queue_init(&dev_device_hashtable[i]); + } + simple_lock_init(&dev_device_hash_lock); +} + +static void dev_device_hash_insert( + const mach_device_t device, + const vm_offset_t offset, + const dev_pager_t rec) +{ + dev_device_entry_t new_entry; + + new_entry = (dev_device_entry_t) kmem_cache_alloc(&dev_device_hash_cache); + new_entry->device = device; + new_entry->offset = offset; + new_entry->pager_rec = rec; + + simple_lock(&dev_device_hash_lock); + queue_enter(&dev_device_hashtable[dev_hash(device + offset)], + new_entry, dev_device_entry_t, links); + simple_unlock(&dev_device_hash_lock); +} + +static void dev_device_hash_delete( + const mach_device_t device, + const vm_offset_t offset) +{ + queue_t bucket; + dev_device_entry_t entry; + + bucket = &dev_device_hashtable[dev_hash(device + offset)]; + + simple_lock(&dev_device_hash_lock); + for (entry = (dev_device_entry_t)queue_first(bucket); + !queue_end(bucket, &entry->links); + entry = (dev_device_entry_t)queue_next(&entry->links)) { + if (entry->device == device && entry->offset == offset) { + queue_remove(bucket, entry, dev_device_entry_t, links); + break; + } + } + simple_unlock(&dev_device_hash_lock); + if (!queue_end(bucket, &entry->links)) + kmem_cache_free(&dev_device_hash_cache, (vm_offset_t)entry); +} + +static dev_pager_t dev_device_hash_lookup( + const mach_device_t device, + const vm_offset_t offset) +{ + queue_t bucket; + dev_device_entry_t entry; + dev_pager_t pager; + + bucket = &dev_device_hashtable[dev_hash(device + offset)]; + + simple_lock(&dev_device_hash_lock); + for (entry = (dev_device_entry_t)queue_first(bucket); + !queue_end(bucket, &entry->links); + entry = (dev_device_entry_t)queue_next(&entry->links)) { + if (entry->device == device && entry->offset == offset) { + pager = entry->pager_rec; + dev_pager_reference(pager); + simple_unlock(&dev_device_hash_lock); + return (pager); + } + } + simple_unlock(&dev_device_hash_lock); + return (DEV_PAGER_NULL); +} + +kern_return_t device_pager_setup( + const mach_device_t device, + int prot, + vm_offset_t offset, + vm_size_t size, + mach_port_t *pager) +{ + dev_pager_t d; + + /* + * Verify the device is indeed mappable + */ + if (!device->dev_ops->d_mmap || (device->dev_ops->d_mmap == nomap)) + return (D_INVALID_OPERATION); + + /* + * Allocate a structure to hold the arguments + * and port to represent this object. + */ + + d = dev_device_hash_lookup(device, offset); + if (d != DEV_PAGER_NULL) { + *pager = (mach_port_t) ipc_port_make_send(d->pager); + dev_pager_deallocate(d); + return (D_SUCCESS); + } + + d = (dev_pager_t) kmem_cache_alloc(&dev_pager_cache); + if (d == DEV_PAGER_NULL) + return (KERN_RESOURCE_SHORTAGE); + + simple_lock_init(&d->lock); + d->ref_count = 1; + + /* + * Allocate the pager port. + */ + d->pager = ipc_port_alloc_kernel(); + if (d->pager == IP_NULL) { + dev_pager_deallocate(d); + return (KERN_RESOURCE_SHORTAGE); + } + + d->client_count = 0; + d->pager_request = IP_NULL; + d->pager_name = IP_NULL; + d->device = device; + mach_device_reference(device); + d->offset = offset; + d->prot = prot; + d->size = round_page(size); + if (device->dev_ops->d_mmap == block_io_mmap) { + d->type = DEV_PAGER_TYPE; + } else { + d->type = CHAR_PAGER_TYPE; + } + + dev_pager_hash_insert(d->pager, d); + dev_device_hash_insert(d->device, d->offset, d); + + *pager = (mach_port_t) ipc_port_make_send(d->pager); + return (KERN_SUCCESS); +} + +boolean_t device_pager_debug = FALSE; + +kern_return_t device_pager_data_request( + const ipc_port_t pager, + const ipc_port_t pager_request, + vm_offset_t offset, + vm_size_t length, + vm_prot_t protection_required) +{ + dev_pager_t ds; + kern_return_t ret; + + if (device_pager_debug) + printf("(device_pager)data_request: pager=%p, offset=0x%lx, length=0x%lx\n", + pager, (unsigned long) offset, (unsigned long) length); + + ds = dev_pager_hash_lookup(pager); + if (ds == DEV_PAGER_NULL) + panic("(device_pager)data_request: lookup failed"); + + if (ds->pager_request != pager_request) + panic("(device_pager)data_request: bad pager_request"); + + if (ds->type == CHAR_PAGER_TYPE) { + vm_object_t object; + + object = vm_object_lookup(pager_request); + if (object == VM_OBJECT_NULL) { + (void) r_memory_object_data_error(pager_request, + offset, length, + KERN_FAILURE); + dev_pager_deallocate(ds); + return (KERN_SUCCESS); + } + + ret = vm_object_page_map(object, + offset, length, + device_map_page, (void *)ds); + + if (ret != KERN_SUCCESS) { + (void) r_memory_object_data_error(pager_request, + offset, length, + ret); + vm_object_deallocate(object); + dev_pager_deallocate(ds); + return (KERN_SUCCESS); + } + + vm_object_deallocate(object); + } + else { + panic("(device_pager)data_request: dev pager"); + } + + dev_pager_deallocate(ds); + + return (KERN_SUCCESS); +} + +kern_return_t device_pager_copy( + const ipc_port_t pager, + const ipc_port_t pager_request, + vm_offset_t offset, + vm_size_t length, + const ipc_port_t new_pager) +{ + panic("(device_pager)copy: called"); +} + +kern_return_t +device_pager_supply_completed( + const ipc_port_t pager, + const ipc_port_t pager_request, + vm_offset_t offset, + vm_size_t length, + kern_return_t result, + vm_offset_t error_offset) +{ + panic("(device_pager)supply_completed: called"); +} + +kern_return_t +device_pager_data_return( + const ipc_port_t pager, + const ipc_port_t pager_request, + vm_offset_t offset, + pointer_t addr, + mach_msg_type_number_t data_cnt, + boolean_t dirty, + boolean_t kernel_copy) +{ + panic("(device_pager)data_return: called"); +} + +kern_return_t +device_pager_change_completed( + const ipc_port_t pager, + boolean_t may_cache, + memory_object_copy_strategy_t copy_strategy) +{ + panic("(device_pager)change_completed: called"); +} + +/* + * The mapping function takes a byte offset, but returns + * a machine-dependent page frame number. We convert + * that into something that the pmap module will + * accept later. + */ +phys_addr_t device_map_page( + void *dsp, + vm_offset_t offset) +{ + dev_pager_t ds = (dev_pager_t) dsp; + vm_offset_t pagenum = + (*(ds->device->dev_ops->d_mmap)) + (ds->device->dev_number, + ds->offset + offset, + ds->prot); + + if (pagenum == -1) + return vm_page_fictitious_addr; + + return pmap_phys_address(pagenum); +} + +kern_return_t device_pager_init_pager( + const ipc_port_t pager, + const ipc_port_t pager_request, + const ipc_port_t pager_name, + vm_size_t pager_page_size) +{ + dev_pager_t ds; + + if (device_pager_debug) + printf("(device_pager)init: pager=%p, request=%p, name=%p\n", + pager, pager_request, pager_name); + + assert(pager_page_size == PAGE_SIZE); + assert(IP_VALID(pager_request)); + assert(IP_VALID(pager_name)); + + ds = dev_pager_hash_lookup(pager); + assert(ds != DEV_PAGER_NULL); + + assert(ds->client_count == 0); + assert(ds->pager_request == IP_NULL); + assert(ds->pager_name == IP_NULL); + + ds->client_count = 1; + + /* + * We save the send rights for the request and name ports. + */ + + ds->pager_request = pager_request; + ds->pager_name = pager_name; + + if (ds->type == CHAR_PAGER_TYPE) { + /* + * Reply that the object is ready + */ + (void) r_memory_object_ready(pager_request, + FALSE, /* do not cache */ + MEMORY_OBJECT_COPY_NONE); + } else { + (void) r_memory_object_ready(pager_request, + TRUE, /* cache */ + MEMORY_OBJECT_COPY_DELAY); + } + + dev_pager_deallocate(ds); + return (KERN_SUCCESS); +} + +kern_return_t device_pager_terminate( + const ipc_port_t pager, + const ipc_port_t pager_request, + const ipc_port_t pager_name) +{ + dev_pager_t ds; + + assert(IP_VALID(pager_request)); + assert(IP_VALID(pager_name)); + + ds = dev_pager_hash_lookup(pager); + assert(ds != DEV_PAGER_NULL); + + assert(ds->client_count == 1); + assert(ds->pager_request == pager_request); + assert(ds->pager_name == pager_name); + + dev_pager_hash_delete(ds->pager); + dev_device_hash_delete(ds->device, ds->offset); + mach_device_deallocate(ds->device); + + /* release the send rights we have saved from the init call */ + + ipc_port_release_send(pager_request); + ipc_port_release_send(pager_name); + + /* release the naked receive rights we just acquired */ + + ipc_port_release_receive(pager_request); + ipc_port_release_receive(pager_name); + + /* release the kernel's receive right for the pager port */ + + ipc_port_dealloc_kernel(pager); + + /* once for ref from lookup, once to make it go away */ + dev_pager_deallocate(ds); + dev_pager_deallocate(ds); + + return (KERN_SUCCESS); +} + +kern_return_t device_pager_data_unlock( + const ipc_port_t memory_object, + const ipc_port_t memory_control_port, + vm_offset_t offset, + vm_size_t length, + vm_prot_t desired_access) +{ + panic("(device_pager)data_unlock: called"); + return (KERN_FAILURE); +} + +kern_return_t device_pager_lock_completed( + const ipc_port_t memory_object, + const ipc_port_t pager_request_port, + vm_offset_t offset, + vm_size_t length) +{ + panic("(device_pager)lock_completed: called"); + return (KERN_FAILURE); +} + +void device_pager_init(void) +{ + vm_size_t size; + + /* + * Initialize cache of paging structures. + */ + size = sizeof(struct dev_pager); + kmem_cache_init(&dev_pager_cache, "dev_pager", size, 0, + NULL, 0); + + /* + * Initialize the name port hashing stuff. + */ + dev_pager_hash_init(); + dev_device_hash_init(); +} diff --git a/device/dev_pager.h b/device/dev_pager.h new file mode 100644 index 0000000..dc4b202 --- /dev/null +++ b/device/dev_pager.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2013 Free Software Foundation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _DEVICE_DEV_PAGER_H_ +#define _DEVICE_DEV_PAGER_H_ + +phys_addr_t device_map_page(void *dsp, vm_offset_t offset); + +boolean_t device_pager_data_request_done(io_req_t ior); + +#endif /* _DEVICE_DEV_PAGER_H_ */ diff --git a/device/device.srv b/device/device.srv new file mode 100644 index 0000000..f63813f --- /dev/null +++ b/device/device.srv @@ -0,0 +1,27 @@ +/* + * Copyright (c) 1994 The University of Utah and + * the Computer Systems Laboratory at the University of Utah (CSL). + * All rights reserved. + * + * Permission to use, copy, modify and distribute this software is hereby + * granted provided that (1) source code retains these copyright, permission, + * and disclaimer notices, and (2) redistributions including binaries + * reproduce the notices in supporting documentation, and (3) all advertising + * materials mentioning features or use of this software display the following + * acknowledgement: ``This product includes software developed by the + * Computer Systems Laboratory at the University of Utah.'' + * + * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS + * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF + * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * CSL requests users of this software to return to csl-dist@cs.utah.edu any + * improvements that they make and grant CSL redistribution rights. + * + * Author: Bryan Ford, University of Utah CSL + */ +/* This is a server presentation file. */ + +#define KERNEL_SERVER 1 + +#include diff --git a/device/device_emul.h b/device/device_emul.h new file mode 100644 index 0000000..873d7f5 --- /dev/null +++ b/device/device_emul.h @@ -0,0 +1,64 @@ +/* + * Mach device emulation definitions (i386at version). + * + * Copyright (c) 1996 The University of Utah and + * the Computer Systems Laboratory at the University of Utah (CSL). + * All rights reserved. + * + * Permission to use, copy, modify and distribute this software is hereby + * granted provided that (1) source code retains these copyright, permission, + * and disclaimer notices, and (2) redistributions including binaries + * reproduce the notices in supporting documentation, and (3) all advertising + * materials mentioning features or use of this software display the following + * acknowledgement: ``This product includes software developed by the + * Computer Systems Laboratory at the University of Utah.'' + * + * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS + * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF + * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * CSL requests users of this software to return to csl-dist@cs.utah.edu any + * improvements that they make and grant CSL redistribution rights. + * + * Author: Shantanu Goel, University of Utah CSL + */ + +#ifndef _I386AT_DEVICE_EMUL_H_ +#define _I386AT_DEVICE_EMUL_H_ + +#include +#include + +/* Each emulation layer provides these operations. */ +struct device_emulation_ops +{ + void (*reference) (void *); + void (*dealloc) (void *); + ipc_port_t (*dev_to_port) (void *); + io_return_t (*open) (ipc_port_t, mach_msg_type_name_t, + dev_mode_t, const char *, device_t *); + io_return_t (*close) (void *); + io_return_t (*write) (void *, ipc_port_t, mach_msg_type_name_t, + dev_mode_t, recnum_t, io_buf_ptr_t, unsigned, int *); + io_return_t (*write_inband) (void *, ipc_port_t, mach_msg_type_name_t, + dev_mode_t, recnum_t, const io_buf_ptr_inband_t, + unsigned, int *); + io_return_t (*read) (void *, ipc_port_t, mach_msg_type_name_t, + dev_mode_t, recnum_t, int, io_buf_ptr_t *, unsigned *); + io_return_t (*read_inband) (void *, ipc_port_t, mach_msg_type_name_t, + dev_mode_t, recnum_t, int, char *, unsigned *); + io_return_t (*set_status) (void *, dev_flavor_t, dev_status_t, + mach_msg_type_number_t); + io_return_t (*get_status) (void *, dev_flavor_t, dev_status_t, + mach_msg_type_number_t *); + io_return_t (*set_filter) (void *, ipc_port_t, int, filter_t [], unsigned); + io_return_t (*map) (void *, vm_prot_t, vm_offset_t, + vm_size_t, ipc_port_t *, boolean_t); + void (*no_senders) (mach_no_senders_notification_t *); + io_return_t (*write_trap) (void *, dev_mode_t, + rpc_recnum_t, rpc_vm_offset_t, rpc_vm_size_t); + io_return_t (*writev_trap) (void *, dev_mode_t, + rpc_recnum_t, rpc_io_buf_vec_t *, rpc_vm_size_t); +}; + +#endif /* _I386AT_DEVICE_EMUL_H_ */ diff --git a/device/device_init.c b/device/device_init.c new file mode 100644 index 0000000..287d0a2 --- /dev/null +++ b/device/device_init.c @@ -0,0 +1,67 @@ +/* + * 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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 8/89 + * + * Initialize device service as part of kernel task. + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + + +ipc_port_t master_device_port; + +void +device_service_create(void) +{ + master_device_port = ipc_port_alloc_kernel(); + if (master_device_port == IP_NULL) + panic("can't allocate master device port"); + + mach_device_init(); +#ifdef MACH_HYP + hyp_dev_init(); +#endif + dev_lookup_init(); + net_io_init(); + device_pager_init(); + chario_init(); + + (void) kernel_thread(kernel_task, io_done_thread, 0); + (void) kernel_thread(kernel_task, net_thread, 0); +} diff --git a/device/device_init.h b/device/device_init.h new file mode 100644 index 0000000..175b34d --- /dev/null +++ b/device/device_init.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2013 Free Software Foundation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _DEVICE_DEVICE_INIT_H_ +#define _DEVICE_DEVICE_INIT_H_ + +extern void device_service_create(void); + +#endif /* _DEVICE_DEVICE_INIT_H_ */ diff --git a/device/device_pager.srv b/device/device_pager.srv new file mode 100644 index 0000000..410323d --- /dev/null +++ b/device/device_pager.srv @@ -0,0 +1,43 @@ +/* + * Copyright (c) 1994 The University of Utah and + * the Computer Systems Laboratory at the University of Utah (CSL). + * All rights reserved. + * + * Permission to use, copy, modify and distribute this software is hereby + * granted provided that (1) source code retains these copyright, permission, + * and disclaimer notices, and (2) redistributions including binaries + * reproduce the notices in supporting documentation, and (3) all advertising + * materials mentioning features or use of this software display the following + * acknowledgement: ``This product includes software developed by the + * Computer Systems Laboratory at the University of Utah.'' + * + * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS + * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF + * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * CSL requests users of this software to return to csl-dist@cs.utah.edu any + * improvements that they make and grant CSL redistribution rights. + * + * Author: Bryan Ford, University of Utah CSL + */ +/* This is a server presentation file. */ + +#define KERNEL_SERVER 1 + +#define memory_object device_pager + +/* + * Rename all of the functions in the pager interface, to avoid + * confusing them with the kernel interface. + */ +#define memory_object_init device_pager_init_pager +#define memory_object_terminate device_pager_terminate +#define memory_object_copy device_pager_copy +#define memory_object_data_request device_pager_data_request +#define memory_object_data_unlock device_pager_data_unlock +#define memory_object_lock_completed device_pager_lock_completed +#define memory_object_supply_completed device_pager_supply_completed +#define memory_object_data_return device_pager_data_return +#define memory_object_change_completed device_pager_change_completed + +#include diff --git a/device/device_port.h b/device/device_port.h new file mode 100644 index 0000000..8f8aaaa --- /dev/null +++ b/device/device_port.h @@ -0,0 +1,41 @@ +/* + * 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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 8/89 + */ + +#ifndef _DEVICE_DEVICE_PORT_H_ +#define _DEVICE_DEVICE_PORT_H_ + +#include + +/* + * Master privileged port for this host's device service + */ +extern ipc_port_t master_device_port; + +#endif /* _DEVICE_DEVICE_PORT_H_ */ diff --git a/device/device_reply.cli b/device/device_reply.cli new file mode 100644 index 0000000..956540c --- /dev/null +++ b/device/device_reply.cli @@ -0,0 +1,27 @@ +/* + * Copyright (c) 1994 The University of Utah and + * the Computer Systems Laboratory at the University of Utah (CSL). + * All rights reserved. + * + * Permission to use, copy, modify and distribute this software is hereby + * granted provided that (1) source code retains these copyright, permission, + * and disclaimer notices, and (2) redistributions including binaries + * reproduce the notices in supporting documentation, and (3) all advertising + * materials mentioning features or use of this software display the following + * acknowledgement: ``This product includes software developed by the + * Computer Systems Laboratory at the University of Utah.'' + * + * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS + * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF + * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * CSL requests users of this software to return to csl-dist@cs.utah.edu any + * improvements that they make and grant CSL redistribution rights. + * + * Author: Bryan Ford, University of Utah CSL + */ +/* This is a client presentation file. */ + +#define KERNEL_USER 1 + +#include diff --git a/device/device_types_kernel.h b/device/device_types_kernel.h new file mode 100644 index 0000000..e17055c --- /dev/null +++ b/device/device_types_kernel.h @@ -0,0 +1,43 @@ +/* + * 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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 8/89 + */ + +#ifndef _DEVICE_DEVICE_TYPES_KERNEL_H_ +#define _DEVICE_DEVICE_TYPES_KERNEL_H_ + +/* + * Kernel-only type definitions for device server. + */ + +#include +#include + +extern ipc_port_t convert_device_to_port(device_t); + +#endif /* _DEVICE_DEVICE_TYPES_KERNEL_H_ */ diff --git a/device/ds_routines.c b/device/ds_routines.c new file mode 100644 index 0000000..d97d229 --- /dev/null +++ b/device/ds_routines.c @@ -0,0 +1,1901 @@ +/* + * Mach Operating System + * Copyright (c) 1993,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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 3/89 + */ + +/* + * Mach device server routines (i386at version). + * + * Copyright (c) 1996 The University of Utah and + * the Computer Systems Laboratory at the University of Utah (CSL). + * All rights reserved. + * + * Permission to use, copy, modify and distribute this software is hereby + * granted provided that (1) source code retains these copyright, permission, + * and disclaimer notices, and (2) redistributions including binaries + * reproduce the notices in supporting documentation, and (3) all advertising + * materials mentioning features or use of this software display the following + * acknowledgement: ``This product includes software developed by the + * Computer Systems Laboratory at the University of Utah.'' + * + * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS + * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF + * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * CSL requests users of this software to return to csl-dist@cs.utah.edu any + * improvements that they make and grant CSL redistribution rights. + * + * Author: Shantanu Goel, University of Utah CSL + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include /* spl definitions */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef LINUX_DEV +extern struct device_emulation_ops linux_block_emulation_ops; +#ifdef CONFIG_INET +extern struct device_emulation_ops linux_net_emulation_ops; +extern void free_skbuffs (void); +#ifdef CONFIG_PCMCIA +extern struct device_emulation_ops linux_pcmcia_emulation_ops; +#endif /* CONFIG_PCMCIA */ +#endif /* CONFIG_INET */ +#endif /* LINUX_DEV */ +#ifdef MACH_HYP +extern struct device_emulation_ops hyp_block_emulation_ops; +extern struct device_emulation_ops hyp_net_emulation_ops; +#endif /* MACH_HYP */ +extern struct device_emulation_ops mach_device_emulation_ops; + +/* List of emulations. */ +static struct device_emulation_ops *emulation_list[] = +{ +#ifdef LINUX_DEV + &linux_block_emulation_ops, +#ifdef CONFIG_INET + &linux_net_emulation_ops, +#ifdef CONFIG_PCMCIA + &linux_pcmcia_emulation_ops, +#endif /* CONFIG_PCMCIA */ +#endif /* CONFIG_INET */ +#endif /* LINUX_DEV */ +#ifdef MACH_HYP + &hyp_block_emulation_ops, + &hyp_net_emulation_ops, +#endif /* MACH_HYP */ + &mach_device_emulation_ops, +}; + +static struct vm_map device_io_map_store; +vm_map_t device_io_map = &device_io_map_store; +struct kmem_cache io_inband_cache; + +#define NUM_EMULATION (sizeof (emulation_list) / sizeof (emulation_list[0])) + +io_return_t +ds_device_open (ipc_port_t open_port, ipc_port_t reply_port, + mach_msg_type_name_t reply_port_type, dev_mode_t mode, + const_dev_name_t name, device_t *devp) +{ + unsigned i; + io_return_t err; + + /* Open must be called on the master device port. */ + if (open_port != master_device_port) + return D_INVALID_OPERATION; + + /* There must be a reply port. */ + if (! IP_VALID (reply_port)) + { + printf ("ds_* invalid reply port\n"); + SoftDebugger ("ds_* reply_port"); + return MIG_NO_REPLY; + } + + /* Call each emulation's open routine to find the device. */ + for (i = 0; i < NUM_EMULATION; i++) + { + err = (*emulation_list[i]->open) (reply_port, reply_port_type, + mode, name, devp); + if (err != D_NO_SUCH_DEVICE) + break; + } + + return err; +} + +io_return_t +ds_device_open_new (ipc_port_t open_port, ipc_port_t reply_port, + mach_msg_type_name_t reply_port_type, dev_mode_t mode, + const_dev_name_t name, device_t *devp) +{ + return ds_device_open (open_port, reply_port, reply_port_type, mode, name, devp); +} + +io_return_t +ds_device_close (device_t dev) +{ + /* Refuse if device is dead or not completely open. */ + if (dev == DEVICE_NULL) + return D_NO_SUCH_DEVICE; + + return (dev->emul_ops->close + ? (*dev->emul_ops->close) (dev->emul_data) + : D_SUCCESS); +} + +io_return_t +ds_device_write (device_t dev, ipc_port_t reply_port, + mach_msg_type_name_t reply_port_type, dev_mode_t mode, + recnum_t recnum, io_buf_ptr_t data, unsigned int count, + int *bytes_written) +{ + /* Refuse if device is dead or not completely open. */ + if (dev == DEVICE_NULL) + return D_NO_SUCH_DEVICE; + + if (data == 0) + return D_INVALID_SIZE; + + if (! dev->emul_ops->write) + return D_INVALID_OPERATION; + + return (*dev->emul_ops->write) (dev->emul_data, reply_port, + reply_port_type, mode, recnum, + data, count, bytes_written); +} + +io_return_t +ds_device_write_inband (device_t dev, ipc_port_t reply_port, + mach_msg_type_name_t reply_port_type, + dev_mode_t mode, recnum_t recnum, + const io_buf_ptr_inband_t data, unsigned count, + int *bytes_written) +{ + /* Refuse if device is dead or not completely open. */ + if (dev == DEVICE_NULL) + return D_NO_SUCH_DEVICE; + + if (data == 0) + return D_INVALID_SIZE; + + if (! dev->emul_ops->write_inband) + return D_INVALID_OPERATION; + + return (*dev->emul_ops->write_inband) (dev->emul_data, reply_port, + reply_port_type, mode, recnum, + data, count, bytes_written); +} + +io_return_t +ds_device_read (device_t dev, ipc_port_t reply_port, + mach_msg_type_name_t reply_port_type, dev_mode_t mode, + recnum_t recnum, int count, io_buf_ptr_t *data, + unsigned *bytes_read) +{ + /* Refuse if device is dead or not completely open. */ + if (dev == DEVICE_NULL) + return D_NO_SUCH_DEVICE; + + if (! dev->emul_ops->read) + return D_INVALID_OPERATION; + + return (*dev->emul_ops->read) (dev->emul_data, reply_port, + reply_port_type, mode, recnum, + count, data, bytes_read); +} + +io_return_t +ds_device_read_inband (device_t dev, ipc_port_t reply_port, + mach_msg_type_name_t reply_port_type, dev_mode_t mode, + recnum_t recnum, int count, io_buf_ptr_inband_t data, + unsigned *bytes_read) +{ + /* Refuse if device is dead or not completely open. */ + if (dev == DEVICE_NULL) + return D_NO_SUCH_DEVICE; + + if (! dev->emul_ops->read_inband) + return D_INVALID_OPERATION; + + return (*dev->emul_ops->read_inband) (dev->emul_data, reply_port, + reply_port_type, mode, recnum, + count, data, bytes_read); +} + +io_return_t +ds_device_set_status (device_t dev, dev_flavor_t flavor, + dev_status_t status, mach_msg_type_number_t status_count) +{ + /* Refuse if device is dead or not completely open. */ + if (dev == DEVICE_NULL) + return D_NO_SUCH_DEVICE; + + if (! dev->emul_ops->set_status) + return D_INVALID_OPERATION; + + return (*dev->emul_ops->set_status) (dev->emul_data, flavor, status, + status_count); +} + +io_return_t +ds_device_get_status (device_t dev, dev_flavor_t flavor, dev_status_t status, + mach_msg_type_number_t *status_count) +{ + /* Refuse if device is dead or not completely open. */ + if (dev == DEVICE_NULL) + return D_NO_SUCH_DEVICE; + + if (! dev->emul_ops->get_status) + return D_INVALID_OPERATION; + + return (*dev->emul_ops->get_status) (dev->emul_data, flavor, status, + status_count); +} + +io_return_t +ds_device_set_filter (device_t dev, ipc_port_t receive_port, int priority, + filter_t *filter, unsigned filter_count) +{ + /* Refuse if device is dead or not completely open. */ + if (dev == DEVICE_NULL) + return D_NO_SUCH_DEVICE; + + if (! dev->emul_ops->set_filter) + return D_INVALID_OPERATION; + + return (*dev->emul_ops->set_filter) (dev->emul_data, receive_port, + priority, filter, filter_count); +} + +io_return_t +ds_device_map (device_t dev, vm_prot_t prot, vm_offset_t offset, + vm_size_t size, ipc_port_t *pager, boolean_t unmap) +{ + /* Refuse if device is dead or not completely open. */ + if (dev == DEVICE_NULL) + return D_NO_SUCH_DEVICE; + + if (! dev->emul_ops->map) + return D_INVALID_OPERATION; + + return (*dev->emul_ops->map) (dev->emul_data, prot, + offset, size, pager, unmap); +} + +/* TODO: missing deregister support */ +io_return_t +ds_device_intr_register (device_t dev, int id, + int flags, ipc_port_t receive_port) +{ +#if defined(MACH_XEN) + return D_INVALID_OPERATION; +#else /* MACH_XEN */ + kern_return_t err; + mach_device_t mdev; + + /* Refuse if device is dead or not completely open. */ + if (dev == DEVICE_NULL) + return D_NO_SUCH_DEVICE; + + mdev = dev->emul_data; + + /* No flag is defined for now */ + if (flags != 0) + return D_INVALID_OPERATION; + + /* Must be called on the irq device only */ + if (! name_equal(mdev->dev_ops->d_name, 3, "irq")) + return D_INVALID_OPERATION; + + user_intr_t *e = insert_intr_entry (&irqtab, id, receive_port); + if (!e) + return D_NO_MEMORY; + + // TODO detect when the port get destroyed because the driver crashes and + // restart, to replace it when the same device driver calls it again. + err = install_user_intr_handler (&irqtab, id, flags, e); + if (err == D_SUCCESS) + { + /* If the port is installed successfully, increase its reference by 1. + * Thus, the port won't be destroyed after its task is terminated. */ + ip_reference (receive_port); + } + return err; +#endif /* MACH_XEN */ +} + +kern_return_t +ds_device_intr_ack (device_t dev, ipc_port_t receive_port) +{ +#if defined(MACH_XEN) + return D_INVALID_OPERATION; +#else /* MACH_XEN */ + mach_device_t mdev; + kern_return_t ret; + + /* Refuse if device is dead or not completely open. */ + if (dev == DEVICE_NULL) + return D_NO_SUCH_DEVICE; + + mdev = dev->emul_data; + + /* Must be called on the irq device only */ + if (! name_equal(mdev->dev_ops->d_name, 3, "irq")) + return D_INVALID_OPERATION; + + ret = irq_acknowledge(receive_port); + + if (ret == D_SUCCESS) + ipc_port_release_send(receive_port); + + return ret; +#endif /* MACH_XEN */ +} + +boolean_t +ds_notify (mach_msg_header_t *msg) +{ + if (msg->msgh_id == MACH_NOTIFY_NO_SENDERS) + { + device_t dev; + mach_no_senders_notification_t *ns; + + ns = (mach_no_senders_notification_t *) msg; + dev = dev_port_lookup((ipc_port_t) ns->not_header.msgh_remote_port); + assert(dev); + if (dev->emul_ops->no_senders) + (*dev->emul_ops->no_senders) (ns); + return TRUE; + } + + printf ("ds_notify: strange notification %d\n", msg->msgh_id); + return FALSE; +} + +io_return_t +ds_device_write_trap (device_t dev, dev_mode_t mode, + rpc_recnum_t recnum, rpc_vm_offset_t data, rpc_vm_size_t count) +{ + /* Refuse if device is dead or not completely open. */ + if (dev == DEVICE_NULL) + return D_NO_SUCH_DEVICE; + + if (! dev->emul_ops->write_trap) + return D_INVALID_OPERATION; + + return (*dev->emul_ops->write_trap) (dev->emul_data, + mode, recnum, data, count); +} + +io_return_t +ds_device_writev_trap (device_t dev, dev_mode_t mode, + rpc_recnum_t recnum, rpc_io_buf_vec_t *iovec, rpc_vm_size_t count) +{ + /* Refuse if device is dead or not completely open. */ + if (dev == DEVICE_NULL) + return D_NO_SUCH_DEVICE; + + if (! dev->emul_ops->writev_trap) + return D_INVALID_OPERATION; + + return (*dev->emul_ops->writev_trap) (dev->emul_data, + mode, recnum, iovec, count); +} + +void +device_reference (device_t dev) +{ + /* Refuse if device is dead or not completely open. */ + if (dev == DEVICE_NULL) + return; + + if (dev->emul_ops->reference) + (*dev->emul_ops->reference) (dev->emul_data); +} + +void +device_deallocate (device_t dev) +{ + /* Refuse if device is dead or not completely open. */ + if (dev == DEVICE_NULL) + return; + + if (dev->emul_ops->dealloc) + (*dev->emul_ops->dealloc) (dev->emul_data); +} + +/* + * What follows is the interface for the native Mach devices. + */ + +static ipc_port_t +mach_convert_device_to_port (mach_device_t device) +{ + ipc_port_t port; + + if (! device) + return IP_NULL; + + device_lock(device); + + if (device->state == DEV_STATE_OPEN) + port = ipc_port_make_send(device->port); + else + port = IP_NULL; + + device_unlock(device); + + mach_device_deallocate(device); + + return port; +} + +static io_return_t +device_open(const ipc_port_t reply_port, + mach_msg_type_name_t reply_port_type, + dev_mode_t mode, + const char * name, + device_t *device_p) +{ + mach_device_t device; + kern_return_t result; + io_req_t ior; + ipc_port_t notify; + + /* + * Find the device. + */ + device = device_lookup(name); + if (device == MACH_DEVICE_NULL) + return (D_NO_SUCH_DEVICE); + + /* + * If the device is being opened or closed, + * wait for that operation to finish. + */ + device_lock(device); + while (device->state == DEV_STATE_OPENING || + device->state == DEV_STATE_CLOSING) { + device->io_wait = TRUE; + thread_sleep((event_t)device, simple_lock_addr(device->lock), TRUE); + device_lock(device); + } + + /* + * If the device is already open, increment the open count + * and return. + */ + if (device->state == DEV_STATE_OPEN) { + + if (device->flag & D_EXCL_OPEN) { + /* + * Cannot open a second time. + */ + device_unlock(device); + mach_device_deallocate(device); + return (D_ALREADY_OPEN); + } + + device->open_count++; + device_unlock(device); + *device_p = &device->dev; + return (D_SUCCESS); + /* + * Return deallocates device reference while acquiring + * port. + */ + } + + /* + * Allocate the device port and register the device before + * opening it. + */ + device->state = DEV_STATE_OPENING; + device_unlock(device); + + /* + * Allocate port, keeping a reference for it. + */ + device->port = ipc_port_alloc_kernel(); + if (device->port == IP_NULL) { + device_lock(device); + device->state = DEV_STATE_INIT; + device->port = IP_NULL; + if (device->io_wait) { + device->io_wait = FALSE; + thread_wakeup((event_t)device); + } + device_unlock(device); + mach_device_deallocate(device); + return (KERN_RESOURCE_SHORTAGE); + } + + dev_port_enter(device); + + /* + * Request no-senders notifications on device port. + */ + notify = ipc_port_make_sonce(device->port); + ip_lock(device->port); + ipc_port_nsrequest(device->port, 1, notify, ¬ify); + assert(notify == IP_NULL); + + /* + * Open the device. + */ + io_req_alloc(ior, 0); + + ior->io_device = device; + ior->io_unit = device->dev_number; + ior->io_op = IO_OPEN | IO_CALL; + ior->io_mode = mode; + ior->io_error = 0; + ior->io_done = ds_open_done; + ior->io_reply_port = reply_port; + ior->io_reply_port_type = reply_port_type; + + result = (*device->dev_ops->d_open)(device->dev_number, (int)mode, ior); + if (result == D_IO_QUEUED) + return (MIG_NO_REPLY); + + /* + * Return result via ds_open_done. + */ + ior->io_error = result; + (void) ds_open_done(ior); + + io_req_free(ior); + + return (MIG_NO_REPLY); /* reply already sent */ +} + +boolean_t +ds_open_done(const io_req_t ior) +{ + kern_return_t result; + mach_device_t device; + + device = ior->io_device; + result = ior->io_error; + + if (result != D_SUCCESS) { + /* + * Open failed. Deallocate port and device. + */ + dev_port_remove(device); + ipc_port_dealloc_kernel(device->port); + device->port = IP_NULL; + + device_lock(device); + device->state = DEV_STATE_INIT; + if (device->io_wait) { + device->io_wait = FALSE; + thread_wakeup((event_t)device); + } + device_unlock(device); + + mach_device_deallocate(device); + device = MACH_DEVICE_NULL; + } + else { + /* + * Open succeeded. + */ + device_lock(device); + device->state = DEV_STATE_OPEN; + device->open_count = 1; + if (device->io_wait) { + device->io_wait = FALSE; + thread_wakeup((event_t)device); + } + device_unlock(device); + + /* donate device reference to get port */ + } + /* + * Must explicitly convert device to port, since + * device_reply interface is built as 'user' side + * (thus cannot get translation). + */ + if (IP_VALID(ior->io_reply_port)) { + (void) ds_device_open_reply(ior->io_reply_port, + ior->io_reply_port_type, + result, + mach_convert_device_to_port(device)); + } else if (device) + mach_device_deallocate(device); + + return (TRUE); +} + +static io_return_t +device_close(void *dev) +{ + mach_device_t device = dev; + + device_lock(device); + + /* + * If device will remain open, do nothing. + */ + if (--device->open_count > 0) { + device_unlock(device); + return (D_SUCCESS); + } + + /* + * If device is being closed, do nothing. + */ + if (device->state == DEV_STATE_CLOSING) { + device_unlock(device); + return (D_SUCCESS); + } + + /* + * Mark device as closing, to prevent new IO. + * Outstanding IO will still be in progress. + */ + device->state = DEV_STATE_CLOSING; + device_unlock(device); + + /* + * ? wait for IO to end ? + * only if device wants to + */ + + /* + * Remove the device-port association. + */ + dev_port_remove(device); + ipc_port_dealloc_kernel(device->port); + + /* + * Close the device + */ + (*device->dev_ops->d_close)(device->dev_number, 0); + + /* + * Finally mark it closed. If someone else is trying + * to open it, the open can now proceed. + */ + device_lock(device); + device->state = DEV_STATE_INIT; + if (device->io_wait) { + device->io_wait = FALSE; + thread_wakeup((event_t)device); + } + device_unlock(device); + + return (D_SUCCESS); +} + +/* + * Write to a device. + */ +static io_return_t +device_write(void *dev, + const ipc_port_t reply_port, + mach_msg_type_name_t reply_port_type, + dev_mode_t mode, + recnum_t recnum, + const io_buf_ptr_t data, + unsigned int data_count, + int *bytes_written) +{ + mach_device_t device = dev; + io_req_t ior; + io_return_t result; + + if (device->state != DEV_STATE_OPEN) + return (D_NO_SUCH_DEVICE); + + /* + * XXX Need logic to reject ridiculously big requests. + */ + + /* XXX note that a CLOSE may proceed at any point */ + + /* + * Package the write request for the device driver + */ + io_req_alloc(ior, data_count); + + ior->io_device = device; + ior->io_unit = device->dev_number; + ior->io_op = IO_WRITE | IO_CALL; + ior->io_mode = mode; + ior->io_recnum = recnum; + ior->io_data = data; + ior->io_count = data_count; + ior->io_total = data_count; + ior->io_alloc_size = 0; + ior->io_residual = 0; + ior->io_error = 0; + ior->io_done = ds_write_done; + ior->io_reply_port = reply_port; + ior->io_reply_port_type = reply_port_type; + ior->io_copy = VM_MAP_COPY_NULL; + + /* + * The ior keeps an extra reference for the device. + */ + mach_device_reference(device); + + /* + * And do the write ... + * + * device_write_dealoc returns false if there's more + * to do; it has updated the ior appropriately and expects + * its caller to reinvoke it on the device. + */ + + do { + + result = (*device->dev_ops->d_write)(device->dev_number, ior); + + /* + * If the IO was queued, delay reply until it is finished. + */ + if (result == D_IO_QUEUED) + return (MIG_NO_REPLY); + + /* + * Discard the local mapping of the data. + */ + + } while (!device_write_dealloc(ior)); + + /* + * Return the number of bytes actually written. + */ + *bytes_written = ior->io_total - ior->io_residual; + + /* + * Remove the extra reference. + */ + mach_device_deallocate(device); + + io_req_free(ior); + return (result); +} + +/* + * Write to a device, but memory is in message. + */ +static io_return_t +device_write_inband(void *dev, + const ipc_port_t reply_port, + mach_msg_type_name_t reply_port_type, + dev_mode_t mode, + recnum_t recnum, + const io_buf_ptr_inband_t data, + unsigned int data_count, + int *bytes_written) +{ + mach_device_t device = dev; + io_req_t ior; + io_return_t result; + + if (device->state != DEV_STATE_OPEN) + return (D_NO_SUCH_DEVICE); + + /* XXX note that a CLOSE may proceed at any point */ + + /* + * Package the write request for the device driver. + */ + io_req_alloc(ior, 0); + + ior->io_device = device; + ior->io_unit = device->dev_number; + ior->io_op = IO_WRITE | IO_CALL | IO_INBAND; + ior->io_mode = mode; + ior->io_recnum = recnum; + ior->io_data = (io_buf_ptr_t)data; + ior->io_count = data_count; + ior->io_total = data_count; + ior->io_alloc_size = 0; + ior->io_residual = 0; + ior->io_error = 0; + ior->io_done = ds_write_done; + ior->io_reply_port = reply_port; + ior->io_reply_port_type = reply_port_type; + + /* + * The ior keeps an extra reference for the device. + */ + mach_device_reference(device); + + /* + * And do the write. + */ + result = (*device->dev_ops->d_write)(device->dev_number, ior); + + /* + * If the IO was queued, delay reply until it is finished. + */ + if (result == D_IO_QUEUED) + return (MIG_NO_REPLY); + + /* + * Return the number of bytes actually written. + */ + *bytes_written = ior->io_total - ior->io_residual; + + /* + * Remove the extra reference. + */ + mach_device_deallocate(device); + + io_req_free(ior); + return (result); +} + +/* + * Wire down incoming memory to give to device. + */ +kern_return_t +device_write_get( + io_req_t ior, + boolean_t *wait) +{ + vm_map_copy_t io_copy; + vm_offset_t new_addr; + kern_return_t result; + int bsize; + vm_size_t min_size; + + /* + * By default, caller does not have to wait. + */ + *wait = FALSE; + + /* + * Nothing to do if no data. + */ + if (ior->io_count == 0) + return (KERN_SUCCESS); + + /* + * Loaned iors already have valid data. + */ + if (ior->io_op & IO_LOANED) + return (KERN_SUCCESS); + + /* + * Inband case. + */ + if (ior->io_op & IO_INBAND) { + assert(ior->io_count <= sizeof (io_buf_ptr_inband_t)); + new_addr = kmem_cache_alloc(&io_inband_cache); + memcpy((void*)new_addr, ior->io_data, ior->io_count); + ior->io_data = (io_buf_ptr_t)new_addr; + ior->io_alloc_size = sizeof (io_buf_ptr_inband_t); + + return (KERN_SUCCESS); + } + + /* + * Figure out how much data to move this time. If the device + * won't return a block size, then we have to do the whole + * request in one shot (ditto if this is a block fragment), + * otherwise, move at least one block's worth. + */ + result = (*ior->io_device->dev_ops->d_dev_info)( + ior->io_device->dev_number, + D_INFO_BLOCK_SIZE, + &bsize); + + if (result != KERN_SUCCESS || ior->io_count < (vm_size_t) bsize) + min_size = (vm_size_t) ior->io_count; + else + min_size = (vm_size_t) bsize; + + /* + * Map the pages from this page list into memory. + * io_data records location of data. + * io_alloc_size is the vm size of the region to deallocate. + */ + io_copy = (vm_map_copy_t) ior->io_data; + result = kmem_io_map_copyout(device_io_map, + (vm_offset_t*)&ior->io_data, &new_addr, + &ior->io_alloc_size, io_copy, min_size); + if (result != KERN_SUCCESS) + return (result); + + if ((ior->io_data + ior->io_count) > + (((char *)new_addr) + ior->io_alloc_size)) { + + /* + * Operation has to be split. Reset io_count for how + * much we can do this time. + */ + assert(vm_map_copy_has_cont(io_copy)); + assert(ior->io_count == io_copy->size); + ior->io_count = ior->io_alloc_size - + (ior->io_data - ((char *)new_addr)); + + /* + * Caller must wait synchronously. + */ + ior->io_op &= ~IO_CALL; + *wait = TRUE; + } + + ior->io_copy = io_copy; /* vm_map_copy to discard */ + return (KERN_SUCCESS); +} + +/* + * Clean up memory allocated for IO. + */ +boolean_t +device_write_dealloc(io_req_t ior) +{ + vm_map_copy_t new_copy = VM_MAP_COPY_NULL; + vm_map_copy_t io_copy; + kern_return_t result; + vm_offset_t size_to_do; + int bsize; + + if (ior->io_alloc_size == 0) + return (TRUE); + + /* + * Inband case. + */ + if (ior->io_op & IO_INBAND) { + kmem_cache_free(&io_inband_cache, (vm_offset_t)ior->io_data); + + return (TRUE); + } + + if ((io_copy = ior->io_copy) == VM_MAP_COPY_NULL) + return (TRUE); + + /* + * To prevent a possible deadlock with the default pager, + * we have to release space in the device_io_map before + * we allocate any memory. (Which vm_map_copy_invoke_cont + * might do.) See the discussion in mach_device_init. + */ + + kmem_io_map_deallocate(device_io_map, + trunc_page(ior->io_data), + ior->io_alloc_size); + + if (vm_map_copy_has_cont(io_copy)) { + + /* + * Remember how much is left, then + * invoke or abort the continuation. + */ + size_to_do = io_copy->size - ior->io_count; + if (ior->io_error == 0) { + vm_map_copy_invoke_cont(io_copy, &new_copy, &result); + } + else { + vm_map_copy_abort_cont(io_copy); + result = KERN_FAILURE; + } + + if (result == KERN_SUCCESS && new_copy != VM_MAP_COPY_NULL) { + int res; + + /* + * We have a new continuation, reset the ior to + * represent the remainder of the request. Must + * adjust the recnum because drivers assume + * that the residual is zero. + */ + ior->io_op &= ~IO_DONE; + ior->io_op |= IO_CALL; + + res = (*ior->io_device->dev_ops->d_dev_info)( + ior->io_device->dev_number, + D_INFO_BLOCK_SIZE, + &bsize); + + if (res != D_SUCCESS) + panic("device_write_dealloc: No block size"); + + ior->io_recnum += ior->io_count/bsize; + ior->io_count = new_copy->size; + } + else { + + /* + * No continuation. Add amount we didn't get + * to into residual. + */ + ior->io_residual += size_to_do; + } + } + + /* + * Clean up the state for the IO that just completed. + */ + vm_map_copy_discard(ior->io_copy); + ior->io_copy = VM_MAP_COPY_NULL; + ior->io_data = (char *) new_copy; + + /* + * Return FALSE if there's more IO to do. + */ + + return(new_copy == VM_MAP_COPY_NULL); +} + +/* + * Send write completion message to client, and discard the data. + */ +boolean_t +ds_write_done(const io_req_t ior) +{ + /* + * device_write_dealloc discards the data that has been + * written, but may decide that there is more to write. + */ + while (!device_write_dealloc(ior)) { + io_return_t result; + mach_device_t device; + + /* + * More IO to do -- invoke it. + */ + device = ior->io_device; + result = (*device->dev_ops->d_write)(device->dev_number, ior); + + /* + * If the IO was queued, return FALSE -- not done yet. + */ + if (result == D_IO_QUEUED) + return (FALSE); + } + + /* + * Now the write is really complete. Send reply. + */ + + if (IP_VALID(ior->io_reply_port)) { + (void) (*((ior->io_op & IO_INBAND) ? + ds_device_write_reply_inband : + ds_device_write_reply))(ior->io_reply_port, + ior->io_reply_port_type, + ior->io_error, + (int) (ior->io_total - + ior->io_residual)); + } + mach_device_deallocate(ior->io_device); + + return (TRUE); +} + +/* + * Read from a device. + */ +static io_return_t +device_read(void *dev, + const ipc_port_t reply_port, + mach_msg_type_name_t reply_port_type, + dev_mode_t mode, + recnum_t recnum, + int bytes_wanted, + io_buf_ptr_t *data, + unsigned int *data_count) +{ + mach_device_t device = dev; + io_req_t ior; + io_return_t result; + + if (device->state != DEV_STATE_OPEN) + return (D_NO_SUCH_DEVICE); + + /* XXX note that a CLOSE may proceed at any point */ + + /* + * There must be a reply port. + */ + if (!IP_VALID(reply_port)) { + printf("ds_* invalid reply port\n"); + SoftDebugger("ds_* reply_port"); + return (MIG_NO_REPLY); /* no sense in doing anything */ + } + + /* + * Package the read request for the device driver + */ + io_req_alloc(ior, 0); + + ior->io_device = device; + ior->io_unit = device->dev_number; + ior->io_op = IO_READ | IO_CALL; + ior->io_mode = mode; + ior->io_recnum = recnum; + ior->io_data = 0; /* driver must allocate data */ + ior->io_count = bytes_wanted; + ior->io_alloc_size = 0; /* no data allocated yet */ + ior->io_residual = 0; + ior->io_error = 0; + ior->io_done = ds_read_done; + ior->io_reply_port = reply_port; + ior->io_reply_port_type = reply_port_type; + + /* + * The ior keeps an extra reference for the device. + */ + mach_device_reference(device); + + /* + * And do the read. + */ + result = (*device->dev_ops->d_read)(device->dev_number, ior); + + /* + * If the IO was queued, delay reply until it is finished. + */ + if (result == D_IO_QUEUED) + return (MIG_NO_REPLY); + + /* + * Return result via ds_read_done. + */ + ior->io_error = result; + (void) ds_read_done(ior); + io_req_free(ior); + + return (MIG_NO_REPLY); /* reply has already been sent. */ +} + +/* + * Read from a device, but return the data 'inband.' + */ +static io_return_t +device_read_inband(void *dev, + const ipc_port_t reply_port, + mach_msg_type_name_t reply_port_type, + dev_mode_t mode, + recnum_t recnum, + int bytes_wanted, + char *data, + unsigned int *data_count) +{ + mach_device_t device = dev; + io_req_t ior; + io_return_t result; + + if (device->state != DEV_STATE_OPEN) + return (D_NO_SUCH_DEVICE); + + /* XXX note that a CLOSE may proceed at any point */ + + /* + * There must be a reply port. + */ + if (!IP_VALID(reply_port)) { + printf("ds_* invalid reply port\n"); + SoftDebugger("ds_* reply_port"); + return (MIG_NO_REPLY); /* no sense in doing anything */ + } + + /* + * Package the read for the device driver + */ + io_req_alloc(ior, 0); + + ior->io_device = device; + ior->io_unit = device->dev_number; + ior->io_op = IO_READ | IO_CALL | IO_INBAND; + ior->io_mode = mode; + ior->io_recnum = recnum; + ior->io_data = 0; /* driver must allocate data */ + ior->io_count = + ((bytes_wanted < sizeof(io_buf_ptr_inband_t)) ? + bytes_wanted : sizeof(io_buf_ptr_inband_t)); + ior->io_alloc_size = 0; /* no data allocated yet */ + ior->io_residual = 0; + ior->io_error = 0; + ior->io_done = ds_read_done; + ior->io_reply_port = reply_port; + ior->io_reply_port_type = reply_port_type; + + /* + * The ior keeps an extra reference for the device. + */ + mach_device_reference(device); + + /* + * Do the read. + */ + result = (*device->dev_ops->d_read)(device->dev_number, ior); + + /* + * If the io was queued, delay reply until it is finished. + */ + if (result == D_IO_QUEUED) + return (MIG_NO_REPLY); + + /* + * Return result, via ds_read_done. + */ + ior->io_error = result; + (void) ds_read_done(ior); + io_req_free(ior); + + return (MIG_NO_REPLY); /* reply has already been sent. */ +} + +/* + * Allocate wired-down memory for device read. + */ +kern_return_t device_read_alloc( + io_req_t ior, + vm_size_t size) +{ + vm_offset_t addr; + kern_return_t kr; + + /* + * Nothing to do if no data. + */ + if (ior->io_count == 0) + return (KERN_SUCCESS); + + if (ior->io_op & IO_INBAND) { + ior->io_data = (io_buf_ptr_t) kmem_cache_alloc(&io_inband_cache); + ior->io_alloc_size = sizeof(io_buf_ptr_inband_t); + } else { + size = round_page(size); + kr = kmem_alloc(kernel_map, &addr, size); + if (kr != KERN_SUCCESS) + return (kr); + + ior->io_data = (io_buf_ptr_t) addr; + ior->io_alloc_size = size; + } + + return (KERN_SUCCESS); +} + +boolean_t ds_read_done(const io_req_t ior) +{ + vm_offset_t start_data, end_data; + vm_offset_t start_sent, end_sent; + vm_size_t size_read; + + if (ior->io_error) + size_read = 0; + else + size_read = ior->io_count - ior->io_residual; + + start_data = (vm_offset_t)ior->io_data; + end_data = start_data + size_read; + + start_sent = (ior->io_op & IO_INBAND) ? start_data : + trunc_page(start_data); + end_sent = (ior->io_op & IO_INBAND) ? + start_data + ior->io_alloc_size : round_page(end_data); + + /* + * Zero memory that the device did not fill. + */ + if (start_sent < start_data) + memset((void *)start_sent, 0, start_data - start_sent); + if (end_sent > end_data) + memset((void *)end_data, 0, end_sent - end_data); + + + /* + * Touch the data being returned, to mark it dirty. + * If the pages were filled by DMA, the pmap module + * may think that they are clean. + */ + { + vm_offset_t touch; + int c; + + for (touch = start_sent; touch < end_sent; touch += PAGE_SIZE) { + c = *(volatile char *)touch; + *(volatile char *)touch = c; + } + } + + /* + * Send the data to the reply port - this + * unwires and deallocates it. + */ + if (ior->io_op & IO_INBAND) { + (void)ds_device_read_reply_inband(ior->io_reply_port, + ior->io_reply_port_type, + ior->io_error, + (char *) start_data, + size_read); + } else { + vm_map_copy_t copy; + kern_return_t kr; + + kr = vm_map_copyin_page_list(kernel_map, start_data, + size_read, TRUE, TRUE, + ©, FALSE); + + if (kr != KERN_SUCCESS) + panic("read_done: vm_map_copyin_page_list failed"); + + (void)ds_device_read_reply(ior->io_reply_port, + ior->io_reply_port_type, + ior->io_error, + (char *) copy, + size_read); + } + + /* + * Free any memory that was allocated but not sent. + */ + if (ior->io_count != 0) { + if (ior->io_op & IO_INBAND) { + if (ior->io_alloc_size > 0) + kmem_cache_free(&io_inband_cache, (vm_offset_t)ior->io_data); + } else { + vm_offset_t end_alloc; + + end_alloc = start_sent + round_page(ior->io_alloc_size); + if (end_alloc > end_sent) + (void) vm_deallocate(kernel_map, + end_sent, + end_alloc - end_sent); + } + } + + mach_device_deallocate(ior->io_device); + + return (TRUE); +} + +static io_return_t +device_set_status( + void *dev, + dev_flavor_t flavor, + dev_status_t status, + mach_msg_type_number_t status_count) +{ + mach_device_t device = dev; + if (device->state != DEV_STATE_OPEN) + return (D_NO_SUCH_DEVICE); + + /* XXX note that a CLOSE may proceed at any point */ + + return ((*device->dev_ops->d_setstat)(device->dev_number, + flavor, + status, + status_count)); +} + +static io_return_t +mach_device_get_status( + void *dev, + dev_flavor_t flavor, + dev_status_t status, /* pointer to OUT array */ + mach_msg_type_number_t *status_count) /* out */ +{ + mach_device_t device = dev; + if (device->state != DEV_STATE_OPEN) + return (D_NO_SUCH_DEVICE); + + /* XXX note that a CLOSE may proceed at any point */ + + return ((*device->dev_ops->d_getstat)(device->dev_number, + flavor, + status, + status_count)); +} + +static io_return_t +device_set_filter(void *dev, + const ipc_port_t receive_port, + int priority, + filter_t filter[], + unsigned int filter_count) +{ + mach_device_t device = dev; + if (device->state != DEV_STATE_OPEN) + return (D_NO_SUCH_DEVICE); + + /* XXX note that a CLOSE may proceed at any point */ + + /* + * Request is absurd if no receive port is specified. + */ + if (!IP_VALID(receive_port)) + return (D_INVALID_OPERATION); + + return ((*device->dev_ops->d_async_in)(device->dev_number, + receive_port, + priority, + filter, + filter_count)); +} + +static io_return_t +device_map( + void *dev, + vm_prot_t protection, + vm_offset_t offset, + vm_size_t size, + ipc_port_t *pager, /* out */ + boolean_t unmap) /* ? */ +{ + mach_device_t device = dev; + if (protection & ~VM_PROT_ALL) + return (KERN_INVALID_ARGUMENT); + + if (device->state != DEV_STATE_OPEN) + return (D_NO_SUCH_DEVICE); + + /* XXX note that a CLOSE may proceed at any point */ + + return (device_pager_setup(device, protection, offset, size, + (mach_port_t*)pager)); +} + +/* + * Doesn't do anything (yet). + */ +static void +ds_no_senders(mach_no_senders_notification_t *notification) +{ + printf("ds_no_senders called! device_port=0x%zx count=%d\n", + notification->not_header.msgh_remote_port, + notification->not_count); +} + +/* Shall be taken at splio only */ +def_simple_lock_irq_data(static, io_done_list_lock) /* Lock for... */ +queue_head_t io_done_list; + +#define splio splsched /* XXX must block ALL io devices */ + +void iodone(io_req_t ior) +{ + spl_t s; + + /* + * If this ior was loaned to us, return it directly. + */ + if (ior->io_op & IO_LOANED) { + (*ior->io_done)(ior); + return; + } + /* + * If !IO_CALL, some thread is waiting for this. Must lock + * structure to interlock correctly with iowait(). Else can + * toss on queue for io_done thread to call completion. + */ + s = splio(); + if ((ior->io_op & IO_CALL) == 0) { + ior_lock(ior); + ior->io_op |= IO_DONE; + ior->io_op &= ~IO_WANTED; + ior_unlock(ior); + thread_wakeup((event_t)ior); + } else { + ior->io_op |= IO_DONE; + simple_lock_nocheck(&io_done_list_lock.slock); + enqueue_tail(&io_done_list, (queue_entry_t)ior); + thread_wakeup((event_t)&io_done_list); + simple_unlock_nocheck(&io_done_list_lock.slock); + } + splx(s); +} + +static void __attribute__ ((noreturn)) io_done_thread_continue(void) +{ + for (;;) { + spl_t s; + io_req_t ior; + +#if defined (LINUX_DEV) && defined (CONFIG_INET) + free_skbuffs (); +#endif + s = simple_lock_irq(&io_done_list_lock); + while ((ior = (io_req_t)dequeue_head(&io_done_list)) != 0) { + simple_unlock_irq(s, &io_done_list_lock); + + if ((*ior->io_done)(ior)) { + /* + * IO done - free io_req_elt + */ + io_req_free(ior); + } + /* else routine has re-queued it somewhere */ + + s = simple_lock_irq(&io_done_list_lock); + } + + assert_wait(&io_done_list, FALSE); + simple_unlock_irq(s, &io_done_list_lock); + counter(c_io_done_thread_block++); + thread_block(io_done_thread_continue); + } +} + +void io_done_thread(void) +{ + /* + * Set thread privileges and highest priority. + */ + current_thread()->vm_privilege = 1; + stack_privilege(current_thread()); + thread_set_own_priority(0); + + io_done_thread_continue(); + /*NOTREACHED*/ +} + +#define DEVICE_IO_MAP_SIZE (16 * 1024 * 1024) + +static void mach_device_trap_init(void); /* forward */ + +void mach_device_init(void) +{ + vm_offset_t device_io_min, device_io_max; + + queue_init(&io_done_list); + simple_lock_init_irq(&io_done_list_lock); + + kmem_submap(device_io_map, kernel_map, &device_io_min, &device_io_max, + DEVICE_IO_MAP_SIZE); + + /* + * If the kernel receives many device_write requests, the + * device_io_map might run out of space. To prevent + * device_write_get from failing in this case, we enable + * wait_for_space on the map. This causes kmem_io_map_copyout + * to block until there is sufficient space. + * (XXX Large writes may be starved by small writes.) + * + * There is a potential deadlock problem with this solution, + * if a device_write from the default pager has to wait + * for the completion of a device_write which needs to wait + * for memory allocation. Hence, once device_write_get + * allocates space in device_io_map, no blocking memory + * allocations should happen until device_write_dealloc + * frees the space. (XXX A large write might starve + * a small write from the default pager.) + */ + device_io_map->wait_for_space = TRUE; + + kmem_cache_init(&io_inband_cache, "io_buf_ptr_inband", + sizeof(io_buf_ptr_inband_t), 0, NULL, 0); + + mach_device_trap_init(); +} + +void iowait(io_req_t ior) +{ + spl_t s; + + s = splio(); + ior_lock(ior); + while ((ior->io_op&IO_DONE)==0) { + assert_wait((event_t)ior, FALSE); + ior_unlock(ior); + thread_block((void (*)()) 0); + ior_lock(ior); + } + ior_unlock(ior); + splx(s); +} + + +/* + * Device trap support. + */ + +/* + * Memory Management + * + * This currently has a single pool of 2k wired buffers + * since we only handle writes to an ethernet device. + * Should be more general. + */ +#define IOTRAP_REQSIZE 2048 + +struct kmem_cache io_trap_cache; + +/* + * Initialization. Called from mach_device_init(). + */ +static void +mach_device_trap_init(void) +{ + kmem_cache_init(&io_trap_cache, "io_req", IOTRAP_REQSIZE, 0, + NULL, 0); +} + +/* + * Allocate an io_req_t. + * Currently allocates from io_trap_cache. + * + * Could have lists of different size caches. + * Could call a device-specific routine. + */ +static io_req_t +ds_trap_req_alloc(const mach_device_t device, vm_size_t data_size) +{ + return (io_req_t) kmem_cache_alloc(&io_trap_cache); +} + +/* + * Called by iodone to release ior. + */ +static boolean_t +ds_trap_write_done(const io_req_t ior) +{ + mach_device_t dev; + + dev = ior->io_device; + + /* + * Should look at reply port and maybe send a message. + */ + kmem_cache_free(&io_trap_cache, (vm_offset_t) ior); + + /* + * Give up device reference from ds_write_trap. + */ + mach_device_deallocate(dev); + return TRUE; +} + +/* + * Like device_write except that data is in user space. + */ +static io_return_t +device_write_trap (mach_device_t device, dev_mode_t mode, + rpc_recnum_t recnum, rpc_vm_offset_t data, rpc_vm_size_t data_count) +{ + io_req_t ior; + io_return_t result; + + if (device->state != DEV_STATE_OPEN) + return (D_NO_SUCH_DEVICE); + + /* XXX note that a CLOSE may proceed at any point */ + + /* + * Get a buffer to hold the ioreq. + */ + ior = ds_trap_req_alloc(device, data_count); + + /* + * Package the write request for the device driver. + */ + + ior->io_device = device; + ior->io_unit = device->dev_number; + ior->io_op = IO_WRITE | IO_CALL | IO_LOANED; + ior->io_mode = mode; + ior->io_recnum = recnum; + ior->io_data = (io_buf_ptr_t) + (vm_offset_t)ior + sizeof(struct io_req); + ior->io_count = data_count; + ior->io_total = data_count; + ior->io_alloc_size = 0; + ior->io_residual = 0; + ior->io_error = 0; + ior->io_done = ds_trap_write_done; + ior->io_reply_port = IP_NULL; /* XXX */ + ior->io_reply_port_type = 0; /* XXX */ + + /* + * Copy the data from user space. + */ + if (data_count > 0) + copyin((void*)(vm_offset_t)data, ior->io_data, data_count); + + /* + * The ior keeps an extra reference for the device. + */ + mach_device_reference(device); + + /* + * And do the write. + */ + result = (*device->dev_ops->d_write)(device->dev_number, ior); + + /* + * If the IO was queued, delay reply until it is finished. + */ + if (result == D_IO_QUEUED) + return (MIG_NO_REPLY); + + /* + * Remove the extra reference. + */ + mach_device_deallocate(device); + + kmem_cache_free(&io_trap_cache, (vm_offset_t) ior); + return (result); +} + +static io_return_t +device_writev_trap (mach_device_t device, dev_mode_t mode, + rpc_recnum_t recnum, rpc_io_buf_vec_t *iovec, rpc_vm_size_t iocount) +{ + io_req_t ior; + io_return_t result; + io_buf_vec_t stack_iovec[16]; /* XXX */ + vm_size_t data_count; + unsigned i; + + if (device->state != DEV_STATE_OPEN) + return (D_NO_SUCH_DEVICE); + + /* XXX note that a CLOSE may proceed at any point */ + + /* + * Copyin user addresses. + */ + if (iocount > 16) + return KERN_INVALID_VALUE; /* lame */ + + for (data_count = 0, i=0; iio_device = device; + ior->io_unit = device->dev_number; + ior->io_op = IO_WRITE | IO_CALL | IO_LOANED; + ior->io_mode = mode; + ior->io_recnum = recnum; + ior->io_data = (io_buf_ptr_t) + (vm_offset_t)ior + sizeof(struct io_req); + ior->io_count = data_count; + ior->io_total = data_count; + ior->io_alloc_size = 0; + ior->io_residual = 0; + ior->io_error = 0; + ior->io_done = ds_trap_write_done; + ior->io_reply_port = IP_NULL; /* XXX */ + ior->io_reply_port_type = 0; /* XXX */ + + /* + * Copy the data from user space. + */ + if (data_count > 0) { + vm_offset_t p; + + p = (vm_offset_t) ior->io_data; + for (i = 0; i < iocount; i++) { + copyin((void *) stack_iovec[i].data, + (void *) p, + stack_iovec[i].count); + p += stack_iovec[i].count; + } + } + + /* + * The ior keeps an extra reference for the device. + */ + mach_device_reference(device); + + /* + * And do the write. + */ + result = (*device->dev_ops->d_write)(device->dev_number, ior); + + /* + * If the IO was queued, delay reply until it is finished. + */ + if (result == D_IO_QUEUED) + return (MIG_NO_REPLY); + + /* + * Remove the extra reference. + */ + mach_device_deallocate(device); + + kmem_cache_free(&io_trap_cache, (vm_offset_t) ior); + return (result); +} + +struct device_emulation_ops mach_device_emulation_ops = +{ + (void*) mach_device_reference, + (void*) mach_device_deallocate, + (void*) mach_convert_device_to_port, + device_open, + device_close, + device_write, + device_write_inband, + device_read, + device_read_inband, + device_set_status, + mach_device_get_status, + device_set_filter, + device_map, + ds_no_senders, + (void*) device_write_trap, + (void*) device_writev_trap +}; diff --git a/device/ds_routines.h b/device/ds_routines.h new file mode 100644 index 0000000..48d85dd --- /dev/null +++ b/device/ds_routines.h @@ -0,0 +1,86 @@ +/* + * 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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 8/89 + * + * Device service utility routines. + */ + +#ifndef DS_ROUTINES_H +#define DS_ROUTINES_H + +#include +#include +#include +#include + +/* + * Map for device IO memory. + */ +extern vm_map_t device_io_map; + +extern queue_head_t io_done_list; + +kern_return_t device_read_alloc(io_req_t, vm_size_t); +kern_return_t device_write_get(io_req_t, boolean_t *); +boolean_t device_write_dealloc(io_req_t); +void device_reference(device_t); + +boolean_t ds_notify(mach_msg_header_t *msg); +boolean_t ds_open_done(io_req_t); +boolean_t ds_read_done(io_req_t); +boolean_t ds_write_done(io_req_t); + +void iowait (io_req_t ior); + +kern_return_t device_pager_setup( + const mach_device_t device, + int prot, + vm_offset_t offset, + vm_size_t size, + mach_port_t *pager); + +extern void mach_device_init(void); +extern void dev_lookup_init(void); +extern void device_pager_init(void); +extern void io_done_thread(void) __attribute__ ((noreturn)); + +io_return_t ds_device_write_trap( + device_t dev, + dev_mode_t mode, + rpc_recnum_t recnum, + rpc_vm_offset_t data, + rpc_vm_size_t count); + +io_return_t ds_device_writev_trap( + device_t dev, + dev_mode_t mode, + rpc_recnum_t recnum, + rpc_io_buf_vec_t *iovec, + rpc_vm_size_t count); + +#endif /* DS_ROUTINES_H */ diff --git a/device/if_ether.h b/device/if_ether.h new file mode 100644 index 0000000..91d4d9a --- /dev/null +++ b/device/if_ether.h @@ -0,0 +1,52 @@ +/* + * Mach Operating System + * Copyright (c) 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. + */ +/* + * Ethernet definitions. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 8/89 + */ + +#ifndef _DEVICE_IF_ETHER_H_ +#define _DEVICE_IF_ETHER_H_ + +#include + +/* + * Structure of a 10Mb/s Ethernet header. + */ +struct ether_header { + u_char ether_dhost[6]; + u_char ether_shost[6]; + u_short ether_type; +}; + +#ifdef KERNEL +extern char * ether_sprintf(const u_char *); +#endif /* KERNEL */ + +#endif /*_DEVICE_IF_ETHER_H_*/ diff --git a/device/if_hdr.h b/device/if_hdr.h new file mode 100644 index 0000000..e53983b --- /dev/null +++ b/device/if_hdr.h @@ -0,0 +1,165 @@ +/* + * 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. + */ +/* + * Taken from (bsd)net/if.h. Modified for MACH kernel. + */ +/* + * Copyright (c) 1982, 1986 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. The name of the Laboratory may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 7.3 (Berkeley) 6/27/88 + */ + +#ifndef _IF_HDR_ +#define _IF_HDR_ + +#include +#include + +/* + * Queue for network output and filter input. + */ +struct ifqueue { + queue_head_t ifq_head; /* queue of io_req_t */ + int ifq_len; /* length of queue */ + int ifq_maxlen; /* maximum length of queue */ + int ifq_drops; /* number of packets dropped + because queue full */ + decl_simple_lock_data(, + ifq_lock) /* lock for queue and counters */ +}; + +/* + * Header for network interface drivers. + */ +struct ifnet { + short if_unit; /* unit number */ + short if_flags; /* up/down, broadcast, etc. */ + short if_timer; /* time until if_watchdog called */ + short if_mtu; /* maximum transmission unit */ + short if_header_size; /* length of header */ + short if_header_format; /* format of hardware header */ + short if_address_size; /* length of hardware address */ + short if_alloc_size; /* size of read buffer to allocate */ + char *if_address; /* pointer to hardware address */ + struct ifqueue if_snd; /* output queue */ + queue_head_t if_rcv_port_list; /* input filter list */ + queue_head_t if_snd_port_list; /* output filter list */ + decl_simple_lock_data(, + if_rcv_port_list_lock) /* lock for input filter list */ + decl_simple_lock_data(, + if_snd_port_list_lock) /* lock for output filter list */ +/* statistics */ + int if_ipackets; /* packets received */ + int if_ierrors; /* input errors */ + int if_opackets; /* packets sent */ + int if_oerrors; /* output errors */ + int if_collisions; /* collisions on csma interfaces */ + int if_rcvdrops; /* packets received but dropped */ +}; + +#define IFF_UP 0x0001 /* interface is up */ +#define IFF_BROADCAST 0x0002 /* interface can broadcast */ +#define IFF_DEBUG 0x0004 /* turn on debugging */ +#define IFF_LOOPBACK 0x0008 /* is a loopback net */ +#define IFF_POINTOPOINT 0x0010 /* point-to-point link */ +#define IFF_RUNNING 0x0040 /* resources allocated */ +#define IFF_NOARP 0x0080 /* no address resolution protocol */ +#define IFF_PROMISC 0x0100 /* receive all packets */ +#define IFF_ALLMULTI 0x0200 /* receive all multicast packets */ +#define IFF_BRIDGE 0x0100 /* support token ring routing field */ +#define IFF_SNAP 0x0200 /* support extended sap header */ + +/* internal flags only: */ +#define IFF_CANTCHANGE (IFF_BROADCAST | IFF_POINTOPOINT | IFF_RUNNING) + +/* + * Output queues (ifp->if_snd) + * have queues of messages stored on ifqueue structures. Entries + * are added to and deleted from these structures by these macros, which + * should be called with ipl raised to splimp(). + * XXX locking XXX + */ + +#define IF_QFULL(ifq) ((ifq)->ifq_len >= (ifq)->ifq_maxlen) +#define IF_DROP(ifq) ((ifq)->ifq_drops++) +#define IF_ENQUEUE(ifq, ior) { \ + simple_lock(&(ifq)->ifq_lock); \ + enqueue_tail(&(ifq)->ifq_head, (queue_entry_t)ior); \ + (ifq)->ifq_len++; \ + simple_unlock(&(ifq)->ifq_lock); \ +} +#define IF_PREPEND(ifq, ior) { \ + simple_lock(&(ifq)->ifq_lock); \ + enqueue_head(&(ifq)->ifq_head, (queue_entry_t)ior); \ + (ifq)->ifq_len++; \ + simple_unlock(&(ifq)->ifq_lock); \ +} + +#define IF_DEQUEUE(ifq, ior) { \ + simple_lock(&(ifq)->ifq_lock); \ + if (((ior) = (io_req_t)dequeue_head(&(ifq)->ifq_head)) != 0) \ + (ifq)->ifq_len--; \ + simple_unlock(&(ifq)->ifq_lock); \ +} + +#define IFQ_MAXLEN 50 + +#define IFQ_INIT(ifq) { \ + queue_init(&(ifq)->ifq_head); \ + simple_lock_init(&(ifq)->ifq_lock); \ + (ifq)->ifq_len = 0; \ + (ifq)->ifq_maxlen = IFQ_MAXLEN; \ + (ifq)->ifq_drops = 0; \ +} + +#define IFNET_SLOWHZ 1 /* granularity is 1 second */ + +#endif /* _IF_HDR_ */ diff --git a/device/intr.c b/device/intr.c new file mode 100644 index 0000000..9035c03 --- /dev/null +++ b/device/intr.c @@ -0,0 +1,372 @@ +/* + * Copyright (c) 2010, 2011, 2016, 2019 Free Software Foundation, Inc. + * + * 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. + * + * THE FREE SOFTWARE FOUNDATIONALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. THE FREE SOFTWARE FOUNDATION DISCLAIMS ANY LIABILITY OF ANY KIND + * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef MACH_XEN + +queue_head_t main_intr_queue; +static boolean_t deliver_intr (int id, ipc_port_t dst_port); + +#ifndef LINUX_DEV +#define SA_SHIRQ 0x04000000 + +struct intr_list { + user_intr_t *user_intr; + unsigned long flags; + struct intr_list *next; +}; +static struct intr_list *user_intr_handlers[NINTR]; +#endif + +static user_intr_t * +search_intr (struct irqdev *dev, ipc_port_t dst_port) +{ + user_intr_t *e; + queue_iterate (dev->intr_queue, e, user_intr_t *, chain) + { + if (e->dst_port == dst_port) + return e; + } + return NULL; +} + + +/* + * Interrupt handling logic: + * + * interrupt.S raises spl (thus IF cleared) + * interrupt.S EOI + * interrupt.S calls the handler + * - for pure in-kernel handlers, they do whatever they want with IF cleared. + * - when a userland handler is registered, queue_intr masks the irq. + * interrupt.S lowers spl with splx_cli, thus IF still cleared + * iret, that also sets IF + * + * later on, (irq_acknowledge), userland acks the IRQ, that unmasks the irq + */ +kern_return_t +irq_acknowledge (ipc_port_t receive_port) +{ + user_intr_t *e; + kern_return_t ret = 0; + + spl_t s = splhigh (); + e = search_intr (&irqtab, receive_port); + + if (!e) + { + printf("didn't find user intr for interrupt !?\n"); + ret = KERN_INVALID_ARGUMENT; + } + else + { + if (!e->n_unacked) + ret = D_INVALID_OPERATION; + else + e->n_unacked--; + } + splx (s); + + if (ret) + return ret; + + __enable_irq (irqtab.irq[e->id]); + + return D_SUCCESS; +} + +/* This function can only be used in the interrupt handler. */ +static void +queue_intr (struct irqdev *dev, int id, user_intr_t *e) +{ + /* Until userland has handled the IRQ in the driver, we have to keep it + * disabled. Level-triggered interrupts would keep raising otherwise. */ + __disable_irq (dev->irq[id]); + + spl_t s = splhigh (); + e->n_unacked++; + e->interrupts++; + dev->tot_num_intr++; + splx (s); + + thread_wakeup ((event_t) &intr_thread); +} + +int +deliver_user_intr (struct irqdev *dev, int id, user_intr_t *e) +{ + /* The reference of the port was increased + * when the port was installed. If the reference is 1, it means + * the port was deallocated and we should clean after it. */ + if (!e->dst_port || e->dst_port->ip_references == 1) + { + thread_wakeup ((event_t) &intr_thread); + return 0; + } + else + { + queue_intr (dev, id, e); + return 1; + } +} + +/* insert an interrupt entry in the queue. + * This entry exists in the queue until + * the corresponding interrupt port is removed.*/ +user_intr_t * +insert_intr_entry (struct irqdev *dev, int id, ipc_port_t dst_port) +{ + user_intr_t *e, *new, *ret; + int free = 0; + + new = (user_intr_t *) kalloc (sizeof (*new)); + if (new == NULL) + return NULL; + + /* check whether the intr entry has been in the queue. */ + spl_t s = splhigh (); + e = search_intr (dev, dst_port); + if (e) + { + printf ("the interrupt entry for irq[%d] and port %p has already been inserted\n", id, dst_port); + free = 1; + ret = NULL; + goto out; + } + printf("irq handler [%d]: new delivery port %p entry %p\n", id, dst_port, new); + ret = new; + new->id = id; + new->dst_port = dst_port; + new->interrupts = 0; + new->n_unacked = 0; + + queue_enter (dev->intr_queue, new, user_intr_t *, chain); +out: + splx (s); + if (free) + kfree ((vm_offset_t) new, sizeof (*new)); + return ret; +} + +#ifndef LINUX_DEV + +static void +user_irq_handler (int id) +{ + struct intr_list *handler; + struct intr_list **prev = &user_intr_handlers[id]; + user_intr_t *e; + spl_t s; + + s = splhigh(); + + for (handler = *prev; handler; handler = handler->next) + { + e = handler->user_intr; + if (!deliver_user_intr(&irqtab, id, e)) + { + /* We failed to deliver this interrupt, remove handler from list */ + *prev = handler->next; + } + prev = &handler->next; + } + splx(s); +} + +int +install_user_intr_handler (struct irqdev *dev, int id, unsigned long flags, + user_intr_t *user_intr) +{ + unsigned int irq = dev->irq[id]; + struct intr_list **head = &user_intr_handlers[id]; + struct intr_list *new, *old = *head; + spl_t s; + + flags |= SA_SHIRQ; + + assert (irq < NINTR); + + /* Don't allow overriding hardclock/kdintr etc */ + if ((ivect[irq] != user_irq_handler) && (ivect[irq] != intnull)) + { + printf("You can't have this interrupt\n"); + return D_ALREADY_OPEN; + } + + if (old) + { + if (!(old->flags & flags & SA_SHIRQ)) + { + printf ("Cannot share irq\n"); + return D_ALREADY_OPEN; + } + } + + new = (struct intr_list *)kalloc (sizeof (struct intr_list)); + new->user_intr = user_intr; + new->flags = flags; + + s = splhigh(); + new->next = *head; + *head = new; + ivect[irq] = user_irq_handler; + iunit[irq] = (int)irq; + unmask_irq (irq); + splx(s); + + return D_SUCCESS; +} +#endif + +void +intr_thread (void) +{ + user_intr_t *e; + int id; + ipc_port_t dst_port; + queue_init (&main_intr_queue); + + for (;;) + { + assert_wait ((event_t) &intr_thread, FALSE); + /* Make sure we wake up from times to times to check for aborted processes */ + thread_set_timeout (hz); + spl_t s = splhigh (); + + /* Now check for interrupts */ + int del; + do + { + del = 0; + + queue_iterate (&main_intr_queue, e, user_intr_t *, chain) + { + /* The reference of the port was increased + * when the port was installed. If the reference is 1, it means + * the port was deallocated and we should clean after it. */ + if (e->dst_port->ip_references == 1) + { + clear_wait (current_thread (), 0, 0); + del = 1; + break; + } + + if (e->interrupts) + { + clear_wait (current_thread (), 0, 0); + id = e->id; + dst_port = e->dst_port; + e->interrupts--; + irqtab.tot_num_intr--; + + splx (s); + deliver_intr (id, dst_port); + s = splhigh (); + } + } + + /* remove the entry without dest port from the queue and free it. */ + if (del) + { + /* + * We clear unacked irqs now, so the Linux handling can trigger, + * and we will cleanup later after the Linux handler is cleared. + */ + assert (!queue_empty (&main_intr_queue)); + queue_remove (&main_intr_queue, e, user_intr_t *, chain); + + printf ("irq handler [%d]: release a dead delivery port %p entry %p\n", e->id, e->dst_port, e); + ipc_port_release (e->dst_port); + e->dst_port = MACH_PORT_NULL; + + if (e->n_unacked) + printf("irq handler [%d]: still %d unacked irqs in entry %p\n", e->id, e->n_unacked, e); + while (e->n_unacked) + { + __enable_irq (irqtab.irq[e->id]); + e->n_unacked--; + } + +#if 0 +#ifndef LINUX_DEV + // TODO: remove from the action list +#else + // FIXME: with the Linux irq handler we don't actually control the action list +#endif + splx (s); + kfree ((vm_offset_t) e, sizeof (*e)); + s = splhigh (); +#endif + } + } + while (del || irqtab.tot_num_intr); + splx (s); + thread_block (NULL); + } +} + +static boolean_t +deliver_intr (int id, ipc_port_t dst_port) +{ + ipc_kmsg_t kmsg; + device_intr_notification_t *n; + mach_port_t dest = (mach_port_t) dst_port; + + if (dest == MACH_PORT_NULL) + return FALSE; + + kmsg = ikm_alloc(sizeof *n); + if (kmsg == IKM_NULL) + return FALSE; + + ikm_init(kmsg, sizeof *n); + n = (device_intr_notification_t *) &kmsg->ikm_header; + + mach_msg_header_t *m = &n->intr_header; + mach_msg_type_t *t = &n->intr_type; + + m->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND, 0); + m->msgh_size = sizeof *n; + m->msgh_seqno = DEVICE_NOTIFY_MSGH_SEQNO; + m->msgh_local_port = MACH_PORT_NULL; + m->msgh_remote_port = MACH_PORT_NULL; + m->msgh_id = DEVICE_INTR_NOTIFY; + + t->msgt_name = MACH_MSG_TYPE_INTEGER_32; + t->msgt_size = 32; + t->msgt_number = 1; + t->msgt_inline = TRUE; + t->msgt_longform = FALSE; + t->msgt_deallocate = FALSE; + t->msgt_unused = 0; + + n->intr_header.msgh_remote_port = dest; + n->id = id; + + ipc_port_copy_send (dst_port); + ipc_mqueue_send_always(kmsg); + + return TRUE; +} + +#endif /* MACH_XEN */ diff --git a/device/intr.h b/device/intr.h new file mode 100644 index 0000000..cd3e0bc --- /dev/null +++ b/device/intr.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2010, 2011, 2019 Free Software Foundation, Inc. + * + * 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. + * + * THE FREE SOFTWARE FOUNDATIONALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. THE FREE SOFTWARE FOUNDATION DISCLAIMS ANY LIABILITY OF ANY KIND + * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + */ + +#ifndef __INTR_H__ +#define __INTR_H__ + +#ifndef MACH_XEN + +#include +#include +#include +#include +#include + +#define DEVICE_NOTIFY_MSGH_SEQNO 0 + +#include + +struct irqdev; +#include + +typedef struct { + queue_chain_t chain; + int interrupts; /* Number of interrupts occurred since last run of intr_thread */ + int n_unacked; /* Number of times irqs were disabled for this */ + ipc_port_t dst_port; /* Notification port */ + int id; /* Mapping to machine dependent irq_t array elem */ +} user_intr_t; + +struct irqdev { + char *name; + void (*irqdev_ack)(struct irqdev *dev, int id); + + queue_head_t *intr_queue; + int tot_num_intr; /* Total number of unprocessed interrupts */ + + /* Machine dependent */ + irq_t irq[NINTR]; +}; + +extern queue_head_t main_intr_queue; +extern int install_user_intr_handler (struct irqdev *dev, int id, unsigned long flags, user_intr_t *e); +extern int deliver_user_intr (struct irqdev *dev, int id, user_intr_t *e); +extern user_intr_t *insert_intr_entry (struct irqdev *dev, int id, ipc_port_t receive_port); + +void intr_thread (void); +kern_return_t irq_acknowledge (ipc_port_t receive_port); + +#endif /* MACH_XEN */ + +#endif diff --git a/device/io_req.h b/device/io_req.h new file mode 100644 index 0000000..fb63696 --- /dev/null +++ b/device/io_req.h @@ -0,0 +1,145 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988 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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 10/88 + */ + +#ifndef _IO_REQ_ +#define _IO_REQ_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + * IO request element, queued on device for delayed replies. + */ +typedef struct io_req *io_req_t; +struct io_req { + struct io_req * io_next; /* next, ... */ + struct io_req * io_prev; /* prev pointers: link in done, + defered, or in-progress list */ + mach_device_t io_device; /* pointer to open-device structure */ + char * io_dev_ptr; /* pointer to driver structure - + filled in by driver if necessary */ + int io_unit; /* unit number ('minor') of device */ + int io_op; /* IO operation */ + dev_mode_t io_mode; /* operation mode (wait, truncate) */ + recnum_t io_recnum; /* starting record number for + random-access devices */ + + union io_un { + io_buf_ptr_t data; /* data, for IO requests */ + } io_un; +#define io_data io_un.data + + long io_count; /* amount requested */ + vm_size_t io_alloc_size; /* amount allocated */ + long io_residual; /* amount NOT done */ + io_return_t io_error; /* error code */ + /* call when done - returns TRUE if IO really finished */ + boolean_t (*io_done)(io_req_t); + struct ipc_port *io_reply_port; /* reply port, for asynchronous + messages */ + mach_msg_type_name_t io_reply_port_type; + /* send or send-once right? */ + struct io_req * io_link; /* forward link (for driver header) */ + struct io_req * io_rlink; /* reverse link (for driver header) */ + vm_map_copy_t io_copy; /* vm_map_copy obj. for this op. */ + long io_total; /* total op size, for write */ + decl_simple_lock_data(,io_req_lock) + /* Lock for this structure */ + long io_physrec; /* mapping to the physical block + number */ + long io_rectotal; /* total number of blocks to move */ +}; + +/* + * LOCKING NOTE: Operations on io_req's are in general single threaded by + * the invoking code, obviating the need for a lock. The usual IO_CALL + * path through the code is: Initiating thread hands io_req to device driver, + * driver passes it to io_done thread, io_done thread sends reply message. No + * locking is needed in this sequence. Unfortunately, a synchronous wait + * for a buffer requires a lock to avoid problems if the wait and interrupt + * happen simultaneously on different processors. + * + * Shall be taken at splio only + */ + +#define ior_lock(ior) simple_lock(&(ior)->io_req_lock) +#define ior_unlock(ior) simple_unlock(&(ior)->io_req_lock) + +/* + * Flags and operations + */ + +#define IO_WRITE 0x00000000 /* operation is write */ +#define IO_READ 0x00000001 /* operation is read */ +#define IO_OPEN 0x00000002 /* operation is open */ +#define IO_DONE 0x00000100 /* operation complete */ +#define IO_ERROR 0x00000200 /* error on operation */ +#define IO_BUSY 0x00000400 /* operation in progress */ +#define IO_WANTED 0x00000800 /* wakeup when no longer BUSY */ +#define IO_BAD 0x00001000 /* bad disk block */ +#define IO_CALL 0x00002000 /* call io_done_thread when done */ +#define IO_INBAND 0x00004000 /* mig call was inband */ +#define IO_INTERNAL 0x00008000 /* internal, device-driver specific */ +#define IO_LOANED 0x00010000 /* ior loaned by another module */ + +#define IO_SPARE_START 0x00020000 /* start of spare flags */ + +/* + * Standard completion routine for io_requests. + */ +void iodone(io_req_t); + +/* + * Macros to allocate and free IORs - will convert to caches later. + */ +#define io_req_alloc(ior,size) \ + MACRO_BEGIN \ + (ior) = (io_req_t)kalloc(sizeof(struct io_req)); \ + simple_lock_init(&(ior)->io_req_lock); \ + MACRO_END + +#define io_req_free(ior) \ + (kfree((vm_offset_t)(ior), sizeof(struct io_req))) + + +extern struct kmem_cache io_inband_cache; /* for inband reads */ + +#endif /* _IO_REQ_ */ diff --git a/device/kmsg.c b/device/kmsg.c new file mode 100644 index 0000000..e5b518e --- /dev/null +++ b/device/kmsg.c @@ -0,0 +1,254 @@ +/* GNU Mach Kernel Message Device. + + Copyright (C) 1998, 1999, 2007 Free Software Foundation, Inc. + + Written by OKUJI Yoshinori. + +This is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +This software is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with the software; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* kmsg provides a stream interface. */ + +#include +#include + +#include +#include +#include +#include +#include +#include + + +#define KMSGBUFSIZE (4096) /* XXX */ + +/* Simple array for buffering messages */ +static char kmsg_buffer[KMSGBUFSIZE]; +/* Point to the offset to write */ +static int kmsg_write_offset; +/* Point to the offset to read */ +static int kmsg_read_offset; +/* I/O request queue for blocking read */ +static queue_head_t kmsg_read_queue; +/* Used for exclusive access to the device */ +static boolean_t kmsg_in_use; +/* Used for exclusive access to the routines */ +def_simple_lock_irq_data (static, kmsg_lock); +/* If already initialized or not */ +static boolean_t kmsg_init_done = FALSE; + +/* Kernel Message Initializer */ +static void +kmsginit (void) +{ + kmsg_write_offset = 0; + kmsg_read_offset = 0; + queue_init (&kmsg_read_queue); + kmsg_in_use = FALSE; + simple_lock_init_irq (&kmsg_lock); +} + +/* Kernel Message Open Handler */ +io_return_t +kmsgopen (dev_t dev, int flag, const io_req_t ior) +{ + spl_t s = simple_lock_irq (&kmsg_lock); + if (kmsg_in_use) + { + simple_unlock_irq (s, &kmsg_lock); + return D_ALREADY_OPEN; + } + + kmsg_in_use = TRUE; + + simple_unlock_irq (s, &kmsg_lock); + return D_SUCCESS; +} + +/* Kernel Message Close Handler */ +void +kmsgclose (dev_t dev, int flag) +{ + spl_t s = simple_lock_irq (&kmsg_lock); + kmsg_in_use = FALSE; + + simple_unlock_irq (s, &kmsg_lock); +} + +static boolean_t kmsg_read_done (io_req_t ior); + +/* Kernel Message Read Handler */ +io_return_t +kmsgread (dev_t dev, io_req_t ior) +{ + int err; + int amt, len; + + err = device_read_alloc (ior, ior->io_count); + if (err != KERN_SUCCESS) + return err; + + spl_t s = simple_lock_irq (&kmsg_lock); + if (kmsg_read_offset == kmsg_write_offset) + { + /* The queue is empty. */ + if (ior->io_mode & D_NOWAIT) + { + simple_unlock_irq (s, &kmsg_lock); + return D_WOULD_BLOCK; + } + + ior->io_done = kmsg_read_done; + enqueue_tail (&kmsg_read_queue, (queue_entry_t) ior); + simple_unlock_irq (s, &kmsg_lock); + return D_IO_QUEUED; + } + + len = kmsg_write_offset - kmsg_read_offset; + if (len < 0) + len += KMSGBUFSIZE; + + amt = ior->io_count; + if (amt > len) + amt = len; + + if (kmsg_read_offset + amt <= KMSGBUFSIZE) + { + memcpy (ior->io_data, kmsg_buffer + kmsg_read_offset, amt); + } + else + { + int cnt; + + cnt = KMSGBUFSIZE - kmsg_read_offset; + memcpy (ior->io_data, kmsg_buffer + kmsg_read_offset, cnt); + memcpy (ior->io_data + cnt, kmsg_buffer, amt - cnt); + } + + kmsg_read_offset += amt; + if (kmsg_read_offset >= KMSGBUFSIZE) + kmsg_read_offset -= KMSGBUFSIZE; + + ior->io_residual = ior->io_count - amt; + + simple_unlock_irq (s, &kmsg_lock); + return D_SUCCESS; +} + +static boolean_t +kmsg_read_done (io_req_t ior) +{ + int amt, len; + + spl_t s = simple_lock_irq (&kmsg_lock); + if (kmsg_read_offset == kmsg_write_offset) + { + /* The queue is empty. */ + ior->io_done = kmsg_read_done; + enqueue_tail (&kmsg_read_queue, (queue_entry_t) ior); + simple_unlock_irq (s, &kmsg_lock); + return FALSE; + } + + len = kmsg_write_offset - kmsg_read_offset; + if (len < 0) + len += KMSGBUFSIZE; + + amt = ior->io_count; + if (amt > len) + amt = len; + + if (kmsg_read_offset + amt <= KMSGBUFSIZE) + { + memcpy (ior->io_data, kmsg_buffer + kmsg_read_offset, amt); + } + else + { + int cnt; + + cnt = KMSGBUFSIZE - kmsg_read_offset; + memcpy (ior->io_data, kmsg_buffer + kmsg_read_offset, cnt); + memcpy (ior->io_data + cnt, kmsg_buffer, amt - cnt); + } + + kmsg_read_offset += amt; + if (kmsg_read_offset >= KMSGBUFSIZE) + kmsg_read_offset -= KMSGBUFSIZE; + + ior->io_residual = ior->io_count - amt; + + simple_unlock_irq (s, &kmsg_lock); + ds_read_done (ior); + + return TRUE; +} + +io_return_t +kmsggetstat (dev_t dev, dev_flavor_t flavor, dev_status_t data, mach_msg_type_number_t *count) +{ + switch (flavor) + { + case DEV_GET_SIZE: + data[DEV_GET_SIZE_DEVICE_SIZE] = 0; + data[DEV_GET_SIZE_RECORD_SIZE] = 1; + *count = DEV_GET_SIZE_COUNT; + break; + + default: + return D_INVALID_OPERATION; + } + + return D_SUCCESS; +} + +/* Write to Kernel Message Buffer */ +void +kmsg_putchar (int c) +{ + io_req_t ior; + int offset; + spl_t s = -1; + + /* XXX: cninit is not called before cnputc is used. So call kmsginit + here if not initialized yet. */ + if (!kmsg_init_done) + { + kmsginit (); + kmsg_init_done = TRUE; + } + + if (spl_init) + s = simple_lock_irq (&kmsg_lock); + offset = kmsg_write_offset + 1; + if (offset == KMSGBUFSIZE) + offset = 0; + + if (offset == kmsg_read_offset) + { + /* Discard C. */ + if (spl_init) + simple_unlock_irq (s, &kmsg_lock); + return; + } + + kmsg_buffer[kmsg_write_offset++] = c; + if (kmsg_write_offset == KMSGBUFSIZE) + kmsg_write_offset = 0; + + while ((ior = (io_req_t) dequeue_head (&kmsg_read_queue)) != NULL) + iodone (ior); + + if (spl_init) + simple_unlock_irq (s, &kmsg_lock); +} diff --git a/device/kmsg.h b/device/kmsg.h new file mode 100644 index 0000000..00a3505 --- /dev/null +++ b/device/kmsg.h @@ -0,0 +1,18 @@ +#ifndef _DEVICE_KMSG_H_ +#define _DEVICE_KMSG_H_ 1 + + +#include + +#include +#include + +io_return_t kmsgopen (dev_t dev, int flag, io_req_t ior); +void kmsgclose (dev_t dev, int flag); +io_return_t kmsgread (dev_t dev, io_req_t ior); +io_return_t kmsggetstat (dev_t dev, dev_flavor_t flavor, + dev_status_t data, mach_msg_type_number_t *count); +void kmsg_putchar (int c); + + +#endif /* !_DEVICE_KMSG_H_ */ diff --git a/device/memory_object_reply.cli b/device/memory_object_reply.cli new file mode 100644 index 0000000..f2cd480 --- /dev/null +++ b/device/memory_object_reply.cli @@ -0,0 +1,27 @@ +/* + * Copyright (c) 1994 The University of Utah and + * the Computer Systems Laboratory at the University of Utah (CSL). + * All rights reserved. + * + * Permission to use, copy, modify and distribute this software is hereby + * granted provided that (1) source code retains these copyright, permission, + * and disclaimer notices, and (2) redistributions including binaries + * reproduce the notices in supporting documentation, and (3) all advertising + * materials mentioning features or use of this software display the following + * acknowledgement: ``This product includes software developed by the + * Computer Systems Laboratory at the University of Utah.'' + * + * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS + * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF + * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * CSL requests users of this software to return to csl-dist@cs.utah.edu any + * improvements that they make and grant CSL redistribution rights. + * + * Author: Bryan Ford, University of Utah CSL + */ +/* This is a client presentation file. */ + +#define KERNEL_USER 1 + +#include diff --git a/device/net_io.c b/device/net_io.c new file mode 100644 index 0000000..ee9435d --- /dev/null +++ b/device/net_io.c @@ -0,0 +1,2153 @@ + /* + * Mach Operating System + * Copyright (c) 1993-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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 3/98 + * + * Network IO. + * + * Packet filter code taken from vaxif/enet.c written + * CMU and Stanford. + */ + +/* + * Note: don't depend on anything in this file. + * It may change a lot real soon. -cmaeda 11 June 1993 + */ + +#include +#include + +#include +#include /* spl definitions */ +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#if MACH_TTD +#include +#endif /* MACH_TTD */ + +#if MACH_TTD +int kttd_async_counter= 0; +#endif /* MACH_TTD */ + + +/* + * Packet Buffer Management + * + * This module manages a private pool of kmsg buffers. + */ + +/* + * List of net kmsgs queued to be sent to users. + * Messages can be high priority or low priority. + * The network thread processes high priority messages first. + */ +def_simple_lock_data(static,net_queue_lock) +boolean_t net_thread_awake = FALSE; +struct ipc_kmsg_queue net_queue_high; +int net_queue_high_size = 0; +int net_queue_high_max = 0; /* for debugging */ +struct ipc_kmsg_queue net_queue_low; +int net_queue_low_size = 0; +int net_queue_low_max = 0; /* for debugging */ + +/* + * List of net kmsgs that can be touched at interrupt level. + * If it is empty, we will also steal low priority messages. + */ +def_simple_lock_data(static,net_queue_free_lock) +struct ipc_kmsg_queue net_queue_free; +int net_queue_free_size = 0; /* on free list */ +int net_queue_free_max = 0; /* for debugging */ + +/* + * This value is critical to network performance. + * At least this many buffers should be sitting in net_queue_free. + * If this is set too small, we will drop network packets. + * Even a low drop rate (<1%) can cause severe network throughput problems. + * We add one to net_queue_free_min for every filter. + */ +int net_queue_free_min = 3; + +int net_queue_free_hits = 0; /* for debugging */ +int net_queue_free_steals = 0; /* for debugging */ +int net_queue_free_misses = 0; /* for debugging */ + +int net_kmsg_send_high_hits = 0; /* for debugging */ +int net_kmsg_send_low_hits = 0; /* for debugging */ +int net_kmsg_send_high_misses = 0; /* for debugging */ +int net_kmsg_send_low_misses = 0; /* for debugging */ + +int net_thread_awaken = 0; /* for debugging */ +int net_ast_taken = 0; /* for debugging */ + +def_simple_lock_data(static,net_kmsg_total_lock) +int net_kmsg_total = 0; /* total allocated */ +int net_kmsg_max; /* initialized below */ + +vm_size_t net_kmsg_size; /* initialized below */ + +/* + * We want more buffers when there aren't enough in the free queue + * and the low priority queue. However, we don't want to allocate + * more than net_kmsg_max. + */ + +#define net_kmsg_want_more() \ + (((net_queue_free_size + net_queue_low_size) < net_queue_free_min) && \ + (net_kmsg_total < net_kmsg_max)) + +ipc_kmsg_t +net_kmsg_get(void) +{ + ipc_kmsg_t kmsg; + spl_t s; + + /* + * First check the list of free buffers. + */ + s = splimp(); + simple_lock(&net_queue_free_lock); + kmsg = ipc_kmsg_queue_first(&net_queue_free); + if (kmsg != IKM_NULL) { + ipc_kmsg_rmqueue_first_macro(&net_queue_free, kmsg); + net_queue_free_size--; + net_queue_free_hits++; + } + simple_unlock(&net_queue_free_lock); + + if (kmsg == IKM_NULL) { + /* + * Try to steal from the low priority queue. + */ + simple_lock(&net_queue_lock); + kmsg = ipc_kmsg_queue_first(&net_queue_low); + if (kmsg != IKM_NULL) { + ipc_kmsg_rmqueue_first_macro(&net_queue_low, kmsg); + net_queue_low_size--; + net_queue_free_steals++; + } + simple_unlock(&net_queue_lock); + } + + if (kmsg == IKM_NULL) + net_queue_free_misses++; + (void) splx(s); + + if (net_kmsg_want_more() || (kmsg == IKM_NULL)) { + boolean_t awake; + + s = splimp(); + simple_lock(&net_queue_lock); + awake = net_thread_awake; + net_thread_awake = TRUE; + simple_unlock(&net_queue_lock); + (void) splx(s); + + if (!awake) + thread_wakeup((event_t) &net_thread_awake); + } + + return kmsg; +} + +void +net_kmsg_put(const ipc_kmsg_t kmsg) +{ + spl_t s; + + s = splimp(); + simple_lock(&net_queue_free_lock); + ipc_kmsg_enqueue_macro(&net_queue_free, kmsg); + if (++net_queue_free_size > net_queue_free_max) + net_queue_free_max = net_queue_free_size; + simple_unlock(&net_queue_free_lock); + (void) splx(s); +} + +void +net_kmsg_collect(void) +{ + ipc_kmsg_t kmsg; + spl_t s; + + s = splimp(); + simple_lock(&net_queue_free_lock); + while (net_queue_free_size > net_queue_free_min) { + kmsg = ipc_kmsg_dequeue(&net_queue_free); + net_queue_free_size--; + simple_unlock(&net_queue_free_lock); + (void) splx(s); + + net_kmsg_free(kmsg); + simple_lock(&net_kmsg_total_lock); + net_kmsg_total--; + simple_unlock(&net_kmsg_total_lock); + + s = splimp(); + simple_lock(&net_queue_free_lock); + } + simple_unlock(&net_queue_free_lock); + (void) splx(s); +} + +static void +net_kmsg_more(void) +{ + ipc_kmsg_t kmsg; + + /* + * Replenish net kmsg pool if low. We don't have the locks + * necessary to look at these variables, but that's OK because + * misread values aren't critical. The danger in this code is + * that while we allocate buffers, interrupts are happening + * which take buffers out of the free list. If we are not + * careful, we will sit in the loop and allocate a zillion + * buffers while a burst of packets arrives. So we count + * buffers in the low priority queue as available, because + * net_kmsg_get will make use of them, and we cap the total + * number of buffers we are willing to allocate. + */ + + while (net_kmsg_want_more()) { + simple_lock(&net_kmsg_total_lock); + net_kmsg_total++; + simple_unlock(&net_kmsg_total_lock); + kmsg = net_kmsg_alloc(); + net_kmsg_put(kmsg); + } +} + +/* + * Packet Filter Data Structures + * + * Each network interface has a set of packet filters + * that are run on incoming packets. + * + * Each packet filter may represent a single network + * session or multiple network sessions. For example, + * all application level TCP sessions would be represented + * by a single packet filter data structure. + * + * If a packet filter has a single session, we use a + * struct net_rcv_port to represent it. If the packet + * filter represents multiple sessions, we use a + * struct net_hash_header to represent it. + */ + +/* + * Each interface has a write port and a set of read ports. + * Each read port has one or more filters to determine what packets + * should go to that port. + */ + +/* + * Receive port for net, with packet filter. + * This data structure by itself represents a packet + * filter for a single session. + */ +struct net_rcv_port { + queue_chain_t input; /* list of input open_descriptors */ + queue_chain_t output; /* list of output open_descriptors */ + ipc_port_t rcv_port; /* port to send packet to */ + int rcv_qlimit; /* port's qlimit */ + int rcv_count; /* number of packets received */ + int priority; /* priority for filter */ + filter_t *filter_end; /* pointer to end of filter */ + filter_t filter[NET_MAX_FILTER]; + /* filter operations */ +}; + +struct kmem_cache net_rcv_cache; /* cache of net_rcv_port structs */ + +#define NET_HASH_SIZE 256 +#define N_NET_HASH 4 +#define N_NET_HASH_KEYS 4 + +/* + * A single hash entry. + */ +struct net_hash_entry { + queue_chain_t chain; /* list of entries with same hval */ +#define he_next chain.next +#define he_prev chain.prev + ipc_port_t rcv_port; /* destination port */ + int rcv_qlimit; /* qlimit for the port */ + unsigned int keys[N_NET_HASH_KEYS]; +}; + +struct kmem_cache net_hash_entry_cache; + +/* + * This structure represents a packet filter with multiple sessions. + * + * For example, all application level TCP sessions might be + * represented by one of these structures. It looks like a + * net_rcv_port struct so that both types can live on the + * same packet filter queues. + */ +struct net_hash_header { + struct net_rcv_port rcv; + int n_keys; /* zero if not used */ + int ref_count; /* reference count */ + net_hash_entry_t table[NET_HASH_SIZE]; +} filter_hash_header[N_NET_HASH]; + +def_simple_lock_data(static,net_hash_header_lock) + +#define HASH_ITERATE(head, elt) (elt) = (net_hash_entry_t) (head); do { +#define HASH_ITERATE_END(head, elt) \ + (elt) = (net_hash_entry_t) queue_next((queue_entry_t) (elt)); \ + } while ((elt) != (head)); + +#define FILTER_ITERATE(if_port_list, fp, nextfp, chain) \ + for ((fp) = (net_rcv_port_t) queue_first(if_port_list); \ + !queue_end(if_port_list, (queue_entry_t)(fp)); \ + (fp) = (nextfp)) { \ + (nextfp) = (net_rcv_port_t) queue_next(chain); +#define FILTER_ITERATE_END } + +/* entry_p must be net_rcv_port_t or net_hash_entry_t */ +#define ENQUEUE_DEAD(dead, entry_p, chain) { \ + (entry_p)->chain.next = (queue_entry_t) (dead); \ + (dead) = (queue_entry_t)(entry_p); \ +} + +/* + * ethernet_priority: + * + * This function properly belongs in the ethernet interfaces; + * it should not be called by this module. (We get packet + * priorities as an argument to net_filter.) It is here + * to avoid massive code duplication. + * + * Returns TRUE for high-priority packets. + */ + +boolean_t ethernet_priority(const ipc_kmsg_t kmsg) +{ + unsigned char *addr = + (unsigned char *) net_kmsg(kmsg)->header; + + /* + * A simplistic check for broadcast packets. + */ + + if ((addr[0] == 0xff) && (addr[1] == 0xff) && + (addr[2] == 0xff) && (addr[3] == 0xff) && + (addr[4] == 0xff) && (addr[5] == 0xff)) + return FALSE; + else + return TRUE; +} + +mach_msg_type_t header_type = { + .msgt_name = MACH_MSG_TYPE_BYTE, + .msgt_size = 8, + .msgt_number = NET_HDW_HDR_MAX, + .msgt_inline = TRUE, + .msgt_longform = FALSE, + .msgt_deallocate = FALSE, + .msgt_unused = 0 +}; + +mach_msg_type_t packet_type = { + .msgt_name = MACH_MSG_TYPE_BYTE, + .msgt_size = 8, + .msgt_number = 0, + .msgt_inline = TRUE, + .msgt_longform = FALSE, + .msgt_deallocate = FALSE, + .msgt_unused = 0 +}; + +/* + * net_deliver: + * + * Called and returns holding net_queue_lock, at splimp. + * Dequeues a message and delivers it at spl0. + * Returns FALSE if no messages. + */ +static boolean_t net_deliver(boolean_t nonblocking) +{ + ipc_kmsg_t kmsg; + boolean_t high_priority; + struct ipc_kmsg_queue send_list; + + /* + * Pick up a pending network message and deliver it. + * Deliver high priority messages before low priority. + */ + + if ((kmsg = ipc_kmsg_dequeue(&net_queue_high)) != IKM_NULL) { + net_queue_high_size--; + high_priority = TRUE; + } else if ((kmsg = ipc_kmsg_dequeue(&net_queue_low)) != IKM_NULL) { + net_queue_low_size--; + high_priority = FALSE; + } else + return FALSE; + simple_unlock(&net_queue_lock); + (void) spl0(); + + /* + * Run the packet through the filters, + * getting back a queue of packets to send. + */ + net_filter(kmsg, &send_list); + + if (!nonblocking) { + /* + * There is a danger of running out of available buffers + * because they all get moved into the high priority queue + * or a port queue. In particular, we might need to + * allocate more buffers as we pull (previously available) + * buffers out of the low priority queue. But we can only + * allocate if we are allowed to block. + */ + net_kmsg_more(); + } + + while ((kmsg = ipc_kmsg_dequeue(&send_list)) != IKM_NULL) { + int count; + + /* + * Fill in the rest of the kmsg. + */ + count = net_kmsg(kmsg)->net_rcv_msg_packet_count; + + ikm_init_special(kmsg, IKM_SIZE_NETWORK); + + kmsg->ikm_header.msgh_bits = + MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND, 0); + /* remember message sizes must be rounded up */ + kmsg->ikm_header.msgh_size = + (mach_msg_size_t) P2ROUND(sizeof(struct net_rcv_msg) + - sizeof net_kmsg(kmsg)->sent + - NET_RCV_MAX + count, + __alignof__ (uintptr_t)); + kmsg->ikm_header.msgh_local_port = MACH_PORT_NULL; + kmsg->ikm_header.msgh_kind = MACH_MSGH_KIND_NORMAL; + kmsg->ikm_header.msgh_id = NET_RCV_MSG_ID; + + net_kmsg(kmsg)->header_type = header_type; + net_kmsg(kmsg)->packet_type = packet_type; + net_kmsg(kmsg)->net_rcv_msg_packet_count = count; + + /* + * Send the packet to the destination port. Drop it + * if the destination port is over its backlog. + */ + + if (ipc_mqueue_send(kmsg, MACH_SEND_TIMEOUT, 0) == + MACH_MSG_SUCCESS) { + if (high_priority) + net_kmsg_send_high_hits++; + else + net_kmsg_send_low_hits++; + /* the receiver is responsible for the message now */ + } else { + if (high_priority) + net_kmsg_send_high_misses++; + else + net_kmsg_send_low_misses++; + ipc_kmsg_destroy(kmsg); + } + } + + (void) splimp(); + simple_lock(&net_queue_lock); + return TRUE; +} + +/* + * We want to deliver packets using ASTs, so we can avoid the + * thread_wakeup/thread_block needed to get to the network + * thread. However, we can't allocate memory in the AST handler, + * because memory allocation might block. Hence we have the + * network thread to allocate memory. The network thread also + * delivers packets, so it can be allocating and delivering for a + * burst. net_thread_awake is protected by net_queue_lock + * (instead of net_queue_free_lock) so that net_packet and + * net_ast can safely determine if the network thread is running. + * This prevents a race that might leave a packet sitting without + * being delivered. It is possible for net_kmsg_get to think + * the network thread is awake, and so avoid a wakeup, and then + * have the network thread sleep without allocating. The next + * net_kmsg_get will do a wakeup. + */ + +void net_ast(void) +{ + spl_t s; + + net_ast_taken++; + + /* + * If the network thread is awake, then we would + * rather deliver messages from it, because + * it can also allocate memory. + */ + + s = splimp(); + simple_lock(&net_queue_lock); + while (!net_thread_awake && net_deliver(TRUE)) + continue; + + /* + * Prevent an unnecessary AST. Either the network + * thread will deliver the messages, or there are + * no messages left to deliver. + */ + + simple_unlock(&net_queue_lock); + (void) splsched(); + ast_off(cpu_number(), AST_NETWORK); + (void) splx(s); +} + +static void __attribute__ ((noreturn)) net_thread_continue(void) +{ + for (;;) { + spl_t s; + + net_thread_awaken++; + + /* + * First get more buffers. + */ + net_kmsg_more(); + + s = splimp(); + simple_lock(&net_queue_lock); + while (net_deliver(FALSE)) + continue; + + net_thread_awake = FALSE; + assert_wait(&net_thread_awake, FALSE); + simple_unlock(&net_queue_lock); + (void) splx(s); + counter(c_net_thread_block++); + thread_block(net_thread_continue); + } +} + +void net_thread(void) +{ + spl_t s; + + /* + * We should be very high priority. + */ + + thread_set_own_priority(0); + + /* + * We sleep initially, so that we don't allocate any buffers + * unless the network is really in use and they are needed. + */ + + s = splimp(); + simple_lock(&net_queue_lock); + net_thread_awake = FALSE; + assert_wait(&net_thread_awake, FALSE); + simple_unlock(&net_queue_lock); + (void) splx(s); + counter(c_net_thread_block++); + thread_block(net_thread_continue); + net_thread_continue(); + /*NOTREACHED*/ +} + +static void +reorder_queue( + queue_t first, + queue_t last) +{ + queue_entry_t prev, next; + + prev = first->prev; + next = last->next; + + prev->next = last; + next->prev = first; + + last->prev = prev; + last->next = first; + + first->next = next; + first->prev = last; +} + +/* + * Incoming packet. Header has already been moved to proper place. + * We are already at splimp. + */ +void +net_packet( + struct ifnet *ifp, + ipc_kmsg_t kmsg, + unsigned int count, + boolean_t priority) +{ + boolean_t awake; + +#if MACH_TTD + /* + * Do a quick check to see if it is a kernel TTD packet. + * + * Only check if KernelTTD is enabled, ie. the current + * device driver supports TTD, and the bootp succeeded. + */ + if (kttd_enabled && kttd_handle_async(kmsg)) { + /* + * Packet was a valid ttd packet and + * doesn't need to be passed up to filter. + * The ttd code put the used kmsg buffer + * back onto the free list. + */ + if (kttd_debug) + printf("**%x**", kttd_async_counter++); + return; + } +#endif /* MACH_TTD */ + + kmsg->ikm_header.msgh_remote_port = (mach_port_t) ifp; + net_kmsg(kmsg)->net_rcv_msg_packet_count = count; + + simple_lock(&net_queue_lock); + if (priority) { + ipc_kmsg_enqueue(&net_queue_high, kmsg); + if (++net_queue_high_size > net_queue_high_max) + net_queue_high_max = net_queue_high_size; + } else { + ipc_kmsg_enqueue(&net_queue_low, kmsg); + if (++net_queue_low_size > net_queue_low_max) + net_queue_low_max = net_queue_low_size; + } + /* + * If the network thread is awake, then we don't + * need to take an AST, because the thread will + * deliver the packet. + */ + awake = net_thread_awake; + simple_unlock(&net_queue_lock); + + if (!awake) { + spl_t s = splsched(); + ast_on(cpu_number(), AST_NETWORK); + (void) splx(s); + } +} + +int net_filter_queue_reorder = 0; /* non-zero to enable reordering */ + +/* + * Run a packet through the filters, returning a list of messages. + * We are *not* called at interrupt level. + */ +void +net_filter(const ipc_kmsg_t kmsg, + ipc_kmsg_queue_t send_list) +{ + struct ifnet *ifp; + net_rcv_port_t infp, nextfp; + ipc_kmsg_t new_kmsg; + + net_hash_entry_t entp, *hash_headp; + ipc_port_t dest; + queue_entry_t dead_infp = (queue_entry_t) 0; + queue_entry_t dead_entp = (queue_entry_t) 0; + unsigned int ret_count; + + queue_head_t *if_port_list; + + int count = net_kmsg(kmsg)->net_rcv_msg_packet_count; + ifp = (struct ifnet *) kmsg->ikm_header.msgh_remote_port; + ipc_kmsg_queue_init(send_list); + + if (net_kmsg(kmsg)->sent) + if_port_list = &ifp->if_snd_port_list; + else + if_port_list = &ifp->if_rcv_port_list; + + /* + * Unfortunately we can't allocate or deallocate memory + * while holding these locks. And we can't drop the locks + * while examining the filter lists. + * Both locks are hold in case a filter is removed from both + * queues. + */ + simple_lock(&ifp->if_rcv_port_list_lock); + simple_lock(&ifp->if_snd_port_list_lock); + FILTER_ITERATE(if_port_list, infp, nextfp, + net_kmsg(kmsg)->sent ? &infp->output : &infp->input) + { + entp = (net_hash_entry_t) 0; + if ((infp->filter[0] & NETF_TYPE_MASK) == NETF_BPF) { + ret_count = bpf_do_filter(infp, net_kmsg(kmsg)->packet + + sizeof(struct packet_header), + count - sizeof(struct packet_header), + net_kmsg(kmsg)->header, + ifp->if_header_size, &hash_headp, + &entp); + if (entp == (net_hash_entry_t) 0) + dest = infp->rcv_port; + else + dest = entp->rcv_port; + if (ret_count) + ret_count += sizeof(struct packet_header); + } else { + ret_count = net_do_filter(infp, net_kmsg(kmsg)->packet, count, + net_kmsg(kmsg)->header); + if (ret_count) + ret_count = count; + dest = infp->rcv_port; + } + + if (ret_count) { + + /* + * Make a send right for the destination. + */ + + dest = ipc_port_copy_send(dest); + if (!IP_VALID(dest)) { + /* + * This filter is dead. We remove it from the + * filter list and set it aside for deallocation. + */ + + if (entp == (net_hash_entry_t) 0) { + if (infp->filter[0] & NETF_IN) + queue_remove(&ifp->if_rcv_port_list, infp, + net_rcv_port_t, input); + if (infp->filter[0] & NETF_OUT) + queue_remove(&ifp->if_snd_port_list, infp, + net_rcv_port_t, output); + + /* Use input only for queues of dead filters. */ + ENQUEUE_DEAD(dead_infp, infp, input); + continue; + } else { + hash_ent_remove (ifp, + (net_hash_header_t)infp, + FALSE, /* no longer used */ + hash_headp, + entp, + &dead_entp); + continue; + } + } + + /* + * Deliver copy of packet to this channel. + */ + if (ipc_kmsg_queue_empty(send_list)) { + /* + * Only receiver, so far + */ + new_kmsg = kmsg; + } else { + /* + * Other receivers - must allocate message and copy. + */ + new_kmsg = net_kmsg_get(); + if (new_kmsg == IKM_NULL) { + ipc_port_release_send(dest); + break; + } + + memcpy( + net_kmsg(new_kmsg)->packet, + net_kmsg(kmsg)->packet, + ret_count); + memcpy( + net_kmsg(new_kmsg)->header, + net_kmsg(kmsg)->header, + NET_HDW_HDR_MAX); + } + net_kmsg(new_kmsg)->net_rcv_msg_packet_count = ret_count; + new_kmsg->ikm_header.msgh_remote_port = (mach_port_t) dest; + ipc_kmsg_enqueue(send_list, new_kmsg); + + { + net_rcv_port_t prevfp; + int rcount = ++infp->rcv_count; + + /* + * See if ordering of filters is wrong + */ + if (infp->priority >= NET_HI_PRI) { +#define REORDER_PRIO(chain) \ + prevfp = (net_rcv_port_t) queue_prev(&infp->chain); \ + /* \ + * If infp is not the first element on the queue, \ + * and the previous element is at equal priority \ + * but has a lower count, then promote infp to \ + * be in front of prevfp. \ + */ \ + if ((queue_t)prevfp != if_port_list && \ + infp->priority == prevfp->priority) { \ + /* \ + * Threshold difference to prevent thrashing \ + */ \ + if (net_filter_queue_reorder \ + && (100 + prevfp->rcv_count < rcount)) \ + reorder_queue(&prevfp->chain, &infp->chain);\ + } + + REORDER_PRIO(input); + REORDER_PRIO(output); + + /* + * High-priority filter -> no more deliveries + */ + break; + } + } + } + } + FILTER_ITERATE_END + simple_unlock(&ifp->if_snd_port_list_lock); + simple_unlock(&ifp->if_rcv_port_list_lock); + + /* + * Deallocate dead filters. + */ + if (dead_infp != 0) + net_free_dead_infp(dead_infp); + if (dead_entp != 0) + net_free_dead_entp(dead_entp); + + if (ipc_kmsg_queue_empty(send_list)) { + /* Not sent - recycle */ + net_kmsg_put(kmsg); + } +} + +boolean_t +net_do_filter(net_rcv_port_t infp, + const char * data, + unsigned int data_count, + const char * header) +{ + int stack[NET_FILTER_STACK_DEPTH+1]; + int *sp; + filter_t *fp, *fpe; + unsigned int op, arg; + + /* + * The filter accesses the header and data + * as unsigned short words. + */ + data_count /= sizeof(unsigned short); + +#define data_word ((unsigned short *)data) +#define header_word ((unsigned short *)header) + + sp = &stack[NET_FILTER_STACK_DEPTH]; + fp = &infp->filter[1]; /* filter[0] used for flags */ + fpe = infp->filter_end; + + *sp = TRUE; + + while (fp < fpe) { + arg = *fp++; + op = NETF_OP(arg); + arg = NETF_ARG(arg); + + switch (arg) { + case NETF_NOPUSH: + arg = *sp++; + break; + case NETF_PUSHZERO: + arg = 0; + break; + case NETF_PUSHLIT: + arg = *fp++; + break; + case NETF_PUSHIND: + arg = *sp++; + if (arg >= data_count) + return FALSE; + arg = data_word[arg]; + break; + case NETF_PUSHHDRIND: + arg = *sp++; + if (arg >= NET_HDW_HDR_MAX/sizeof(unsigned short)) + return FALSE; + arg = header_word[arg]; + break; + default: + if (arg >= NETF_PUSHSTK) { + arg = sp[arg - NETF_PUSHSTK]; + } + else if (arg >= NETF_PUSHHDR) { + arg = header_word[arg - NETF_PUSHHDR]; + } + else { + arg -= NETF_PUSHWORD; + if (arg >= data_count) + return FALSE; + arg = data_word[arg]; + } + break; + + } + switch (op) { + case NETF_OP(NETF_NOP): + *--sp = arg; + break; + case NETF_OP(NETF_AND): + *sp &= arg; + break; + case NETF_OP(NETF_OR): + *sp |= arg; + break; + case NETF_OP(NETF_XOR): + *sp ^= arg; + break; + case NETF_OP(NETF_EQ): + *sp = (*sp == arg); + break; + case NETF_OP(NETF_NEQ): + *sp = (*sp != arg); + break; + case NETF_OP(NETF_LT): + *sp = (*sp < arg); + break; + case NETF_OP(NETF_LE): + *sp = (*sp <= arg); + break; + case NETF_OP(NETF_GT): + *sp = (*sp > arg); + break; + case NETF_OP(NETF_GE): + *sp = (*sp >= arg); + break; + case NETF_OP(NETF_COR): + if (*sp++ == arg) + return (TRUE); + break; + case NETF_OP(NETF_CAND): + if (*sp++ != arg) + return (FALSE); + break; + case NETF_OP(NETF_CNOR): + if (*sp++ == arg) + return (FALSE); + break; + case NETF_OP(NETF_CNAND): + if (*sp++ != arg) + return (TRUE); + break; + case NETF_OP(NETF_LSH): + *sp <<= arg; + break; + case NETF_OP(NETF_RSH): + *sp >>= arg; + break; + case NETF_OP(NETF_ADD): + *sp += arg; + break; + case NETF_OP(NETF_SUB): + *sp -= arg; + break; + } + } + return ((*sp) ? TRUE : FALSE); + +#undef data_word +#undef header_word +} + +/* + * Check filter for invalid operations or stack over/under-flow. + */ +static boolean_t +parse_net_filter( + filter_t *filter, + unsigned int count) +{ + int sp; + filter_t *fpe = &filter[count]; + filter_t op, arg; + + /* + * count is at least 1, and filter[0] is used for flags. + */ + filter++; + sp = NET_FILTER_STACK_DEPTH; + + for (; filter < fpe; filter++) { + op = NETF_OP(*filter); + arg = NETF_ARG(*filter); + + switch (arg) { + case NETF_NOPUSH: + break; + case NETF_PUSHZERO: + sp--; + break; + case NETF_PUSHLIT: + filter++; + if (filter >= fpe) + return (FALSE); /* literal value not in filter */ + sp--; + break; + case NETF_PUSHIND: + case NETF_PUSHHDRIND: + break; + default: + if (arg >= NETF_PUSHSTK) { + if (arg - NETF_PUSHSTK + sp > NET_FILTER_STACK_DEPTH) + return FALSE; + } + else if (arg >= NETF_PUSHHDR) { + if (arg - NETF_PUSHHDR >= + NET_HDW_HDR_MAX/sizeof(unsigned short)) + return FALSE; + } + /* else... cannot check for packet bounds + without packet */ + sp--; + break; + } + if (sp < 2) { + return (FALSE); /* stack overflow */ + } + if (op == NETF_OP(NETF_NOP)) + continue; + + /* + * all non-NOP operators are binary. + */ + if (sp > NET_MAX_FILTER-2) + return (FALSE); + + sp++; + switch (op) { + case NETF_OP(NETF_AND): + case NETF_OP(NETF_OR): + case NETF_OP(NETF_XOR): + case NETF_OP(NETF_EQ): + case NETF_OP(NETF_NEQ): + case NETF_OP(NETF_LT): + case NETF_OP(NETF_LE): + case NETF_OP(NETF_GT): + case NETF_OP(NETF_GE): + case NETF_OP(NETF_COR): + case NETF_OP(NETF_CAND): + case NETF_OP(NETF_CNOR): + case NETF_OP(NETF_CNAND): + case NETF_OP(NETF_LSH): + case NETF_OP(NETF_RSH): + case NETF_OP(NETF_ADD): + case NETF_OP(NETF_SUB): + break; + default: + return (FALSE); + } + } + return (TRUE); +} + +/* + * Set a filter for a network interface. + * + * We are given a naked send right for the rcv_port. + * If we are successful, we must consume that right. + */ +io_return_t +net_set_filter( + struct ifnet *ifp, + ipc_port_t rcv_port, + int priority, + filter_t *filter, + unsigned int filter_count) +{ + int filter_bytes; + bpf_insn_t match; + net_rcv_port_t infp, my_infp; + net_rcv_port_t nextfp; + net_hash_header_t hhp; + net_hash_entry_t entp; + net_hash_entry_t *head, nextentp; + queue_entry_t dead_infp, dead_entp; + int i; + int ret, is_new_infp; + io_return_t rval; + boolean_t in, out; + + /* Initialize hash_entp to NULL to quiet GCC + * warning about uninitialized variable. hash_entp is only + * used when match != 0; in that case it is properly initialized + * by kmem_cache_alloc(). + */ + net_hash_entry_t hash_entp = NULL; + + /* + * Check the filter syntax. + */ + + filter_bytes = CSPF_BYTES(filter_count); + match = (bpf_insn_t) 0; + + if (filter_count == 0) { + return (D_INVALID_OPERATION); + } else if (!((filter[0] & NETF_IN) || (filter[0] & NETF_OUT))) { + return (D_INVALID_OPERATION); /* NETF_IN or NETF_OUT required */ + } else if ((filter[0] & NETF_TYPE_MASK) == NETF_BPF) { + ret = bpf_validate((bpf_insn_t)filter, filter_bytes, &match); + if (!ret) + return (D_INVALID_OPERATION); + } else if ((filter[0] & NETF_TYPE_MASK) == 0) { + if (!parse_net_filter(filter, filter_count)) + return (D_INVALID_OPERATION); + } else { + return (D_INVALID_OPERATION); + } + + rval = D_SUCCESS; /* default return value */ + dead_infp = dead_entp = 0; + + if (match == (bpf_insn_t) 0) { + /* + * If there is no match instruction, we allocate + * a normal packet filter structure. + */ + my_infp = (net_rcv_port_t) kmem_cache_alloc(&net_rcv_cache); + my_infp->rcv_port = rcv_port; + is_new_infp = TRUE; + } else { + /* + * If there is a match instruction, we assume there will be + * multiple sessions with a common substructure and allocate + * a hash table to deal with them. + */ + my_infp = 0; + hash_entp = (net_hash_entry_t) kmem_cache_alloc(&net_hash_entry_cache); + is_new_infp = FALSE; + } + + /* + * Look for an existing filter on the same reply port. + * Look for filters with dead ports (for GC). + * Look for a filter with the same code except KEY insns. + */ + void check_filter_list(queue_head_t *if_port_list) + { + FILTER_ITERATE(if_port_list, infp, nextfp, + (if_port_list == &ifp->if_rcv_port_list) + ? &infp->input : &infp->output) + { + if (infp->rcv_port == MACH_PORT_NULL) { + if (match != 0 + && infp->priority == priority + && my_infp == 0 + && (infp->filter_end - infp->filter) == filter_count + && bpf_eq((bpf_insn_t)infp->filter, + (bpf_insn_t)filter, filter_bytes)) + my_infp = infp; + + for (i = 0; i < NET_HASH_SIZE; i++) { + head = &((net_hash_header_t) infp)->table[i]; + if (*head == 0) + continue; + + /* + * Check each hash entry to make sure the + * destination port is still valid. Remove + * any invalid entries. + */ + entp = *head; + do { + nextentp = (net_hash_entry_t) entp->he_next; + + /* checked without + ip_lock(entp->rcv_port) */ + if (entp->rcv_port == rcv_port + || !IP_VALID(entp->rcv_port) + || !ip_active(entp->rcv_port)) { + ret = hash_ent_remove (ifp, + (net_hash_header_t)infp, + (my_infp == infp), + head, + entp, + &dead_entp); + if (ret) + goto hash_loop_end; + } + + entp = nextentp; + /* While test checks head since hash_ent_remove + might modify it. + */ + } while (*head != 0 && entp != *head); + } + + hash_loop_end: + ; + } else if (infp->rcv_port == rcv_port + || !IP_VALID(infp->rcv_port) + || !ip_active(infp->rcv_port)) { + + /* Remove the old filter from lists */ + if (infp->filter[0] & NETF_IN) + queue_remove(&ifp->if_rcv_port_list, infp, + net_rcv_port_t, input); + if (infp->filter[0] & NETF_OUT) + queue_remove(&ifp->if_snd_port_list, infp, + net_rcv_port_t, output); + + ENQUEUE_DEAD(dead_infp, infp, input); + } + } + FILTER_ITERATE_END + } + + in = (filter[0] & NETF_IN) != 0; + out = (filter[0] & NETF_OUT) != 0; + + simple_lock(&ifp->if_rcv_port_list_lock); + simple_lock(&ifp->if_snd_port_list_lock); + + if (in) + check_filter_list(&ifp->if_rcv_port_list); + if (out) + check_filter_list(&ifp->if_snd_port_list); + + if (my_infp == 0) { + /* Allocate a dummy infp */ + simple_lock(&net_hash_header_lock); + for (i = 0; i < N_NET_HASH; i++) { + if (filter_hash_header[i].n_keys == 0) + break; + } + if (i == N_NET_HASH) { + simple_unlock(&net_hash_header_lock); + simple_unlock(&ifp->if_snd_port_list_lock); + simple_unlock(&ifp->if_rcv_port_list_lock); + + ipc_port_release_send(rcv_port); + if (match != 0) + kmem_cache_free(&net_hash_entry_cache, + (vm_offset_t)hash_entp); + + rval = D_NO_MEMORY; + goto clean_and_return; + } + + hhp = &filter_hash_header[i]; + hhp->n_keys = match->jt; + simple_unlock(&net_hash_header_lock); + + hhp->ref_count = 0; + for (i = 0; i < NET_HASH_SIZE; i++) + hhp->table[i] = 0; + + my_infp = (net_rcv_port_t)hhp; + my_infp->rcv_port = MACH_PORT_NULL; /* indication of dummy */ + is_new_infp = TRUE; + } + + if (is_new_infp) { + my_infp->priority = priority; + my_infp->rcv_count = 0; + + /* Copy filter program. */ + memcpy (my_infp->filter, filter, filter_bytes); + my_infp->filter_end = + (filter_t *)((char *)my_infp->filter + filter_bytes); + + if (match == 0) { + my_infp->rcv_qlimit = net_add_q_info(rcv_port); + } else { + my_infp->rcv_qlimit = 0; + } + + /* Insert my_infp according to priority */ + if (in) { + queue_iterate(&ifp->if_rcv_port_list, infp, net_rcv_port_t, input) + if (priority > infp->priority) + break; + + queue_enter(&ifp->if_rcv_port_list, my_infp, net_rcv_port_t, input); + } + + if (out) { + queue_iterate(&ifp->if_snd_port_list, infp, net_rcv_port_t, output) + if (priority > infp->priority) + break; + + queue_enter(&ifp->if_snd_port_list, my_infp, net_rcv_port_t, output); + } + } + + if (match != 0) + { /* Insert to hash list */ + net_hash_entry_t *p; + + hash_entp->rcv_port = rcv_port; + for (i = 0; i < match->jt; i++) /* match->jt is n_keys */ + hash_entp->keys[i] = match[i+1].k; + p = &((net_hash_header_t)my_infp)-> + table[bpf_hash(match->jt, hash_entp->keys)]; + + /* Not checking for the same key values */ + if (*p == 0) { + queue_init (&hash_entp->chain); + *p = hash_entp; + } else { + enqueue_tail(&(*p)->chain, &hash_entp->chain); + } + + ((net_hash_header_t)my_infp)->ref_count++; + hash_entp->rcv_qlimit = net_add_q_info(rcv_port); + } + + simple_unlock(&ifp->if_snd_port_list_lock); + simple_unlock(&ifp->if_rcv_port_list_lock); + +clean_and_return: + /* No locks are held at this point. */ + + if (dead_infp != 0) + net_free_dead_infp(dead_infp); + if (dead_entp != 0) + net_free_dead_entp(dead_entp); + + return (rval); +} + +/* + * Other network operations + */ +io_return_t +net_getstat( + struct ifnet *ifp, + dev_flavor_t flavor, + dev_status_t status, /* pointer to OUT array */ + mach_msg_type_number_t *count) /* OUT */ +{ + switch (flavor) { + case NET_STATUS: + { + struct net_status *ns = (struct net_status *)status; + + if (*count < NET_STATUS_COUNT) + return (D_INVALID_OPERATION); + + ns->min_packet_size = ifp->if_header_size; + ns->max_packet_size = ifp->if_header_size + ifp->if_mtu; + ns->header_format = ifp->if_header_format; + ns->header_size = ifp->if_header_size; + ns->address_size = ifp->if_address_size; + ns->flags = ifp->if_flags; + ns->mapped_size = 0; + + *count = NET_STATUS_COUNT; + break; + } + case NET_ADDRESS: + { + int addr_byte_count; + int addr_int_count; + int i; + + addr_byte_count = ifp->if_address_size; + addr_int_count = (addr_byte_count + (sizeof(int)-1)) + / sizeof(int); + + if (*count < addr_int_count) + { +/* XXX debug hack. */ +printf ("net_getstat: count: %d, addr_int_count: %d\n", + *count, addr_int_count); + return (D_INVALID_OPERATION); + } + + memcpy(status, ifp->if_address, addr_byte_count); + if (addr_byte_count < addr_int_count * sizeof(int)) + memset((char *)status + addr_byte_count, 0, + (addr_int_count * sizeof(int) + - addr_byte_count)); + + for (i = 0; i < addr_int_count; i++) { + int word; + + word = status[i]; + status[i] = htonl(word); + } + *count = addr_int_count; + break; + } + default: + return (D_INVALID_OPERATION); + } + return (D_SUCCESS); +} + +io_return_t +net_write( + struct ifnet *ifp, + net_write_start_device_fn start, + io_req_t ior) +{ + spl_t s; + kern_return_t rc; + boolean_t wait; + + /* + * Reject the write if the interface is down. + */ + if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) + return (D_DEVICE_DOWN); + + /* + * Reject the write if the packet is too large or too small. + */ + if (ior->io_count < ifp->if_header_size || + ior->io_count > ifp->if_header_size + ifp->if_mtu) + return (D_INVALID_SIZE); + + /* + * Wire down the memory. + */ + + rc = device_write_get(ior, &wait); + if (rc != KERN_SUCCESS) + return (rc); + + /* + * Network interfaces can't cope with VM continuations. + * If wait is set, just panic. + */ + if (wait) { + panic("net_write: VM continuation"); + } + + /* + * Queue the packet on the output queue, and + * start the device. + */ + s = splimp(); + IF_ENQUEUE(&ifp->if_snd, ior); + (*start)(ifp->if_unit); + splx(s); + + return (D_IO_QUEUED); +} + +/* + * Initialize the whole package. + */ +void +net_io_init(void) +{ + vm_size_t size; + + size = sizeof(struct net_rcv_port); + kmem_cache_init(&net_rcv_cache, "net_rcv_port", size, 0, + NULL, 0); + + size = sizeof(struct net_hash_entry); + kmem_cache_init(&net_hash_entry_cache, "net_hash_entry", size, 0, + NULL, 0); + + size = ikm_plus_overhead(sizeof(struct net_rcv_msg)); + net_kmsg_size = round_page(size); + + /* + * net_kmsg_max caps the number of buffers + * we are willing to allocate. By default, + * we allow for net_queue_free_min plus + * the queue limit for each filter. + * (Added as the filters are added.) + */ + + simple_lock_init(&net_kmsg_total_lock); + if (net_kmsg_max == 0) + net_kmsg_max = net_queue_free_min; + + simple_lock_init(&net_queue_free_lock); + ipc_kmsg_queue_init(&net_queue_free); + + simple_lock_init(&net_queue_lock); + ipc_kmsg_queue_init(&net_queue_high); + ipc_kmsg_queue_init(&net_queue_low); + + simple_lock_init(&net_hash_header_lock); +} + + +/* ======== BPF: Berkeley Packet Filter ======== */ + +/*- + * Copyright (c) 1990-1991 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. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 7.5 (Berkeley) 7/15/91 + */ + +#if defined(sparc) || defined(mips) || defined(ibm032) || defined(alpha) +#define BPF_ALIGN +#endif + +#ifndef BPF_ALIGN +#define EXTRACT_SHORT(p) ((u_short)ntohs(*(u_short *)p)) +#define EXTRACT_LONG(p) (ntohl(*(u_int *)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_int)*((u_char *)p+0)<<24|\ + (u_int)*((u_char *)p+1)<<16|\ + (u_int)*((u_char *)p+2)<<8|\ + (u_int)*((u_char *)p+3)<<0) +#endif + +/* + * 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 + */ + +int +bpf_do_filter( + net_rcv_port_t infp, + char * p, /* packet data */ + unsigned int wirelen, /* data_count (in bytes) */ + char * header, + unsigned int hlen, /* header len (in bytes) */ + net_hash_entry_t **hash_headpp, + net_hash_entry_t *entpp) /* out */ +{ + bpf_insn_t pc, pc_end; + unsigned int buflen; + + unsigned int A, X; + int k; + unsigned int mem[BPF_MEMWORDS]; + + /* Generic pointer to either HEADER or P according to the specified offset. */ + char *data = NULL; + + pc = ((bpf_insn_t) infp->filter) + 1; + /* filter[0].code is (NETF_BPF | flags) */ + pc_end = (bpf_insn_t)infp->filter_end; + buflen = NET_RCV_MAX; + *entpp = 0; /* default */ + + A = 0; + X = 0; + + for (; pc < pc_end; ++pc) { + switch (pc->code) { + + default: +#ifdef KERNEL + return 0; +#else + abort(); +#endif + case BPF_RET|BPF_K: + if (infp->rcv_port == MACH_PORT_NULL && + *entpp == 0) { + return 0; + } + return ((u_int)pc->k <= wirelen) ? + pc->k : wirelen; + + case BPF_RET|BPF_A: + if (infp->rcv_port == MACH_PORT_NULL && + *entpp == 0) { + return 0; + } + return ((u_int)A <= wirelen) ? + A : wirelen; + + case BPF_RET|BPF_MATCH_IMM: + if (bpf_match ((net_hash_header_t)infp, pc->jt, mem, + hash_headpp, entpp)) { + return ((u_int)pc->k <= wirelen) ? + pc->k : wirelen; + } + return 0; + + case BPF_LD|BPF_W|BPF_ABS: + k = pc->k; + + load_word: + if ((u_int)k + sizeof(int) <= hlen) + data = header; + else if ((u_int)k + sizeof(int) <= buflen) { + k -= hlen; + data = p; + } else + return 0; + +#ifdef BPF_ALIGN + if (((int)(data + k) & 3) != 0) + A = EXTRACT_LONG(&data[k]); + else +#endif + A = ntohl(*(int *)(data + k)); + continue; + + case BPF_LD|BPF_H|BPF_ABS: + k = pc->k; + + load_half: + if ((u_int)k + sizeof(short) <= hlen) + data = header; + else if ((u_int)k + sizeof(short) <= buflen) { + k -= hlen; + data = p; + } else + return 0; + + A = EXTRACT_SHORT(&data[k]); + continue; + + case BPF_LD|BPF_B|BPF_ABS: + k = pc->k; + + load_byte: + if ((u_int)k < hlen) + data = header; + else if ((u_int)k < buflen) { + data = p; + k -= hlen; + } else + return 0; + + A = data[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; + goto load_word; + + case BPF_LD|BPF_H|BPF_IND: + k = X + pc->k; + goto load_half; + + case BPF_LD|BPF_B|BPF_IND: + k = X + pc->k; + goto load_byte; + + case BPF_LDX|BPF_MSH|BPF_B: + k = pc->k; + if (k < hlen) + data = header; + else if (k < buflen) { + data = p; + k -= hlen; + } else + return 0; + + X = (data[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; + } + } + + return 0; +} + +/* + * Return 1 if the 'f' is a valid filter program without a MATCH + * instruction. Return 2 if it is a valid filter program with a MATCH + * instruction. Otherwise, return 0. + * 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( + bpf_insn_t f, + int bytes, + bpf_insn_t *match) +{ + int i, j, len; + bpf_insn_t p; + + len = BPF_BYTES2LEN(bytes); + + /* + * f[0].code is already checked to be (NETF_BPF | flags). + * So skip f[0]. + */ + + for (i = 1; i < len; ++i) { + /* + * Check that that jumps are forward, and within + * the code block. + */ + p = &f[i]; + if (BPF_CLASS(p->code) == BPF_JMP) { + 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; + /* + * Check for match instruction. + * Only one match instruction per filter is allowed. + */ + if (p->code == (BPF_RET|BPF_MATCH_IMM)) { + if (*match != 0 || + p->jt == 0 || + p->jt > N_NET_HASH_KEYS) + return 0; + i += p->jt; /* skip keys */ + if (i + 1 > len) + return 0; + + for (j = 1; j <= p->jt; j++) { + if (p[j].code != (BPF_MISC|BPF_KEY)) + return 0; + } + + *match = p; + } + } + if (BPF_CLASS(f[len - 1].code) == BPF_RET) + return ((*match == 0) ? 1 : 2); + else + return 0; +} + +int +bpf_eq( + bpf_insn_t f1, + bpf_insn_t f2, + int bytes) +{ + int count; + + count = BPF_BYTES2LEN(bytes); + for (; count--; f1++, f2++) { + if (!BPF_INSN_EQ(f1, f2)) { + if ( f1->code == (BPF_MISC|BPF_KEY) && + f2->code == (BPF_MISC|BPF_KEY) ) + continue; + return FALSE; + } + }; + return TRUE; +} + +unsigned int +bpf_hash (int n, + const unsigned int *keys) +{ + unsigned int hval = 0; + + while (n--) { + hval += *keys++; + } + return (hval % NET_HASH_SIZE); +} + + +int +bpf_match (net_hash_header_t hash, + int n_keys, + const unsigned int *keys, + net_hash_entry_t **hash_headpp, + net_hash_entry_t *entpp) +{ + net_hash_entry_t head, entp; + int i; + + if (n_keys != hash->n_keys) + return FALSE; + + *hash_headpp = &hash->table[bpf_hash(n_keys, keys)]; + head = **hash_headpp; + + if (head == 0) + return FALSE; + + HASH_ITERATE (head, entp) + { + for (i = 0; i < n_keys; i++) { + if (keys[i] != entp->keys[i]) + break; + } + if (i == n_keys) { + *entpp = entp; + return TRUE; + } + } + HASH_ITERATE_END (head, entp) + return FALSE; +} + + +/* + * Removes a hash entry (ENTP) from its queue (HEAD). + * If the reference count of filter (HP) becomes zero and not USED, + * HP is removed from the corresponding port lists and is freed. + */ + +int +hash_ent_remove( + struct ifnet *ifp, + net_hash_header_t hp, + int used, + net_hash_entry_t *head, + net_hash_entry_t entp, + queue_entry_t *dead_p) +{ + hp->ref_count--; + + if (*head == entp) { + if (queue_empty((queue_t) entp)) { + *head = 0; + ENQUEUE_DEAD(*dead_p, entp, chain); + if (hp->ref_count == 0 && !used) { + if (((net_rcv_port_t)hp)->filter[0] & NETF_IN) + queue_remove(&ifp->if_rcv_port_list, + (net_rcv_port_t)hp, + net_rcv_port_t, input); + if (((net_rcv_port_t)hp)->filter[0] & NETF_OUT) + queue_remove(&ifp->if_snd_port_list, + (net_rcv_port_t)hp, + net_rcv_port_t, output); + hp->n_keys = 0; + return TRUE; + } + return FALSE; + } else { + *head = (net_hash_entry_t)queue_next((queue_t) entp); + } + } + + remqueue((queue_t)*head, (queue_entry_t)entp); + ENQUEUE_DEAD(*dead_p, entp, chain); + return FALSE; +} + +int +net_add_q_info(ipc_port_t rcv_port) +{ + mach_port_msgcount_t qlimit = 0; + + /* + * We use a new port, so increase net_queue_free_min + * and net_kmsg_max to allow for more queued messages. + */ + + if (IP_VALID(rcv_port)) { + ip_lock(rcv_port); + if (ip_active(rcv_port)) + qlimit = rcv_port->ip_qlimit; + ip_unlock(rcv_port); + } + + simple_lock(&net_kmsg_total_lock); + net_queue_free_min++; + net_kmsg_max += qlimit + 1; + simple_unlock(&net_kmsg_total_lock); + + return (int)qlimit; +} + +static void +net_del_q_info(int qlimit) +{ + simple_lock(&net_kmsg_total_lock); + net_queue_free_min--; + net_kmsg_max -= qlimit + 1; + simple_unlock(&net_kmsg_total_lock); +} + + +/* + * net_free_dead_infp (dead_infp) + * queue_entry_t dead_infp; list of dead net_rcv_port_t. + * + * Deallocates dead net_rcv_port_t. + * No locks should be held when called. + */ +void +net_free_dead_infp(queue_entry_t dead_infp) +{ + net_rcv_port_t infp, nextfp; + + for (infp = (net_rcv_port_t) dead_infp; infp != 0; infp = nextfp) + { + nextfp = (net_rcv_port_t) queue_next(&infp->input); + ipc_port_release_send(infp->rcv_port); + net_del_q_info(infp->rcv_qlimit); + kmem_cache_free(&net_rcv_cache, (vm_offset_t) infp); + } +} + +/* + * net_free_dead_entp (dead_entp) + * queue_entry_t dead_entp; list of dead net_hash_entry_t. + * + * Deallocates dead net_hash_entry_t. + * No locks should be held when called. + */ +void +net_free_dead_entp(queue_entry_t dead_entp) +{ + net_hash_entry_t entp, nextentp; + + for (entp = (net_hash_entry_t)dead_entp; entp != 0; entp = nextentp) + { + nextentp = (net_hash_entry_t) queue_next(&entp->chain); + + ipc_port_release_send(entp->rcv_port); + net_del_q_info(entp->rcv_qlimit); + kmem_cache_free(&net_hash_entry_cache, (vm_offset_t) entp); + } +} + diff --git a/device/net_io.h b/device/net_io.h new file mode 100644 index 0000000..c9af85e --- /dev/null +++ b/device/net_io.h @@ -0,0 +1,164 @@ +/* + * 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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: ll/89 + */ + +#ifndef _DEVICE_NET_IO_H_ +#define _DEVICE_NET_IO_H_ + +/* + * Utilities for playing with network messages. + */ + +#include +#include + +#include +#include +#include + +#include +#include +#include + +struct net_rcv_port; +typedef struct net_rcv_port *net_rcv_port_t; + +struct net_hash_entry; +typedef struct net_hash_entry *net_hash_entry_t; + +struct net_hash_header; +typedef struct net_hash_header *net_hash_header_t; + +/* + * A network packet is wrapped in a kernel message while in + * the kernel. + */ + +#define net_kmsg(kmsg) ((net_rcv_msg_t)&(kmsg)->ikm_header) + +/* + * Interrupt routines may allocate and free net_kmsgs with these + * functions. net_kmsg_get may return IKM_NULL. + */ + +extern ipc_kmsg_t net_kmsg_get(void); +extern void net_kmsg_put(ipc_kmsg_t); + +/* + * Network utility routines. + */ + +extern void net_ast(void); +extern void net_packet(struct ifnet *, ipc_kmsg_t, unsigned int, boolean_t); +extern void net_filter(ipc_kmsg_t, ipc_kmsg_queue_t); +extern io_return_t net_getstat(struct ifnet *, dev_flavor_t, dev_status_t, + mach_msg_type_number_t *); + +typedef int (*net_write_start_device_fn)(short); +extern io_return_t net_write(struct ifnet *, net_write_start_device_fn, io_req_t); + +/* + * Non-interrupt code may allocate and free net_kmsgs with these functions. + */ + +extern vm_size_t net_kmsg_size; + +extern void net_kmsg_collect (void); + +extern void net_io_init(void); +extern void net_thread(void) __attribute__ ((noreturn)); + +#define net_kmsg_alloc() ((ipc_kmsg_t) kalloc(net_kmsg_size)) +#define net_kmsg_free(kmsg) kfree((vm_offset_t) (kmsg), net_kmsg_size) + +extern unsigned int ntohl(unsigned int); +extern unsigned short int ntohs(unsigned short int); +extern unsigned int htonl(unsigned int); +extern unsigned short int htons(unsigned short int); + +unsigned int bpf_hash(int n, const unsigned int *keys); + +extern boolean_t +net_do_filter( + net_rcv_port_t infp, + const char * data, + unsigned int data_count, + const char * header); /* CSPF */ + +io_return_t +net_set_filter( + struct ifnet *ifp, + ipc_port_t rcv_port, + int priority, + filter_t *filter, + unsigned int filter_count); + +extern int +bpf_do_filter( + net_rcv_port_t infp, + char * p, + unsigned int wirelen, + char * header, + unsigned int hlen, + net_hash_entry_t **hash_headpp, + net_hash_entry_t *entpp); /* BPF */ + +int hash_ent_remove( + struct ifnet *ifp, + net_hash_header_t hp, + int used, + net_hash_entry_t *head, + net_hash_entry_t entp, + queue_entry_t *dead_p); + +void net_free_dead_infp(queue_entry_t dead_infp); +void net_free_dead_entp (queue_entry_t dead_entp); + +int bpf_validate( + bpf_insn_t f, + int bytes, + bpf_insn_t *match); + +int bpf_eq( + bpf_insn_t f1, + bpf_insn_t f2, + int bytes); + +int net_add_q_info(ipc_port_t rcv_port); + +int bpf_match ( + net_hash_header_t hash, + int n_keys, + const unsigned int *keys, + net_hash_entry_t **hash_headpp, + net_hash_entry_t *entpp); + +boolean_t ethernet_priority(const ipc_kmsg_t kmsg); + +#endif /* _DEVICE_NET_IO_H_ */ diff --git a/device/param.h b/device/param.h new file mode 100644 index 0000000..41b4793 --- /dev/null +++ b/device/param.h @@ -0,0 +1,49 @@ +/* + * 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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 7/90 + */ + +#ifndef _DEVICE_PARAM_H_ +#define _DEVICE_PARAM_H_ + +/* + * Compatibility definitions for disk IO. + */ + +/* + * Disk devices do all IO in 512-byte blocks. + */ +#define DEV_BSIZE 512 + +/* + * Conversion between bytes and disk blocks. + */ +#define btodb(byte_offset) ((byte_offset) >> 9) +#define dbtob(block_number) ((block_number) << 9) + +#endif /* _DEVICE_PARAM_H_ */ diff --git a/device/subrs.c b/device/subrs.c new file mode 100644 index 0000000..6e90a81 --- /dev/null +++ b/device/subrs.c @@ -0,0 +1,86 @@ +/* + * Mach Operating System + * Copyright (c) 1993,1991,1990,1989,1988 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. + */ +/* + * Random device subroutines and stubs. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + + + +/* + * Convert Ethernet address to printable (loggable) representation. + */ +char * +ether_sprintf(const u_char *ap) +{ + int i; + static char etherbuf[18]; + char *cp = etherbuf; + static char digits[] = "0123456789abcdef"; + + for (i = 0; i < 6; i++) { + *cp++ = digits[*ap >> 4]; + *cp++ = digits[*ap++ & 0xf]; + *cp++ = ':'; + } + *--cp = 0; + return (etherbuf); +} + +/* + * Initialize send and receive queues on an interface. + */ +void if_init_queues(struct ifnet *ifp) +{ + IFQ_INIT(&ifp->if_snd); + queue_init(&ifp->if_rcv_port_list); + queue_init(&ifp->if_snd_port_list); + simple_lock_init(&ifp->if_rcv_port_list_lock); + simple_lock_init(&ifp->if_snd_port_list_lock); +} + + +/* + * Compatibility with BSD device drivers. + */ +void sleep(vm_offset_t channel, int priority) +{ + assert_wait((event_t) channel, FALSE); /* not interruptible XXX */ + thread_block((void (*)()) 0); +} + +void wakeup(vm_offset_t channel) +{ + thread_wakeup((event_t) channel); +} diff --git a/device/subrs.h b/device/subrs.h new file mode 100644 index 0000000..60ea651 --- /dev/null +++ b/device/subrs.h @@ -0,0 +1,37 @@ +/* + * Random device functions + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Barry deFreese. + */ +/* + * Random device functions. + * + */ + +#ifndef _SUBRS_H_ +#define _SUBRS_H_ + +#include +#include + +extern void if_init_queues(struct ifnet *ifp); + +extern void sleep (vm_offset_t channel, int priority); +extern void wakeup (vm_offset_t channel); + +#endif /* _SUBRS_H_ */ diff --git a/device/tty.h b/device/tty.h new file mode 100644 index 0000000..3f8b2f6 --- /dev/null +++ b/device/tty.h @@ -0,0 +1,237 @@ +/* + * Mach Operating System + * Copyright (c) 1993-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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 7/90 + * + * Compatibility TTY structure for existing TTY device drivers. + */ + +#ifndef _DEVICE_TTY_H_ +#define _DEVICE_TTY_H_ + +#include +#include +#include + +#include +#include +#include +#include + +struct tty { + decl_simple_lock_irq_data(,t_lock) /* Shall be taken at spltty only */ + struct cirbuf t_inq; /* input buffer */ + struct cirbuf t_outq; /* output buffer */ + char * t_addr; /* device pointer */ + int t_dev; /* device number */ + void (*t_start)(struct tty *); + /* routine to start output */ +#define t_oproc t_start + void (*t_stop)(struct tty *, int); + /* routine to stop output */ + int (*t_mctl)(struct tty *, int, int); + /* (optional) routine to control + modem signals */ + unsigned char t_ispeed; /* input speed */ + unsigned char t_ospeed; /* output speed */ + char t_breakc; /* character to deliver when 'break' + condition received */ + int t_flags; /* mode flags */ + int t_state; /* current state */ + int t_line; /* fake line discipline number, + for old drivers - always 0 */ + queue_head_t t_delayed_read; /* pending read requests */ + queue_head_t t_delayed_write;/* pending write requests */ + queue_head_t t_delayed_open; /* pending open requests */ + +/* + * Items beyond this point should be removed to device-specific + * extension structures. + */ + io_return_t (*t_getstat)(dev_t, dev_flavor_t, dev_status_t, mach_msg_type_number_t *); /* routine to get status */ + io_return_t (*t_setstat)(dev_t, dev_flavor_t, dev_status_t, mach_msg_type_number_t); /* routine to set status */ + dev_ops_t t_tops; /* another device to possibly + push through */ +}; +typedef struct tty *tty_t; + +/* + * Common TTY service routines + */ +extern io_return_t char_open( + int dev, + struct tty * tp, + dev_mode_t mode, + io_req_t ior); + +extern io_return_t char_read( + struct tty * tp, + io_req_t ior); + +extern io_return_t char_write( + struct tty * tp, + io_req_t ior); + +extern void ttyinput( + unsigned int c, + struct tty * tp); + +extern void ttyinput_many( + struct tty * tp, + char * chars, + int count); + +extern boolean_t ttymodem( + struct tty * tp, + boolean_t carrier_up); + +extern void tty_cts( + struct tty * tp, + boolean_t cts_up); + +extern void tty_queue_completion( + queue_t queue); +#define tt_open_wakeup(tp) \ + (tty_queue_completion(&(tp)->t_delayed_open)) +#define tt_write_wakeup(tp) \ + (tty_queue_completion(&(tp)->t_delayed_write)) + +extern void ttychars( + struct tty * tp); + +#define TTMINBUF 90 + +extern short tthiwat[NSPEEDS], ttlowat[NSPEEDS]; +#define TTHIWAT(tp) tthiwat[(tp)->t_ospeed] +#define TTLOWAT(tp) ttlowat[(tp)->t_ospeed] + +extern io_return_t tty_get_status( + struct tty * tp, + dev_flavor_t flavor, + int * data, + natural_t * count); + +extern io_return_t tty_set_status( + struct tty * tp, + dev_flavor_t flavor, + int * data, + natural_t count); + +extern void tty_flush( + struct tty * tp, + int rw); + +extern void ttrstrt( + struct tty * tp); + +extern void ttstart( + struct tty * tp); + +extern void ttyclose( + struct tty * tp); + +extern boolean_t tty_portdeath( + struct tty * tp, + ipc_port_t port); + +/* internal state bits */ +#define TS_INIT 0x00000001 /* tty structure initialized */ +#define TS_TIMEOUT 0x00000002 /* delay timeout in progress */ +#define TS_WOPEN 0x00000004 /* waiting for open to complete */ +#define TS_ISOPEN 0x00000008 /* device is open */ +#define TS_FLUSH 0x00000010 /* outq has been flushed during DMA */ +#define TS_CARR_ON 0x00000020 /* software copy of carrier-present */ +#define TS_BUSY 0x00000040 /* output in progress */ +#define TS_ASLEEP 0x00000080 /* wakeup when output done */ + +#define TS_TTSTOP 0x00000100 /* output stopped by ctl-s */ +#define TS_HUPCLS 0x00000200 /* hang up upon last close */ +#define TS_TBLOCK 0x00000400 /* tandem queue blocked */ + +#define TS_NBIO 0x00001000 /* tty in non-blocking mode */ +#define TS_ONDELAY 0x00002000 /* device is open; software copy of + * carrier is not present */ +#define TS_MIN 0x00004000 /* buffer input chars, if possible */ +#define TS_MIN_TO 0x00008000 /* timeout for the above is active */ + +#define TS_OUT 0x00010000 /* tty in use for dialout only */ +#define TS_RTS_DOWN 0x00020000 /* modem pls stop */ + +#define TS_TRANSLATE 0x00100000 /* translation device enabled */ +#define TS_KDB 0x00200000 /* should enter kdb on ALT */ + +#define TS_MIN_TO_RCV 0x00400000 /* character received during + receive timeout interval */ + +/* flags - old names defined in terms of new ones */ + +#define TANDEM TF_TANDEM +#define ODDP TF_ODDP +#define EVENP TF_EVENP +#define ANYP (ODDP|EVENP) +#define MDMBUF TF_MDMBUF +#define LITOUT TF_LITOUT +#define NOHANG TF_NOHANG + +#define ECHO TF_ECHO +#define CRMOD TF_CRMOD +#define XTABS TF_XTABS + +/* these are here only to let old code compile - they are never set */ +#define RAW LITOUT +#define PASS8 LITOUT + +/* + * Hardware bits. + * SHOULD NOT BE HERE. + */ +#define DONE 0200 +#define IENABLE 0100 + +/* + * Modem control commands. + */ +#define DMSET 0 +#define DMBIS 1 +#define DMBIC 2 +#define DMGET 3 + +/* + * Fake 'line discipline' switch, for the benefit of old code + * that wants to call through it. + */ +struct ldisc_switch { + int (*l_read) (struct tty *, io_req_t); /* read */ + int (*l_write)(struct tty *, io_req_t); /* write */ + void (*l_rint) (unsigned int, struct tty *); /* character input */ + boolean_t (*l_modem)(struct tty *, boolean_t); /* modem change */ + void (*l_start)(struct tty *); /* start output */ +}; + +extern struct ldisc_switch linesw[]; + +#endif /* _DEVICE_TTY_H_ */ diff --git a/doc/.gitignore b/doc/.gitignore new file mode 100644 index 0000000..829355b --- /dev/null +++ b/doc/.gitignore @@ -0,0 +1,4 @@ +/*.info +/*.info-* +/stamp-vti +/version.texi diff --git a/doc/Makefrag.am b/doc/Makefrag.am new file mode 100644 index 0000000..490ebf5 --- /dev/null +++ b/doc/Makefrag.am @@ -0,0 +1,119 @@ +# Makefile fragment for the documentation. + +# Copyright (C) 2006, 2007, 2008 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +# +# The GNU Mach Reference Manual. +# + +info_TEXINFOS += \ + doc/mach.texi +mach_TEXINFOS = \ + doc/fdl.texi doc/gpl.texi +EXTRA_DIST += \ + $(mach_TEXINFOS) + +# +# Web pages of the GNU Mach Reference Manual. +# + +web = doc/web + +# Prepare a checkout in `$(web)/' of the web pages of the GNU Mach Reference +# Manual, using the same account that was used for the source code. Then +# install the potentially updated files into `$(web)/'. +.PHONY: $(web) +$(web): + if test -d $@/CVS; then :; else \ + mkdir -p $@ $@/CVS && \ + sed -e s%cvsroot%web% \ + < $(top_srcdir)/CVS/Root \ + > $@/CVS/Root && \ + echo hurd/gnumach-doc \ + > $@/CVS/Repository && \ + : > $@/CVS/Entries; \ + fi + cd $@/ && \ + cvs update + $(MAKE) $(AM_MAKEFLAGS) \ + html \ + ps \ + pdf + +# Update the files, if such a checkout exists. +html-local: + if test -d $(web); then \ + ( cd $(web)/ && \ + for f in *.html; do \ + if test -f ../../$(HTMLS)/"$$f"; then :; else \ + echo "\`$$f' isn't anymore. Removing." && \ + rm "$$f" && \ + cvs remove "$$f"; \ + fi; \ + done ) && \ + cp $(HTMLS)/*.html $(web)/ && \ + cd $(web)/ && \ + { cvs add *.html || :; }; \ + fi +ps-local: + if test -d $(web); then \ + ( cd $(web)/ && \ + for f in *.ps; do \ + case \ $(PSS)\ in \ + \ doc/"$$f"\ ) :;; \ + *) echo "\`$$f' isn't anymore. Removing." && \ + rm -f "$$f" "$$f".ps && \ + cvs remove "$$f" "$$f".ps ;; \ + esac; \ + done ) && \ + cp $(PSS) $(web)/ && \ + cd $(web)/ && \ + for f in *.ps; do \ + gzip -9 < "$$f" > "$$f".gz; \ + done && \ + { cvs add *.ps *.ps.gz || :; }; \ + fi +pdf-local: + if test -d $(web); then \ + ( cd $(web)/ && \ + for f in *.pdf; do \ + case \ $(PDFS)\ in \ + \ doc/"$$f"\ ) :;; \ + *) echo "\`$$f' isn't anymore. Removing." && \ + rm "$$f" && \ + cvs remove "$$f";; \ + esac; \ + done ) && \ + cp $(PDFS) $(web)/ && \ + cd $(web)/ && \ + { cvs add *.pdf || :; }; \ + fi +# TODO. There doesn't seem to be a hook or `-local' target suitable for this. +$(srcdir)/doc/version.texi: $(srcdir)/doc/stamp-vti + @if test -d $(web); then :; \ + elif grep 2> /dev/null \ + -q :ext: $(top_srcdir)/CVS/Root 2> /dev/null && \ + grep 2> /dev/null \ + -q ^Tgnumach-1-branch$$ $(top_srcdir)/CVS/Tag; \ + then \ + echo "*** As it seems that you'd be allowed to check in the" \ + "possible resulting fixes, you may consider running" \ + " \`make $(web)' to get a checkout of the web pages of the" \ + "GNU Mach manual and have possible changes installed into" \ + "\`$(web)/', ready for checking them in in there." && \ + sleep 2; \ + fi diff --git a/doc/fdl.texi b/doc/fdl.texi new file mode 100644 index 0000000..9c6d9af --- /dev/null +++ b/doc/fdl.texi @@ -0,0 +1,452 @@ + +@node GNU Free Documentation License +@appendixsec GNU Free Documentation License + +@cindex FDL, GNU Free Documentation License +@center Version 1.2, November 2002 + +@display +Copyright @copyright{} 2000,2001,2002 Free Software Foundation, Inc. +51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. +@end display + +@enumerate 0 +@item +PREAMBLE + +The purpose of this License is to make a manual, textbook, or other +functional and useful document @dfn{free} in the sense of freedom: to +assure everyone the effective freedom to copy and redistribute it, +with or without modifying it, either commercially or noncommercially. +Secondarily, this License preserves for the author and publisher a way +to get credit for their work, while not being considered responsible +for modifications made by others. + +This License is a kind of ``copyleft'', which means that derivative +works of the document must themselves be free in the same sense. It +complements the GNU General Public License, which is a copyleft +license designed for free software. + +We have designed this License in order to use it for manuals for free +software, because free software needs free documentation: a free +program should come with manuals providing the same freedoms that the +software does. But this License is not limited to software manuals; +it can be used for any textual work, regardless of subject matter or +whether it is published as a printed book. We recommend this License +principally for works whose purpose is instruction or reference. + +@item +APPLICABILITY AND DEFINITIONS + +This License applies to any manual or other work, in any medium, that +contains a notice placed by the copyright holder saying it can be +distributed under the terms of this License. Such a notice grants a +world-wide, royalty-free license, unlimited in duration, to use that +work under the conditions stated herein. The ``Document'', below, +refers to any such manual or work. Any member of the public is a +licensee, and is addressed as ``you''. You accept the license if you +copy, modify or distribute the work in a way requiring permission +under copyright law. + +A ``Modified Version'' of the Document means any work containing the +Document or a portion of it, either copied verbatim, or with +modifications and/or translated into another language. + +A ``Secondary Section'' is a named appendix or a front-matter section +of the Document that deals exclusively with the relationship of the +publishers or authors of the Document to the Document's overall +subject (or to related matters) and contains nothing that could fall +directly within that overall subject. (Thus, if the Document is in +part a textbook of mathematics, a Secondary Section may not explain +any mathematics.) The relationship could be a matter of historical +connection with the subject or with related matters, or of legal, +commercial, philosophical, ethical or political position regarding +them. + +The ``Invariant Sections'' are certain Secondary Sections whose titles +are designated, as being those of Invariant Sections, in the notice +that says that the Document is released under this License. If a +section does not fit the above definition of Secondary then it is not +allowed to be designated as Invariant. The Document may contain zero +Invariant Sections. If the Document does not identify any Invariant +Sections then there are none. + +The ``Cover Texts'' are certain short passages of text that are listed, +as Front-Cover Texts or Back-Cover Texts, in the notice that says that +the Document is released under this License. A Front-Cover Text may +be at most 5 words, and a Back-Cover Text may be at most 25 words. + +A ``Transparent'' copy of the Document means a machine-readable copy, +represented in a format whose specification is available to the +general public, that is suitable for revising the document +straightforwardly with generic text editors or (for images composed of +pixels) generic paint programs or (for drawings) some widely available +drawing editor, and that is suitable for input to text formatters or +for automatic translation to a variety of formats suitable for input +to text formatters. A copy made in an otherwise Transparent file +format whose markup, or absence of markup, has been arranged to thwart +or discourage subsequent modification by readers is not Transparent. +An image format is not Transparent if used for any substantial amount +of text. A copy that is not ``Transparent'' is called ``Opaque''. + +Examples of suitable formats for Transparent copies include plain +@sc{ascii} without markup, Texinfo input format, La@TeX{} input +format, @acronym{SGML} or @acronym{XML} using a publicly available +@acronym{DTD}, and standard-conforming simple @acronym{HTML}, +PostScript or @acronym{PDF} designed for human modification. Examples +of transparent image formats include @acronym{PNG}, @acronym{XCF} and +@acronym{JPG}. Opaque formats include proprietary formats that can be +read and edited only by proprietary word processors, @acronym{SGML} or +@acronym{XML} for which the @acronym{DTD} and/or processing tools are +not generally available, and the machine-generated @acronym{HTML}, +PostScript or @acronym{PDF} produced by some word processors for +output purposes only. + +The ``Title Page'' means, for a printed book, the title page itself, +plus such following pages as are needed to hold, legibly, the material +this License requires to appear in the title page. For works in +formats which do not have any title page as such, ``Title Page'' means +the text near the most prominent appearance of the work's title, +preceding the beginning of the body of the text. + +A section ``Entitled XYZ'' means a named subunit of the Document whose +title either is precisely XYZ or contains XYZ in parentheses following +text that translates XYZ in another language. (Here XYZ stands for a +specific section name mentioned below, such as ``Acknowledgements'', +``Dedications'', ``Endorsements'', or ``History''.) To ``Preserve the Title'' +of such a section when you modify the Document means that it remains a +section ``Entitled XYZ'' according to this definition. + +The Document may include Warranty Disclaimers next to the notice which +states that this License applies to the Document. These Warranty +Disclaimers are considered to be included by reference in this +License, but only as regards disclaiming warranties: any other +implication that these Warranty Disclaimers may have is void and has +no effect on the meaning of this License. + +@item +VERBATIM COPYING + +You may copy and distribute the Document in any medium, either +commercially or noncommercially, provided that this License, the +copyright notices, and the license notice saying this License applies +to the Document are reproduced in all copies, and that you add no other +conditions whatsoever to those of this License. You may not use +technical measures to obstruct or control the reading or further +copying of the copies you make or distribute. However, you may accept +compensation in exchange for copies. If you distribute a large enough +number of copies you must also follow the conditions in section 3. + +You may also lend copies, under the same conditions stated above, and +you may publicly display copies. + +@item +COPYING IN QUANTITY + +If you publish printed copies (or copies in media that commonly have +printed covers) of the Document, numbering more than 100, and the +Document's license notice requires Cover Texts, you must enclose the +copies in covers that carry, clearly and legibly, all these Cover +Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on +the back cover. Both covers must also clearly and legibly identify +you as the publisher of these copies. The front cover must present +the full title with all words of the title equally prominent and +visible. You may add other material on the covers in addition. +Copying with changes limited to the covers, as long as they preserve +the title of the Document and satisfy these conditions, can be treated +as verbatim copying in other respects. + +If the required texts for either cover are too voluminous to fit +legibly, you should put the first ones listed (as many as fit +reasonably) on the actual cover, and continue the rest onto adjacent +pages. + +If you publish or distribute Opaque copies of the Document numbering +more than 100, you must either include a machine-readable Transparent +copy along with each Opaque copy, or state in or with each Opaque copy +a computer-network location from which the general network-using +public has access to download using public-standard network protocols +a complete Transparent copy of the Document, free of added material. +If you use the latter option, you must take reasonably prudent steps, +when you begin distribution of Opaque copies in quantity, to ensure +that this Transparent copy will remain thus accessible at the stated +location until at least one year after the last time you distribute an +Opaque copy (directly or through your agents or retailers) of that +edition to the public. + +It is requested, but not required, that you contact the authors of the +Document well before redistributing any large number of copies, to give +them a chance to provide you with an updated version of the Document. + +@item +MODIFICATIONS + +You may copy and distribute a Modified Version of the Document under +the conditions of sections 2 and 3 above, provided that you release +the Modified Version under precisely this License, with the Modified +Version filling the role of the Document, thus licensing distribution +and modification of the Modified Version to whoever possesses a copy +of it. In addition, you must do these things in the Modified Version: + +@enumerate A +@item +Use in the Title Page (and on the covers, if any) a title distinct +from that of the Document, and from those of previous versions +(which should, if there were any, be listed in the History section +of the Document). You may use the same title as a previous version +if the original publisher of that version gives permission. + +@item +List on the Title Page, as authors, one or more persons or entities +responsible for authorship of the modifications in the Modified +Version, together with at least five of the principal authors of the +Document (all of its principal authors, if it has fewer than five), +unless they release you from this requirement. + +@item +State on the Title page the name of the publisher of the +Modified Version, as the publisher. + +@item +Preserve all the copyright notices of the Document. + +@item +Add an appropriate copyright notice for your modifications +adjacent to the other copyright notices. + +@item +Include, immediately after the copyright notices, a license notice +giving the public permission to use the Modified Version under the +terms of this License, in the form shown in the Addendum below. + +@item +Preserve in that license notice the full lists of Invariant Sections +and required Cover Texts given in the Document's license notice. + +@item +Include an unaltered copy of this License. + +@item +Preserve the section Entitled ``History'', Preserve its Title, and add +to it an item stating at least the title, year, new authors, and +publisher of the Modified Version as given on the Title Page. If +there is no section Entitled ``History'' in the Document, create one +stating the title, year, authors, and publisher of the Document as +given on its Title Page, then add an item describing the Modified +Version as stated in the previous sentence. + +@item +Preserve the network location, if any, given in the Document for +public access to a Transparent copy of the Document, and likewise +the network locations given in the Document for previous versions +it was based on. These may be placed in the ``History'' section. +You may omit a network location for a work that was published at +least four years before the Document itself, or if the original +publisher of the version it refers to gives permission. + +@item +For any section Entitled ``Acknowledgements'' or ``Dedications'', Preserve +the Title of the section, and preserve in the section all the +substance and tone of each of the contributor acknowledgements and/or +dedications given therein. + +@item +Preserve all the Invariant Sections of the Document, +unaltered in their text and in their titles. Section numbers +or the equivalent are not considered part of the section titles. + +@item +Delete any section Entitled ``Endorsements''. Such a section +may not be included in the Modified Version. + +@item +Do not retitle any existing section to be Entitled ``Endorsements'' or +to conflict in title with any Invariant Section. + +@item +Preserve any Warranty Disclaimers. +@end enumerate + +If the Modified Version includes new front-matter sections or +appendices that qualify as Secondary Sections and contain no material +copied from the Document, you may at your option designate some or all +of these sections as invariant. To do this, add their titles to the +list of Invariant Sections in the Modified Version's license notice. +These titles must be distinct from any other section titles. + +You may add a section Entitled ``Endorsements'', provided it contains +nothing but endorsements of your Modified Version by various +parties---for example, statements of peer review or that the text has +been approved by an organization as the authoritative definition of a +standard. + +You may add a passage of up to five words as a Front-Cover Text, and a +passage of up to 25 words as a Back-Cover Text, to the end of the list +of Cover Texts in the Modified Version. Only one passage of +Front-Cover Text and one of Back-Cover Text may be added by (or +through arrangements made by) any one entity. If the Document already +includes a cover text for the same cover, previously added by you or +by arrangement made by the same entity you are acting on behalf of, +you may not add another; but you may replace the old one, on explicit +permission from the previous publisher that added the old one. + +The author(s) and publisher(s) of the Document do not by this License +give permission to use their names for publicity for or to assert or +imply endorsement of any Modified Version. + +@item +COMBINING DOCUMENTS + +You may combine the Document with other documents released under this +License, under the terms defined in section 4 above for modified +versions, provided that you include in the combination all of the +Invariant Sections of all of the original documents, unmodified, and +list them all as Invariant Sections of your combined work in its +license notice, and that you preserve all their Warranty Disclaimers. + +The combined work need only contain one copy of this License, and +multiple identical Invariant Sections may be replaced with a single +copy. If there are multiple Invariant Sections with the same name but +different contents, make the title of each such section unique by +adding at the end of it, in parentheses, the name of the original +author or publisher of that section if known, or else a unique number. +Make the same adjustment to the section titles in the list of +Invariant Sections in the license notice of the combined work. + +In the combination, you must combine any sections Entitled ``History'' +in the various original documents, forming one section Entitled +``History''; likewise combine any sections Entitled ``Acknowledgements'', +and any sections Entitled ``Dedications''. You must delete all +sections Entitled ``Endorsements.'' + +@item +COLLECTIONS OF DOCUMENTS + +You may make a collection consisting of the Document and other documents +released under this License, and replace the individual copies of this +License in the various documents with a single copy that is included in +the collection, provided that you follow the rules of this License for +verbatim copying of each of the documents in all other respects. + +You may extract a single document from such a collection, and distribute +it individually under this License, provided you insert a copy of this +License into the extracted document, and follow this License in all +other respects regarding verbatim copying of that document. + +@item +AGGREGATION WITH INDEPENDENT WORKS + +A compilation of the Document or its derivatives with other separate +and independent documents or works, in or on a volume of a storage or +distribution medium, is called an ``aggregate'' if the copyright +resulting from the compilation is not used to limit the legal rights +of the compilation's users beyond what the individual works permit. +When the Document is included in an aggregate, this License does not +apply to the other works in the aggregate which are not themselves +derivative works of the Document. + +If the Cover Text requirement of section 3 is applicable to these +copies of the Document, then if the Document is less than one half of +the entire aggregate, the Document's Cover Texts may be placed on +covers that bracket the Document within the aggregate, or the +electronic equivalent of covers if the Document is in electronic form. +Otherwise they must appear on printed covers that bracket the whole +aggregate. + +@item +TRANSLATION + +Translation is considered a kind of modification, so you may +distribute translations of the Document under the terms of section 4. +Replacing Invariant Sections with translations requires special +permission from their copyright holders, but you may include +translations of some or all Invariant Sections in addition to the +original versions of these Invariant Sections. You may include a +translation of this License, and all the license notices in the +Document, and any Warranty Disclaimers, provided that you also include +the original English version of this License and the original versions +of those notices and disclaimers. In case of a disagreement between +the translation and the original version of this License or a notice +or disclaimer, the original version will prevail. + +If a section in the Document is Entitled ``Acknowledgements'', +``Dedications'', or ``History'', the requirement (section 4) to Preserve +its Title (section 1) will typically require changing the actual +title. + +@item +TERMINATION + +You may not copy, modify, sublicense, or distribute the Document except +as expressly provided for under this License. Any other attempt to +copy, modify, sublicense or distribute the Document is void, and will +automatically terminate your rights under this License. However, +parties who have received copies, or rights, from you under this +License will not have their licenses terminated so long as such +parties remain in full compliance. + +@item +FUTURE REVISIONS OF THIS LICENSE + +The Free Software Foundation may publish new, revised versions +of the GNU Free Documentation License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. See +@uref{http://www.gnu.org/copyleft/}. + +Each version of the License is given a distinguishing version number. +If the Document specifies that a particular numbered version of this +License ``or any later version'' applies to it, you have the option of +following the terms and conditions either of that specified version or +of any later version that has been published (not as a draft) by the +Free Software Foundation. If the Document does not specify a version +number of this License, you may choose any version ever published (not +as a draft) by the Free Software Foundation. +@end enumerate + +@page +@heading ADDENDUM: How to use this License for your documents + +To use this License in a document you have written, include a copy of +the License in the document and put the following copyright and +license notices just after the title page: + +@smallexample +@group + Copyright (C) @var{year} @var{your name}. + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.2 + or any later version published by the Free Software Foundation; + with no Invariant Sections, no Front-Cover Texts, and no Back-Cover + Texts. A copy of the license is included in the section entitled ``GNU + Free Documentation License''. +@end group +@end smallexample + +If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, +replace the ``with...Texts.'' line with this: + +@smallexample +@group + with the Invariant Sections being @var{list their titles}, with + the Front-Cover Texts being @var{list}, and with the Back-Cover Texts + being @var{list}. +@end group +@end smallexample + +If you have Invariant Sections without Cover Texts, or some other +combination of the three, merge those two alternatives to suit the +situation. + +If your document contains nontrivial examples of program code, we +recommend releasing these examples in parallel under your choice of +free software license, such as the GNU General Public License, +to permit their use in free software. + +@c Local Variables: +@c ispell-local-pdict: "ispell-dict" +@c End: + diff --git a/doc/gpl.texi b/doc/gpl.texi new file mode 100644 index 0000000..c1f025e --- /dev/null +++ b/doc/gpl.texi @@ -0,0 +1,383 @@ +@node Copying +@unnumbered GNU General Public License +@cindex GPL, GNU General Public License +@center Version 2, June 1991 + +@c This file is intended to be included in another file. + +@display +Copyright @copyright{} 1989, 1991 Free Software Foundation, Inc. +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. +@end display + +@heading Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software---to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + +@heading TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +@enumerate 0 +@item +This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The ``Program'', below, +refers to any such program or work, and a ``work based on the Program'' +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term ``modification''.) Each licensee is addressed as ``you''. + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + +@item +You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + +@item +You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + +@enumerate a +@item +You must cause the modified files to carry prominent notices +stating that you changed the files and the date of any change. + +@item +You must cause any work that you distribute or publish, that in +whole or in part contains or is derived from the Program or any +part thereof, to be licensed as a whole at no charge to all third +parties under the terms of this License. + +@item +If the modified program normally reads commands interactively +when run, you must cause it, when started running for such +interactive use in the most ordinary way, to print or display an +announcement including an appropriate copyright notice and a +notice that there is no warranty (or else, saying that you provide +a warranty) and that users may redistribute the program under +these conditions, and telling the user how to view a copy of this +License. (Exception: if the Program itself is interactive but +does not normally print such an announcement, your work based on +the Program is not required to print an announcement.) +@end enumerate + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + +@item +You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + +@enumerate a +@item +Accompany it with the complete corresponding machine-readable +source code, which must be distributed under the terms of Sections +1 and 2 above on a medium customarily used for software interchange; or, + +@item +Accompany it with a written offer, valid for at least three +years, to give any third party, for a charge no more than your +cost of physically performing source distribution, a complete +machine-readable copy of the corresponding source code, to be +distributed under the terms of Sections 1 and 2 above on a medium +customarily used for software interchange; or, + +@item +Accompany it with the information you received as to the offer +to distribute corresponding source code. (This alternative is +allowed only for noncommercial distribution and only if you +received the program in object code or executable form with such +an offer, in accord with Subsection b above.) +@end enumerate + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + +@item +You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + +@item +You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + +@item +Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + +@item +If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + +@item +If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + +@item +The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and ``any +later version'', you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + +@item +If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + +@item NO WARRANTY + +BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM ``AS IS'' WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + +@item +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. +@end enumerate + +@iftex +@heading END OF TERMS AND CONDITIONS +@end iftex +@ifinfo +@center END OF TERMS AND CONDITIONS + +@end ifinfo + +@page +@heading Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the ``copyright'' line and a pointer to where the full notice is found. + +@smallexample +@var{one line to give the program's name and a brief idea of what it does.} +Copyright (C) @var{yyyy} @var{name of author} + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +@end smallexample + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + +@smallexample +Gnomovision version 69, Copyright (C) @var{year} @var{name of author} +Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. +This is free software, and you are welcome to redistribute it +under certain conditions; type `show c' for details. +@end smallexample + +The hypothetical commands @samp{show w} and @samp{show c} should show +the appropriate parts of the General Public License. Of course, the +commands you use may be called something other than @samp{show w} and +@samp{show c}; they could even be mouse-clicks or menu items---whatever +suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a ``copyright disclaimer'' for the program, if +necessary. Here is a sample; alter the names: + +@example +Yoyodyne, Inc., hereby disclaims all copyright interest in the program +`Gnomovision' (which makes passes at compilers) written by James Hacker. + +@var{signature of Ty Coon}, 1 April 1989 +Ty Coon, President of Vice +@end example + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/doc/mach.texi b/doc/mach.texi new file mode 100644 index 0000000..f85288e --- /dev/null +++ b/doc/mach.texi @@ -0,0 +1,7417 @@ +\input texinfo @c -*- Texinfo -*- +@documentencoding ISO-8859-1 +@setfilename mach.info +@settitle The GNU Mach Reference Manual +@setchapternewpage odd + +@comment Tell install-info what to do. +@dircategory Kernel +@direntry +* GNUMach: (mach). Using and programming the GNU Mach microkernel. +@end direntry + +@c Should have a glossary. +@c Unify some of our indices. +@syncodeindex pg cp +@syncodeindex vr fn +@syncodeindex tp fn + +@c Get the Mach version we are documenting. +@include version.texi +@set EDITION 0.4 +@c @set ISBN X-XXXXXX-XX-X + +@copying +This file documents the GNU Mach microkernel. + +This is edition @value{EDITION}, last updated on @value{UPDATED}, of @cite{The +GNU Mach Reference Manual}, for version @value{VERSION}. + +Copyright @copyright{} 2001, 2002, 2006, 2007, 2008 Free Software +Foundation, Inc. + +@c @sp 2 +@c Published by the Free Software Foundation @* +@c 59 Temple Place -- Suite 330, @* +@c Boston, MA 02111-1307 USA @* +@c ISBN @value{ISBN} @* + +@quotation +Permission is granted to copy, distribute and/or modify this document +under the terms of the GNU Free Documentation License, Version 1.2 or +any later version published by the Free Software Foundation; with no +Invariant Section, with no Front-Cover Texts, and with no Back-Cover +Texts. A copy of the license is included in the section entitled +``GNU Free Documentation License''. + +This work is based on manual pages under the following copyright and license: + +@noindent +Mach Operating System@* +Copyright @copyright{} 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. +@end quotation +@end copying + +@iftex +@shorttitlepage The GNU Mach Reference Manual +@end iftex +@titlepage +@center @titlefont{The GNU Mach} +@sp 1 +@center @titlefont{Reference Manual} +@sp 2 +@center Marcus Brinkmann +@center with +@center Gordon Matzigkeit, Gibran Hasnaoui, +@center Robert V. Baron, Richard P. Draves, Mary R. Thompson, Joseph S. Barrera +@sp 3 +@center Edition @value{EDITION} +@sp 1 +@center last updated @value{UPDATED} +@sp 1 +@center for version @value{VERSION} +@page +@vskip 0pt plus 1filll +@insertcopying +@end titlepage +@c @titlepage +@c @finalout +@c @title The GNU Mach Reference Manual +@c @author Marcus Brinkmann +@c @author Gordon Matzigkeit +@c @author Gibran Hasnaoui + +@c @author Robert V. Baron @c (rvb) +@c @author Richard P. Draves @c (rpd) +@c @author Mary R. Thompson @c (mrt) +@c @author Joseph S. Barrera @c (jsb) +@c @c The following occur rarely in the rcs commit logs of the man pages: +@c @c Dan Stodolsky, (danner) +@c @c David B. Golub, (dbg) +@c @c Terri Watson, (elf) +@c @c Lori Iannamico, (lli) [distribution coordinator] +@c @c Further authors of kernel_interfaces.ps: +@c @c David Black [OSF] +@c @c William Bolosky +@c @c Jonathan Chew +@c @c Alessandro Forin +@c @c Richard F. Rashid +@c @c Avadis Tevanian Jr. +@c @c Michael W. Young +@c @c See also +@c @c http://www.cs.cmu.edu/afs/cs/project/mach/public/www/people-former.html +@page + +@ifnottex +@node Top +@top Main Menu + +@insertcopying +@end ifnottex + +@menu +* Introduction:: How to use this manual. +* Installing:: Setting up GNU Mach on your computer. +* Bootstrap:: Running GNU Mach on your machine. +* Inter Process Communication:: Communication between process. +* Virtual Memory Interface:: Allocating and deallocating virtual memory. +* External Memory Management:: Handling memory pages in user space. +* Threads and Tasks:: Handling of threads and tasks. +* Host Interface:: Interface to a Mach host. +* Processors and Processor Sets:: Handling processors and sets of processors. +* Device Interface:: Accessing kernel devices. +* Kernel Debugger:: How to use the built-in kernel debugger. + +Appendices + +* Copying:: The GNU General Public License says how you + can copy and share the GNU Mach microkernel. +* Documentation License:: This manual is under the GNU Free + Documentation License. + +Indices + +* Concept Index:: Index of concepts and programs. +* Function and Data Index:: Index of functions, variables and data types. + + +@detailmenu + --- The Detailed Node Listing --- + +Introduction + +* Audience:: The people for whom this manual is written. +* Features:: Reasons to install and use GNU Mach. +* Overview:: Basic architecture of the Mach microkernel. +* History:: The story about Mach. + +Installing + +* Binary Distributions:: Obtaining ready-to-run GNU distributions. +* Compilation:: Building GNU Mach from its source code. +* Configuration:: Configuration options at compilation time. +* Cross-Compilation:: Building GNU Mach from another system. + +Bootstrap + +* Bootloader:: Starting the microkernel, or other OSes. +* Modules:: Starting the first task of the OS. + +Inter Process Communication + +* Major Concepts:: The concepts behind the Mach IPC system. +* Messaging Interface:: Composing, sending and receiving messages. +* Port Manipulation Interface:: Manipulating ports, port rights, port sets. + +Messaging Interface + +* Mach Message Call:: Sending and receiving messages. +* Message Format:: The format of Mach messages. +* Exchanging Port Rights:: Sending and receiving port rights. +* Memory:: Passing memory regions in messages. +* Message Send:: Sending messages. +* Message Receive:: Receiving messages. +* Atomicity:: Atomicity of port rights. + +Port Manipulation Interface + +* Port Creation:: How to create new ports and port sets. +* Port Destruction:: How to destroy ports and port sets. +* Port Names:: How to query and manipulate port names. +* Port Rights:: How to work with port rights. +* Ports and other Tasks:: How to move rights between tasks. +* Receive Rights:: How to work with receive rights. +* Port Sets:: How to work with port sets. +* Request Notifications:: How to request notifications for events. +* Inherited Ports:: How to work with the inherited system ports. + +Virtual Memory Interface + +* Memory Allocation:: Allocation of new virtual memory. +* Memory Deallocation:: Freeing unused virtual memory. +* Data Transfer:: Reading, writing and copying memory. +* Memory Attributes:: Tweaking memory regions. +* Mapping Memory Objects:: How to map memory objects. +* Memory Statistics:: How to get statistics about memory usage. + +External Memory Management + +* Memory Object Server:: The basics of external memory management. +* Memory Object Creation:: How new memory objects are created. +* Memory Object Termination:: How memory objects are terminated. +* Memory Objects and Data:: Data transfer to and from memory objects. +* Memory Object Locking:: How memory objects are locked. +* Memory Object Attributes:: Manipulating attributes of memory objects. +* Default Memory Manager:: Setting and using the default memory manager. + +Threads and Tasks + +* Thread Interface:: Manipulating threads. +* Task Interface:: Manipulating tasks. +* Profiling:: Profiling threads and tasks. + +Thread Interface + +* Thread Creation:: Creating threads. +* Thread Termination:: Terminating threads. +* Thread Information:: How to get informations on threads. +* Thread Settings:: How to set threads related informations. +* Thread Execution:: How to control the thread's machine state. +* Scheduling:: Operations on thread scheduling. +* Thread Special Ports:: How to handle the thread's special ports. +* Exceptions:: Managing exceptions. + +Scheduling + +* Thread Priority:: Changing the priority of a thread. +* Hand-Off Scheduling:: Switch to a new thread. +* Scheduling Policy:: Setting the scheduling policy. + +Task Interface + +* Task Creation:: Creating tasks. +* Task Termination:: Terminating tasks. +* Task Information:: Informations on tasks. +* Task Execution:: Thread scheduling in a task. +* Task Special Ports:: How to get and set the task's special ports. +* Syscall Emulation:: How to emulate system calls. + +Host Interface + +* Host Ports:: Ports representing a host. +* Host Information:: Query information about a host. +* Host Time:: Functions to query manipulate the host time. +* Host Reboot:: Rebooting the system. + +Processors and Processor Sets + +* Processor Set Interface:: How to work with processor sets. +* Processor Interface:: How to work with individual processors. + +Processor Set Interface + +* Processor Set Ports:: Ports representing a processor set. +* Processor Set Access:: How the processor sets are accessed. +* Processor Set Creation:: How new processor sets are created. +* Processor Set Destruction:: How processor sets are destroyed. +* Tasks and Threads on Sets:: Assigning tasks or threads to processor sets. +* Processor Set Priority:: Specifying the priority of a processor set. +* Processor Set Policy:: Changing the processor set policies. +* Processor Set Info:: Obtaining information about a processor set. + +Processor Interface + +* Hosted Processors:: Getting a list of all processors on a host. +* Processor Control:: Starting, stopping, controlling processors. +* Processors and Sets:: Combining processors into processor sets. +* Processor Info:: Obtaining information on processors. + +Device Interface + +* Device Open:: Opening hardware devices. +* Device Close:: Closing hardware devices. +* Device Read:: Reading data from the device. +* Device Write:: Writing data to the device. +* Device Map:: Mapping devices into virtual memory. +* Device Status:: Querying and manipulating a device. +* Device Filter:: Filtering packets arriving on a device. +* Device Interrupt:: Getting hardware interrupt notifications. + +Kernel Debugger + +* Operation:: Basic architecture of the kernel debugger. +* Commands:: Available commands in the kernel debugger. +* Variables:: Access of variables from the kernel debugger. +* Expressions:: Usage of expressions in the kernel debugger. + +Documentation License + +* GNU Free Documentation License:: The GNU Free Documentation License. +* CMU License:: The CMU license applies to the original Mach + kernel and its documentation. + +@end detailmenu +@end menu + + +@node Introduction +@chapter Introduction + +GNU Mach is the microkernel of the GNU Project. It is the base of the +operating system, and provides its functionality to the Hurd servers, +the GNU C Library and all user applications. The microkernel itself +does not provide much functionality of the system, just enough to make +it possible for the Hurd servers and the C library to implement the missing +features you would expect from a POSIX compatible operating system. + +@menu +* Audience:: The people for whom this manual is written. +* Features:: Reasons to install and use GNU Mach. +* Overview:: Basic architecture of the Mach microkernel. +* History:: The story about Mach. +@end menu + + +@node Audience +@section Audience + +This manual is designed to be useful to everybody who is interested in +using, administering, or programming the Mach microkernel. + +If you are an end-user and you are looking for help on running the Mach +kernel, the first few chapters of this manual describe the essential +parts of installing and using the kernel in the GNU operating system. + +The rest of this manual is a technical discussion of the Mach +programming interface and its implementation, and would not be helpful +until you want to learn how to extend the system or modify the kernel. + +This manual is organized according to the subsystems of Mach, and each +chapter begins with descriptions of conceptual ideas that are related to +that subsystem. If you are a programmer and want to learn more about, +say, the Mach IPC subsystem, you can skip to the IPC chapter +(@pxref{Inter Process Communication}), and read about the related +concepts and interface definitions. + + +@node Features +@section Features + +GNU Mach is not the most advanced microkernel known to the planet, +nor is it the fastest or smallest, but it has a rich set of interfaces and +some features which make it useful as the base of the Hurd system. + +@table @asis +@item it's free software +Anybody can use, modify, and redistribute it under the terms of the GNU +General Public License (@pxref{Copying}). GNU Mach is part of the GNU +system, which is a complete operating system licensed under the GPL. + +@item it's built to survive +As a microkernel, GNU Mach doesn't implement a lot of the features +commonly found in an operating system, but only the bare minimum +that is required to implement a full operating system on top of it. +This means that a lot of the operating system code is maintained outside +of GNU Mach, and while this code may go through a complete redesign, the +code of the microkernel can remain comparatively stable. + +@item it's scalable +Mach is particularly well suited for SMP and network cluster techniques. +Thread support is provided at the kernel level, and the kernel itself +takes advantage of that. Network transparency at the IPC level makes +resources of the system available across machine boundaries (with NORMA +IPC, currently not available in GNU Mach). + +@item it exists +The Mach microkernel is real software that works Right Now. +It is not a research project or a proposal. You don't have to wait at all +before you can start using and developing it. Mach has been used in +many operating systems in the past, usually as the base for a single +UNIX server. In the GNU system, Mach is the base of a functional +multi-server operating system, the Hurd. +@end table + + +@node Overview +@section Overview + +@c This paragraph by Gordon Matzigkeit from the Hurd manual. +An operating system kernel provides a framework for programs to share a +computer's hardware resources securely and efficiently. This requires +that the programs are separated and protected from each other. To make +running multiple programs in parallel useful, there also needs to be a +facility for programs to exchange information by communication. + +The Mach microkernel provides abstractions of the underlying hardware +resources like devices and memory. It organizes the running programs +into tasks and threads (points of execution in the tasks). In addition, +Mach provides a rich interface for inter-process communication. + +What Mach does not provide is a POSIX compatible programming interface. +In fact, it has no understanding of file systems, POSIX process semantics, +network protocols and many more. All this is implemented in tasks +running on top of the microkernel. In the GNU operating system, the Hurd +servers and the C library share the responsibility to implement the POSIX +interface, and the additional interfaces which are specific to the GNU +system. + + +@node History +@section History + +XXX A few lines about the history of Mach here. + + +@node Installing +@chapter Installing + +Before you can use the Mach microkernel in your system you'll need to install +it and all components you want to use with it, e.g. the rest of the operating +system. You also need a bootloader to load the kernel from the storage +medium and run it when the computer is started. + +GNU Mach is only available for Intel i386-compatible architectures +(such as the Pentium) currently. If you have a different architecture +and want to run the GNU Mach microkernel, you will need to port the +kernel and all other software of the system to your machine's architecture. +Porting is an involved process which requires considerable programming skills, +and it is not recommended for the faint-of-heart. +If you have the talent and desire to do a port, contact +@email{bug-hurd@@gnu.org} in order to coordinate the effort. + +@menu +* Binary Distributions:: Obtaining ready-to-run GNU distributions. +* Compilation:: Building GNU Mach from its source code. +* Configuration:: Configuration options at compile time. +* Cross-Compilation:: Building GNU Mach from another system. +@end menu + + +@node Binary Distributions +@section Binary Distributions + +By far the easiest and best way to install GNU Mach and the operating +system is to obtain a GNU binary distribution. The GNU operating +system consists of GNU Mach, the Hurd, the C library and many applications. +Without the GNU operating system, you will only have a microkernel, which +is not very useful by itself, without the other programs. + +Building the whole operating system takes a huge effort, and you are well +advised to not do it yourself, but to get a binary distribution of the +GNU operating system. The distribution also includes a binary of the +GNU Mach microkernel. + +Information on how to obtain the GNU system can be found in the Hurd +info manual. + + +@node Compilation +@section Compilation + +If you already have a running GNU system, and only want to recompile +the kernel, for example to select a different set of included hardware +drivers, you can easily do this. You need the GNU C compiler and +MIG, the Mach interface generator, which both come in their own +packages. + +Building and installing the kernel is as easy as with any other GNU +software package. The configure script is used to configure the source +and set the compile time options. The compilation is done by running: + +@example +make +@end example + +To install the kernel and its header files, just enter the command: + +@example +make install +@end example + +This will install the kernel as @file{EXEC_PREFIX/boot/gnumach}, the header +files into @file{PREFIX/include/}, the list of message ids as +@file{PREFIX/share/msgids/gnumach.msgids} and the documentation into +@file{PREFIX/share/info/}. + +Note that there is also a way to only install the header and documentation +files without having to actually build the whole package: run @command{make +install-data} after having ran @command{configure} to do so. (This is needed +for bootstrapping a cross compiler and similar procedures.) + +@node Configuration +@section Configuration + +See the following tables for the options can be passed to the +@command{configure} script as command line arguments to control what components +are built into the kernel, how certain things are configured and so on. + +See the top-level @file{INSTALL} file for information about generic +@command{configure} options, like under which paths to install the package's +components. It also describes how to control the process by setting +environment variables. + +The file @file{i386/README-Drivers} has some i386-specific information for +device drivers. You should only need to consult this file in case a device +driver is not working for you. + +@subsection Table of configure switches not related to device drivers + +@table @code +@item --enable-kdb +In-kernel debugger. This is only useful if you actually anticipate debugging +the kernel. It is not enabled by default because it adds considerably to the +unpageable memory footprint of the kernel. @xref{Kernel Debugger}. +@end table + +@table @code +@item --enable-pae +@acronym{PAE, Physical Address Extension} feature (@samp{ix86}-only), +which is available on modern @samp{ix86} processors; on @samp{ix86-at} disabled +by default, on @samp{ix86-xen} enabled by default. +@end table + +@subsection Turning device drivers on or off + +Each device driver has an associated configure switch. The following table +indicates whether a device driver is enabled by default or not. It also gives +--- if possible at all --- the configure switches to use for disabling or +enabling device drivers, in case you're not satisfied with the default choices. +You can specify @samp{--enable-device-drivers=WHICH} (where WHICH on +@samp{ix86-at} must be one of @samp{default}, @samp{qemu}, @samp{none}) to +preset a certain subset of all available device drivers. +@samp{--enable-device-drivers} is sugar for +@samp{--enable-device-drivers=default} (and is the implicit default +nevertheless) and @samp{--disable-device-drivers} is short for +@samp{--enable-device-drivers=none}. @samp{qemu} will include only the set of +device drivers that is useful when using the resulting kernel binary to drive a +Hurd system in the @acronym{QEMU} system emulator. This is only useful for +reducing the kernel build time and the kernel image size. + +@subsection What the configure switches do + +Each configure switch has two effects. First, it defines a @acronym{CPP} +symbol that turns on or off the hooks that autoconfigure the device and add it +to the list of available devices. Second, it adds the source code for the +driver to a make variable so that the code for the driver is compiled and +linked into the kernel. Also follow this route to find the file(s) which are +implementing a certain device driver. + +@subsection Table of configure switches related to device drivers + +(@samp{%d} in the following denotes a unit number, starting with @samp{0}.) + +@table @code +@item --disable-kmsg +Kernel message device @samp{kmsg}. + +@item --enable-lpr +Parallel port device driver for the @samp{lpr%d} devices. On @samp{ix86-at} +enabled by @samp{default}. + +@item --enable-floppy +PC floppy disk controller device driver for the @samp{fd%d} devices. On +@samp{ix86-at} enabled by @samp{default} and for @samp{qemu}. + +@item --enable-ide +IDE controller device driver for the @samp{hd%d} and @samp{hd%ds%d} (disks and +their partitions) devices. On @samp{ix86-at} enabled by @samp{default} and for +@samp{qemu}. +@end table + +The following options control drivers for various SCSI controller. SCSI +devices are named @samp{sd%d} and @samp{sd%ds$d} (disks and their partitions) +or @samp{cd%d} (CD ROMs). + +@table @code +@item --enable-advansys +AdvanSys SCSI controller device driver. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-buslogic +BusLogic SCSI controller device driver. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-flashpoint +Only meaningful in conjunction with the above BusLogic SCSI controller device +driver. Enable the FlashPoint support. + +@item --enable-u14-34f +UltraStor 14F/34F SCSI controller device driver. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-ultrastor +UltraStor SCSI controller device driver. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-aha152x +Adaptec AHA-152x/2825 SCSI controller device driver. On @samp{ix86-at} enabled +by @samp{default}. + +@item --enable-aha1542 +Adaptec AHA-1542 SCSI controller device driver. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-aha1740 +Adaptec AHA-1740 SCSI controller device driver. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-aic7xxx +Adaptec AIC7xxx SCSI controller device driver. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-fdomain +Future Domain 16xx SCSI controller device driver. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-in2000 +Always IN 2000 SCSI controller device driver. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-g_NCR5380 +Generic NCR5380/53c400 SCSI controller device driver. + +@item --enable-NCR53c406a +NCR53c406a SCSI controller device driver. + +@item --enable-pas16 +PAS16 SCSI controller device driver. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-seagate +Seagate ST02 and Future Domain TMC-8xx SCSI controller device driver. On +@samp{ix86-at} enabled by @samp{default}. + +@item --enable-t128 +Trantor T128/T128F/T228 SCSI controller device driver. On @samp{ix86-at} +enabled by @samp{default}. + +@item --enable-53c78xx +NCR53C7,8xx SCSI controller device driver. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-eata_dma +EATA-DMA (DPT, NEC, AT&T, SNI, AST, Olivetti, Alphatronix) SCSI controller +device driver. + +@item --enable-eata_pio +EATA-PIO (old DPT PM2001, PM2012A) SCSI controller device driver. On +@samp{ix86-at} enabled by @samp{default}. + +@item --enable-wd7000 +WD 7000 SCSI controller device driver. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-eata +EATA ISA/EISA/PCI (DPT and generic EATA/DMA-compliant boards) SCSI controller +device driver. On @samp{ix86-at} enabled by @samp{default}. + +@item --enable-am53c974 +AM53/79C974 SCSI controller device driver. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-dtc +DTC3180/3280 SCSI controller device driver. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-ncr53c8xx +NCR53C8XX, dc390w, dc390u, dc390f SCSI controller device driver. On +@samp{ix86-at} enabled by @samp{default}. + +@item --enable-tmscsim +Tekram DC-390(T) SCSI controller device driver. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-ppa +IOMEGA Parallel Port ZIP drive device driver. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-qlogicfas +Qlogic FAS SCSI controller device driver. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-qlogicisp +Qlogic ISP SCSI controller device driver. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-gdth +GDT SCSI Disk Array controller device driver. On @samp{ix86-at} enabled by +@samp{default}. +@end table + +The following options enable drivers for various ethernet cards. NIC devices +are usually named @samp{eth%d}, except for the pocket adaptors. + +@c GNU Mach does only autodetect one ethernet card. To enable any further +@c cards, the source code has to be edited. +@c XXX Reference to the source code. + +@table @code +@item --enable-ne +NE2000/NE1000 ISA network card device driver. On @samp{ix86-at} enabled by +@samp{default} and for @samp{qemu}. + +@item --enable-3c503 +3Com 503 (Etherlink II) network card device driver. On @samp{ix86-at} enabled +by @samp{default}. + +@item --enable-3c509 +3Com 509/579 (Etherlink III) network card device driver. On @samp{ix86-at} +enabled by @samp{default}. + +@item --enable-wd +WD80X3 network card device driver. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-3c501 +3COM 501/Etherlink I network card device driver. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-smc-ultra +SMC Ultra network card device driver. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-smc-ultra32 +SMC Ultra 32 network card device driver. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-hp-plus +HP PCLAN+ (27247B and 27252A) network card device driver. On @samp{ix86-at} +enabled by @samp{default}. + +@item --enable-hp +HP PCLAN (27245 and other 27xxx series) network card device driver. On +@samp{ix86-at} enabled by @samp{default}. + +@item --enable-3c59x +3Com 590/900 series (592/595/597/900/905) "Vortex/Boomerang" network card +device driver. On @samp{ix86-at} enabled by @samp{default}. + +@item --enable-seeq8005 +Seeq8005 network card device driver. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-hp100 +HP 10/100VG PCLAN (ISA, EISA, PCI) network card device driver. On +@samp{ix86-at} enabled by @samp{default}. + +@item --enable-ac3200 +Ansel Communications EISA 3200 network card device driver. On @samp{ix86-at} +enabled by @samp{default}. + +@item --enable-e2100 +Cabletron E21xx network card device driver. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-at1700 +AT1700 (Fujitsu 86965) network card device driver. On @samp{ix86-at} enabled +by @samp{default}. + +@item --enable-eth16i +ICL EtherTeam 16i/32 network card device driver. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-znet +Zenith Z-Note network card device driver. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-eexpress +EtherExpress 16 network card device driver. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-eepro +EtherExpressPro network card device driver. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-eepro100 +Intel EtherExpressPro PCI 10+/100B/100+ network card device driver. On +@samp{ix86-at} enabled by @samp{default}. + +@item --enable-depca +DEPCA, DE10x, DE200, DE201, DE202, DE210, DE422 network card device driver. On +@samp{ix86-at} enabled by @samp{default}. + +@item --enable-ewrk3 +EtherWORKS 3 (DE203, DE204, DE205) network card device driver. On +@samp{ix86-at} enabled by @samp{default}. + +@item --enable-de4x5 +DE425, DE434, DE435, DE450, DE500 network card device driver. On +@samp{ix86-at} enabled by @samp{default}. + +@item --enable-apricot +Apricot XEN-II on board ethernet network card device driver. On @samp{ix86-at} +enabled by @samp{default}. + +@item --enable-wavelan +AT&T WaveLAN & DEC RoamAbout DS network card device driver. + +@item --enable-3c507 +3Com 507 network card device driver. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-3c505 +3Com 505/Etherlink II network card device driver. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-de600 +D-Link DE-600 network card device driver. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-de620 +D-Link DE-620 network card device driver. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-sk_g16 +Schneider & Koch G16 network card device driver. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-ni52 +NI5210 network card device driver. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-ni65 +NI6510 network card device driver. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-atp +AT-LAN-TEC/RealTek pocket adaptor network card device driver for the +@samp{atp%d} devices. + +@item --enable-lance +AMD LANCE and PCnet (AT1500 and NE2100) network card device driver. On +@samp{ix86-at} enabled by @samp{default}. + +@item --enable-tulip +DECchip Tulip (dc21x4x) PCI network card device driver. On @samp{ix86-at} +enabled by @samp{default}. + +@item --enable-fmv18x +FMV-181/182/183/184 network card device driver. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-3c515 +3Com 515 ISA Fast EtherLink network card device driver. On @samp{ix86-at} +enabled by @samp{default}. + +@item --enable-pcnet32 +AMD PCI PCnet32 (PCI bus NE2100 cards) network card device driver. On +@samp{ix86-at} enabled by @samp{default}. + +@item --enable-ne2k-pci +PCI NE2000 network card device driver. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-yellowfin +Packet Engines Yellowfin Gigabit-NIC network card device driver. On +@samp{ix86-at} enabled by @samp{default}. + +@item --enable-rtl8139 +RealTek 8129/8139 (not 8019/8029!) network card device driver. On +@samp{ix86-at} enabled by @samp{default}. + +@item --enable-epic100 +SMC 83c170/175 EPIC/100 (EtherPower II) network card device driver. On +@samp{ix86-at} enabled by @samp{default}. + +@item --enable-tlan +TI ThunderLAN network card device driver. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-via-rhine +VIA Rhine network card device driver. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-hamachi +Packet Engines "Hamachi" GNIC-2 Gigabit Ethernet device driver. On +@samp{ix86-at} enabled by @samp{default}. + +@item --enable-intel-gige +Intel PCI Gigabit Ethernet device driver. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-myson803 +Myson MTD803 Ethernet adapter series device driver. On @samp{ix86-at} enabled +by @samp{default}. + +@item --enable-natsemi +National Semiconductor DP8381x series PCI Ethernet device driver. On +@samp{ix86-at} enabled by @samp{default}. + +@item --enable-ns820 +National Semiconductor DP8382x series PCI Ethernet device driver. On +@samp{ix86-at} enabled by @samp{default}. + +@item --enable-starfire +Adaptec Starfire network adapter device driver. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-sundance +Sundance ST201 "Alta" PCI Ethernet device driver. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-winbond-840 +Winbond W89c840 PCI Ethernet device driver. On @samp{ix86-at} enabled by +@samp{default}. +@end table + +The following options either control device drivers for supported PCMCIA +bridges or control the overall behaviour of the GNU Mach PCMCIA core. To make +use of GNU Mach PCMCIA support you need to have the corresponding userland +applications (GNU Mach Card Services) installed. + +@table @code +@item --enable-i82365 +Device driver for the Intel 82365 and compatible PC Card controllers, and +Yenta-compatible PCI-to-CardBus controllers. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-pcmcia-isa +ISA bus related bits in the GNU Mach PCMCIA core. Keeping it enabled is +generally a good idea, since it does not only have effect if your PC Card +bridge is attached to the ISA bus, but provides more (ISA) interrupts to the +Card Services for it to assign to the cards in turn. On @samp{ix86-at} enabled +by @samp{default}. +@end table + +The following options control device drivers for supported PCMCIA Ethernet +controllers. NIC devices are usually named @samp{eth%d}. + +@table @code +@item --enable-3c574_cs +PCMCIA ethernet driver for the 3Com 3c574 ``RoadRunner''. On @samp{ix86-at} +enabled by @samp{default}. + +@item --enable-3c589_cs +Driver for the 3Com 3c589 PCMCIA card. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-axnet_cs +Driver for the Asix AX88190-based PCMCIA cards. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-fmvj18x_cs +Driver for PCMCIA cards with the fmvj18x chipset. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-nmclan_cs +Driver for the New Media Ethernet LAN PCMCIA cards. On @samp{ix86-at} enabled +by @samp{default}. + +@item --enable-pcnet_cs +Driver for NS8390-based PCMCIA cards. This driver supports the D-Link DE-650 +and Linksys EthernetCard cards, the newer D-Link and Linksys combo cards, +Accton EN2212 cards, the RPTI EP400, and the PreMax PE-200 in non-shared-memory +mode, and the IBM Credit Card Adapter, the NE4100, the Thomas Conrad ethernet +card, and the Kingston KNE-PCM/x in shared-memory mode. It will also handle +the Socket EA card in either mode. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-smc91c92_cs +Driver for SMC91c92-based PCMCIA cards. On @samp{ix86-at} enabled by +@samp{default}. + +@item --enable-xirc2ps_cs +Driver for Xircom CreditCard and Realport PCMCIA ethernet adapters. On +@samp{ix86-at} enabled by @samp{default}. +@end table + +The following options control device drivers for supported PCMCIA Wireless LAN +network controllers. NIC devices are usually named @samp{eth%d}. + +Please mind, that you need to have some userland applications (the GNU Mach +Wireless Tools) installed, in order to make use of these devices. + +@table @code +@item --enable-orinoco_cs +Driver for the Hermes or Prism 2 chipset based PCMCIA wireless adapters, with +Lucent/Agere, Intersil or Symbol firmware. This driver is suitable for PCMCIA +wireless adapters, such as the Lucent WavelanIEEE/Orinoco cards and their OEM +(Cabletron/EnteraSys RoamAbout 802.11, ELSA Airlancer, Melco Buffalo and +others). It should also be usable on various Prism II based cards such as the +Linksys, D-Link and Farallon Skyline. It should also work on Symbol cards such +as the 3Com AirConnect and Ericsson WLAN. On @samp{ix86-at} enabled by +@samp{default}. +@end table + + +@node Cross-Compilation +@section Cross-Compilation + +Another way to install the kernel is to use an existing operating system +in order to compile the kernel binary. +This is called @dfn{cross-compiling}, because it is done between two +different platforms. If the pre-built kernels are not working for +you, and you can't ask someone to compile a custom kernel for your +machine, this is your last chance to get a kernel that boots on your +hardware. + +Luckily, the kernel does have light dependencies. You don't even +need a cross compiler if your build machine has a compiler and is +the same architecture as the system you want to run GNU Mach on. + +You need a cross-mig, though. + +XXX More info needed. + + +@node Bootstrap +@chapter Bootstrap + +Bootstrapping@footnote{The term @dfn{bootstrapping} refers to a Dutch +legend about a boy who was able to fly by pulling himself up by his +bootstraps. In computers, this term refers to any process where a +simple system activates a more complicated system.} is the procedure by +which your machine loads the microkernel and transfers control to the +operating system. + + +@menu +* Bootloader:: Starting the microkernel, or other OSes. +* Modules:: Starting the first task of the OS. +@end menu + +@node Bootloader +@section Bootloader + +The @dfn{bootloader} is the first software that runs on your machine. +Many hardware architectures have a very simple startup routine which +reads a very simple bootloader from the beginning of the internal hard +disk, then transfers control to it. Other architectures have startup +routines which are able to understand more of the contents of the hard +disk, and directly start a more advanced bootloader. + +@cindex GRUB +@cindex GRand Unified Bootloader +@dfn{GRUB}@footnote{The GRand Unified Bootloader, available +from @uref{http://gnu.org/software/grub/}.} is the GNU bootloader. +GRUB provides advanced functionality, and is capable of loading several +different kernels (such as Mach, Linux, DOS, and the *BSD family). +@xref{Top, , Introduction, grub, GRUB Manual}. + +GNU Mach conforms to the Multiboot specification which defines an +interface between the bootloader and the components that run very early +at startup. GNU Mach can be started by any bootloader which supports +the multiboot standard. After the bootloader loaded the kernel image to +a designated address in the system memory, it jumps into the startup +code of the kernel. This code initializes the kernel and detects the +available hardware devices. Afterwards, the first system task is +started. @xref{Top, , Overview, multiboot, Multiboot Specification}. + + +@node Modules +@section Modules +@pindex serverboot + +This is outdated. + +Because the microkernel does not provide filesystem support and other +features necessary to load the first system task from a storage medium, +the first task is loaded by the bootloader as a module to a specified +address. In the GNU system, this first program is the @code{serverboot} +executable. GNU Mach inserts the host control port and the device +master port into this task and appends the port numbers to the command +line before executing it. + +The @code{serverboot} program is responsible for loading and executing +the rest of the Hurd servers. Rather than containing specific +instructions for starting the Hurd, it follows general steps given in a +user-supplied boot script. + +XXX More about boot scripts. + + +@node Inter Process Communication +@chapter Inter Process Communication + +This chapter describes the details of the Mach IPC system. First the +actual calls concerned with sending and receiving messages are +discussed, then the details of the port system are described in detail. + +@menu +* Major Concepts:: The concepts behind the Mach IPC system. +* Messaging Interface:: Composing, sending and receiving messages. +* Port Manipulation Interface:: Manipulating ports, port rights, port sets. +@end menu + + +@node Major Concepts +@section Major Concepts +@cindex interprocess communication (IPC) +@cindex IPC (interprocess communication) +@cindex communication between tasks +@cindex remote procedure calls (RPC) +@cindex RPC (remote procedure calls) +@cindex messages + +The Mach kernel provides message-oriented, capability-based interprocess +communication. The interprocess communication (IPC) primitives +efficiently support many different styles of interaction, including +remote procedure calls (RPC), object-oriented distributed programming, +streaming of data, and sending very large amounts of data. + +The IPC primitives operate on three abstractions: messages, ports, and +port sets. User tasks access all other kernel services and abstractions +via the IPC primitives. + +The message primitives let tasks send and receive messages. Tasks send +messages to ports. Messages sent to a port are delivered reliably +(messages may not be lost) and are received in the order in which they +were sent. Messages contain a fixed-size header and a variable amount +of typed data following the header. The header describes the +destination and size of the message. + +The IPC implementation makes use of the VM system to efficiently +transfer large amounts of data. The message body can contain the +address of a region in the sender's address space which should be +transferred as part of the message. When a task receives a message +containing an out-of-line region of data, the data appears in an unused +portion of the receiver's address space. This transmission of +out-of-line data is optimized so that sender and receiver share the +physical pages of data copy-on-write, and no actual data copy occurs +unless the pages are written. Regions of memory up to the size of a +full address space may be sent in this manner. + +Ports hold a queue of messages. Tasks operate on a port to send and +receive messages by exercising capabilities for the port. Multiple +tasks can hold send capabilities, or rights, for a port. Tasks can also +hold send-once rights, which grant the ability to send a single message. +Only one task can hold the receive capability, or receive right, for a +port. Port rights can be transferred between tasks via messages. The +sender of a message can specify in the message body that the message +contains a port right. If a message contains a receive right for a +port, then the receive right is removed from the sender of the message +and the right is transferred to the receiver of the message. While the +receive right is in transit, tasks holding send rights can still send +messages to the port, and they are queued until a task acquires the +receive right and uses it to receive the messages. + +Tasks can receive messages from ports and port sets. The port set +abstraction allows a single thread to wait for a message from any of +several ports. Tasks manipulate port sets with a capability, or +port-set right, which is taken from the same space as the port +capabilities. The port-set right may not be transferred in a message. +A port set holds receive rights, and a receive operation on a port set +blocks waiting for a message sent to any of the constituent ports. A +port may not belong to more than one port set, and if a port is a member +of a port set, the holder of the receive right can't receive directly +from the port. + +Port rights are a secure, location-independent way of naming ports. The +port queue is a protected data structure, only accessible via the +kernel's exported message primitives. Rights are also protected by the +kernel; there is no way for a malicious user task to guess a port name +and send a message to a port to which it shouldn't have access. Port +rights do not carry any location information. When a receive right for +a port moves from task to task, and even between tasks on different +machines, the send rights for the port remain unchanged and continue to +function. + +@node Messaging Interface +@section Messaging Interface + +This section describes how messages are composed, sent and received +within the Mach IPC system. + +@menu +* Mach Message Call:: Sending and receiving messages. +* Message Format:: The format of Mach messages. +* Exchanging Port Rights:: Sending and receiving port rights. +* Memory:: Passing memory regions in messages. +* Message Send:: Sending messages. +* Message Receive:: Receiving messages. +* Atomicity:: Atomicity of port rights. +@end menu + + +@node Mach Message Call +@subsection Mach Message Call + +To use the @code{mach_msg} call, you can include the header files +@file{mach/port.h} and @file{mach/message.h}. + +@deftypefun mach_msg_return_t mach_msg (@w{mach_msg_header_t *@var{msg}}, @w{mach_msg_option_t @var{option}}, @w{mach_msg_size_t @var{send_size}}, @w{mach_msg_size_t @var{rcv_size}}, @w{mach_port_t @var{rcv_name}}, @w{mach_msg_timeout_t @var{timeout}}, @w{mach_port_t @var{notify}}) +The @code{mach_msg} function is used to send and receive messages. Mach +messages contain typed data, which can include port rights and +references to large regions of memory. + +@var{msg} is the address of a buffer in the caller's address space. +Message buffers should be aligned on long-word boundaries. The message +options @var{option} are bit values, combined with bitwise-or. One or +both of @code{MACH_SEND_MSG} and @code{MACH_RCV_MSG} should be used. +Other options act as modifiers. When sending a message, @var{send_size} +specifies the size of the message buffer. Otherwise zero should be +supplied. When receiving a message, @var{rcv_size} specifies the size +of the message buffer. Otherwise zero should be supplied. When +receiving a message, @var{rcv_name} specifies the port or port set. +Otherwise @code{MACH_PORT_NULL} should be supplied. When using the +@code{MACH_SEND_TIMEOUT} and @code{MACH_RCV_TIMEOUT} options, +@var{timeout} specifies the time in milliseconds to wait before giving +up. Otherwise @code{MACH_MSG_TIMEOUT_NONE} should be supplied. When +using the @code{MACH_SEND_NOTIFY}, @code{MACH_SEND_CANCEL}, and +@code{MACH_RCV_NOTIFY} options, @var{notify} specifies the port used for +the notification. Otherwise @code{MACH_PORT_NULL} should be supplied. + +If the option argument is @code{MACH_SEND_MSG}, it sends a message. The +@var{send_size} argument specifies the size of the message to send. The +@code{msgh_remote_port} field of the message header specifies the +destination of the message. + +If the option argument is @code{MACH_RCV_MSG}, it receives a message. +The @var{rcv_size} argument specifies the size of the message buffer +that will receive the message; messages larger than @var{rcv_size} are +not received. The @var{rcv_name} argument specifies the port or port +set from which to receive. + +If the option argument is @code{MACH_SEND_MSG|MACH_RCV_MSG}, then +@code{mach_msg} does both send and receive operations. If the send +operation encounters an error (any return code other than +@code{MACH_MSG_SUCCESS}), then the call returns immediately without +attempting the receive operation. Semantically the combined call is +equivalent to separate send and receive calls, but it saves a system +call and enables other internal optimizations. + +If the option argument specifies neither @code{MACH_SEND_MSG} nor +@code{MACH_RCV_MSG}, then @code{mach_msg} does nothing. + +Some options, like @code{MACH_SEND_TIMEOUT} and @code{MACH_RCV_TIMEOUT}, +share a supporting argument. If these options are used together, they +make independent use of the supporting argument's value. +@end deftypefun + +@deftp {Data type} mach_msg_timeout_t +This is a @code{natural_t} used by the timeout mechanism. The units are +milliseconds. The value to be used when there is no timeout is +@code{MACH_MSG_TIMEOUT_NONE}. +@end deftp + + +@node Message Format +@subsection Message Format +@cindex message format +@cindex format of a message +@cindex composing messages +@cindex message composition + +A Mach message consists of a fixed size message header, a +@code{mach_msg_header_t}, followed by zero or more data items. Data +items are typed. Each item has a type descriptor followed by the actual +data (or the address of the data, for out-of-line memory regions). + +The following data types are related to Mach ports: + +@deftp {Data type} mach_port_t +The @code{mach_port_t} data type is an unsigned integer type which +represents a port name in the task's port name space. In GNU Mach, this +is an @code{unsigned int}. +@end deftp + +@c This is defined elsewhere. +@c @deftp {Data type} mach_port_seqno_t +@c The @code{mach_port_seqno_t} data type is an unsigned integer type which +@c represents a sequence number of a message. In GNU Mach, this is an +@c @code{unsigned int}. +@c @end deftp + +The following data types are related to Mach messages: + +@deftp {Data type} mach_msg_bits_t +The @code{mach_msg_bits_t} data type is an @code{unsigned int} used to +store various flags for a message. +@end deftp + +@deftp {Data type} mach_msg_size_t +The @code{mach_msg_size_t} data type is an @code{unsigned int} used to +store the size of a message. +@end deftp + +@deftp {Data type} mach_msg_id_t +The @code{mach_msg_id_t} data type is an @code{integer_t} typically used to +convey a function or operation id for the receiver. +@end deftp + +@deftp {Data type} mach_msg_header_t +This structure is the start of every message in the Mach IPC system. It +has the following members: + +@table @code +@item mach_msg_bits_t msgh_bits +The @code{msgh_bits} field has the following bits defined, all other +bits should be zero: + +@table @code +@item MACH_MSGH_BITS_REMOTE_MASK +@itemx MACH_MSGH_BITS_LOCAL_MASK +The remote and local bits encode @code{mach_msg_type_name_t} values that +specify the port rights in the @code{msgh_remote_port} and +@code{msgh_local_port} fields. The remote value must specify a send or +send-once right for the destination of the message. If the local value +doesn't specify a send or send-once right for the message's reply port, +it must be zero and msgh_local_port must be @code{MACH_PORT_NULL}. + +@item MACH_MSGH_BITS_COMPLEX +The complex bit must be specified if the message body contains port +rights or out-of-line memory regions. If it is not specified, then the +message body carries no port rights or memory, no matter what the type +descriptors may seem to indicate. +@end table + +@code{MACH_MSGH_BITS_REMOTE} and @code{MACH_MSGH_BITS_LOCAL} macros +return the appropriate @code{mach_msg_type_name_t} values, given a +@code{msgh_bits} value. The @code{MACH_MSGH_BITS} macro constructs a +value for @code{msgh_bits}, given two @code{mach_msg_type_name_t} +values. + +@item mach_msg_size_t msgh_size +The @code{msgh_size} field in the header of a received message contains +the message's size. The message size, a byte quantity, includes the +message header, type descriptors, and in-line data. For out-of-line +memory regions, the message size includes the size of the in-line +address, not the size of the actual memory region. There are no +arbitrary limits on the size of a Mach message, the number of data items +in a message, or the size of the data items. + +@item mach_port_t msgh_remote_port +The @code{msgh_remote_port} field specifies the destination port of the +message. The field must carry a legitimate send or send-once right for +a port. + +@item mach_port_t msgh_local_port +The @code{msgh_local_port} field specifies an auxiliary port right, +which is conventionally used as a reply port by the recipient of the +message. The field must carry a send right, a send-once right, +@code{MACH_PORT_NULL}, or @code{MACH_PORT_DEAD}. + +@item unsigned long msgh_protected_payload +The @code{msgh_protected_payload} field carries a payload that is set +by the kernel during message delivery. The payload is an opaque +identifier that can be used by the receiver to lookup the associated +data structure. + +It is only valid in received messages. See @ref{Message Receive} for +further information. + +@item mach_port_seqno_t msgh_seqno +The @code{msgh_seqno} field provides a sequence number for the message. +It is only valid in received messages; its value in sent messages is +overwritten. +@c XXX The "MESSAGE RECEIVE" section discusses message sequence numbers. + +@item mach_msg_id_t msgh_id +The @code{mach_msg} call doesn't use the @code{msgh_id} field, but it +conventionally conveys an operation or function id. +@end table +@end deftp + +@deftypefn Macro mach_msg_bits_t MACH_MSGH_BITS (@w{mach_msg_type_name_t @var{remote}}, @w{mach_msg_type_name_t @var{local}}) +This macro composes two @code{mach_msg_type_name_t} values that specify +the port rights in the @code{msgh_remote_port} and +@code{msgh_local_port} fields of a @code{mach_msg} call into an +appropriate @code{mach_msg_bits_t} value. +@end deftypefn + +@deftypefn Macro mach_msg_type_name_t MACH_MSGH_BITS_REMOTE (@w{mach_msg_bits_t @var{bits}}) +This macro extracts the @code{mach_msg_type_name_t} value for the remote +port right in a @code{mach_msg_bits_t} value. +@end deftypefn + +@deftypefn Macro mach_msg_type_name_t MACH_MSGH_BITS_LOCAL (@w{mach_msg_bits_t @var{bits}}) +This macro extracts the @code{mach_msg_type_name_t} value for the local +port right in a @code{mach_msg_bits_t} value. +@end deftypefn + +@deftypefn Macro mach_msg_bits_t MACH_MSGH_BITS_PORTS (@w{mach_msg_bits_t @var{bits}}) +This macro extracts the @code{mach_msg_bits_t} component consisting of +the @code{mach_msg_type_name_t} values for the remote and local port +right in a @code{mach_msg_bits_t} value. +@end deftypefn + +@deftypefn Macro mach_msg_bits_t MACH_MSGH_BITS_OTHER (@w{mach_msg_bits_t @var{bits}}) +This macro extracts the @code{mach_msg_bits_t} component consisting of +everything except the @code{mach_msg_type_name_t} values for the remote +and local port right in a @code{mach_msg_bits_t} value. +@end deftypefn + +Each data item has a type descriptor, a @code{mach_msg_type_t} or a +@code{mach_msg_type_long_t}. The @code{mach_msg_type_long_t} type +descriptor allows larger values for some fields. The +@code{msgtl_header} field in the long descriptor is only used for its +inline, longform, and deallocate bits. + +@deftp {Data type} mach_msg_type_name_t +This is an @code{unsigned int} and can be used to hold the +@code{msgt_name} component of the @code{mach_msg_type_t} and +@code{mach_msg_type_long_t} structure. +@end deftp + +@deftp {Data type} mach_msg_type_size_t +This is an @code{unsigned int} and can be used to hold the +@code{msgt_size} component of the @code{mach_msg_type_t} and +@code{mach_msg_type_long_t} structure. +@end deftp + +@deftp {Data type} mach_msg_type_number_t +This is an @code{natural_t} and can be used to hold the +@code{msgt_number} component of the @code{mach_msg_type_t} and +@code{mach_msg_type_long_t} structure. +@c XXX This is used for the size of arrays, too. Mmh? +@end deftp + +@deftp {Data type} mach_msg_type_t +This structure has the following members: + +@table @code +@item unsigned int msgt_name : 8 +The @code{msgt_name} field specifies the data's type. The following +types are predefined: + +@table @code +@item MACH_MSG_TYPE_UNSTRUCTURED +@item MACH_MSG_TYPE_BIT +@item MACH_MSG_TYPE_BOOLEAN +@item MACH_MSG_TYPE_INTEGER_16 +@item MACH_MSG_TYPE_INTEGER_32 +@item MACH_MSG_TYPE_CHAR +@item MACH_MSG_TYPE_BYTE +@item MACH_MSG_TYPE_INTEGER_8 +@item MACH_MSG_TYPE_REAL +@item MACH_MSG_TYPE_STRING +@item MACH_MSG_TYPE_STRING_C +@item MACH_MSG_TYPE_PORT_NAME +@item MACH_MSG_TYPE_PROTECTED_PAYLOAD +@end table + +The following predefined types specify port rights, and receive special +treatment. The next section discusses these types in detail. The type +@c XXX cross ref +@code{MACH_MSG_TYPE_PORT_NAME} describes port right names, when no +rights are being transferred, but just names. For this purpose, it +should be used in preference to @code{MACH_MSG_TYPE_INTEGER_32}. + +@table @code +@item MACH_MSG_TYPE_MOVE_RECEIVE +@item MACH_MSG_TYPE_MOVE_SEND +@item MACH_MSG_TYPE_MOVE_SEND_ONCE +@item MACH_MSG_TYPE_COPY_SEND +@item MACH_MSG_TYPE_MAKE_SEND +@item MACH_MSG_TYPE_MAKE_SEND_ONCE +@end table + +The type @code{MACH_MSG_TYPE_PROTECTED_PAYLOAD} is used by the kernel +to indicate that a delivered message carries a payload in the +@code{msgh_protected_payload} field. See @ref{Message Receive} for +more information. + +@item msgt_size : 8 +The @code{msgt_size} field specifies the size of each datum, in bits. For +example, the msgt_size of @code{MACH_MSG_TYPE_INTEGER_32} data is 32. + +@item msgt_number : 12 +The @code{msgt_number} field specifies how many data elements comprise +the data item. Zero is a legitimate number. + +The total length specified by a type descriptor is @w{@code{(msgt_size * +msgt_number)}}, rounded up to an integral number of bytes. In-line data +is then padded to an integral number of long-words. This ensures that +type descriptors always start on long-word boundaries. It implies that +message sizes are always an integral multiple of a long-word's size. + +@item msgt_inline : 1 +The @code{msgt_inline} bit specifies, when @code{FALSE}, that the data +actually resides in an out-of-line region. The address of the memory +region (a @code{vm_offset_t} or @code{vm_address_t}) follows the type +descriptor in the message body. The @code{msgt_name}, @code{msgt_size}, +and @code{msgt_number} fields describe the memory region, not the +address. + +@item msgt_longform : 1 +The @code{msgt_longform} bit specifies, when @code{TRUE}, that this type +descriptor is a @code{mach_msg_type_long_t} instead of a +@code{mach_msg_type_t}. The @code{msgt_name}, @code{msgt_size}, and +@code{msgt_number} fields should be zero. Instead, @code{mach_msg} uses +the following @code{msgtl_name}, @code{msgtl_size}, and +@code{msgtl_number} fields. + +@item msgt_deallocate : 1 +The @code{msgt_deallocate} bit is used with out-of-line regions. When +@code{TRUE}, it specifies that the memory region should be deallocated +from the sender's address space (as if with @code{vm_deallocate}) when +the message is sent. + +@item msgt_unused : 1 +The @code{msgt_unused} bit should be zero. +@end table +@end deftp + +@deftypefn Macro boolean_t MACH_MSG_TYPE_PORT_ANY (mach_msg_type_name_t type) +This macro returns @code{TRUE} if the given type name specifies a port +type, otherwise it returns @code{FALSE}. +@end deftypefn + +@deftypefn Macro boolean_t MACH_MSG_TYPE_PORT_ANY_SEND (mach_msg_type_name_t type) +This macro returns @code{TRUE} if the given type name specifies a port +type with a send or send-once right, otherwise it returns @code{FALSE}. +@end deftypefn + +@deftypefn Macro boolean_t MACH_MSG_TYPE_PORT_ANY_RIGHT (mach_msg_type_name_t type) +This macro returns @code{TRUE} if the given type name specifies a port +right type which is moved, otherwise it returns @code{FALSE}. +@end deftypefn + +@deftp {Data type} mach_msg_type_long_t +This structure has the following members: + +@table @code +@item mach_msg_type_t msgtl_header +Same meaning as @code{msgt_header}. +@c XXX cross ref + +@item unsigned short msgtl_name +Same meaning as @code{msgt_name}. + +@item unsigned short msgtl_size +Same meaning as @code{msgt_size}. + +@item unsigned int msgtl_number +Same meaning as @code{msgt_number}. +@end table +@end deftp + + +@node Exchanging Port Rights +@subsection Exchanging Port Rights +@cindex sending port rights +@cindex receiving port rights +@cindex moving port rights + +Each task has its own space of port rights. Port rights are named with +positive integers. Except for the reserved values +@w{@code{MACH_PORT_NULL (0)}@footnote{In the Hurd system, we don't make +the assumption that @code{MACH_PORT_NULL} is zero and evaluates to +false, but rather compare port names to @code{MACH_PORT_NULL} +explicitly}} and @w{@code{MACH_PORT_DEAD (~0)}}, this is a full 32-bit +name space. When the kernel chooses a name for a new right, it is free +to pick any unused name (one which denotes no right) in the space. + +There are five basic kinds of rights: receive rights, send rights, +send-once rights, port-set rights, and dead names. Dead names are not +capabilities. They act as place-holders to prevent a name from being +otherwise used. + +A port is destroyed, or dies, when its receive right is deallocated. +When a port dies, send and send-once rights for the port turn into dead +names. Any messages queued at the port are destroyed, which deallocates +the port rights and out-of-line memory in the messages. + +Tasks may hold multiple user-references for send rights and dead names. +When a task receives a send right which it already holds, the kernel +increments the right's user-reference count. When a task deallocates a +send right, the kernel decrements its user-reference count, and the task +only loses the send right when the count goes to zero. + +Send-once rights always have a user-reference count of one, although a +port can have multiple send-once rights, because each send-once right +held by a task has a different name. In contrast, when a task holds +send rights or a receive right for a port, the rights share a single +name. + +A message body can carry port rights; the @code{msgt_name} +(@code{msgtl_name}) field in a type descriptor specifies the type of +port right and how the port right is to be extracted from the caller. +The values @code{MACH_PORT_NULL} and @code{MACH_PORT_DEAD} are always +valid in place of a port right in a message body. In a sent message, +the following @code{msgt_name} values denote port rights: + +@table @code +@item MACH_MSG_TYPE_MAKE_SEND +The message will carry a send right, but the caller must supply a +receive right. The send right is created from the receive right, and +the receive right's make-send count is incremented. + +@item MACH_MSG_TYPE_COPY_SEND +The message will carry a send right, and the caller should supply a send +right. The user reference count for the supplied send right is not +changed. The caller may also supply a dead name and the receiving task +will get @code{MACH_PORT_DEAD}. + +@item MACH_MSG_TYPE_MOVE_SEND +The message will carry a send right, and the caller should supply a send +right. The user reference count for the supplied send right is +decremented, and the right is destroyed if the count becomes zero. +Unless a receive right remains, the name becomes available for +recycling. The caller may also supply a dead name, which loses a user +reference, and the receiving task will get @code{MACH_PORT_DEAD}. + +@item MACH_MSG_TYPE_MAKE_SEND_ONCE +The message will carry a send-once right, but the caller must supply a +receive right. The send-once right is created from the receive right. + +@item MACH_MSG_TYPE_MOVE_SEND_ONCE +The message will carry a send-once right, and the caller should supply a +send-once right. The caller loses the supplied send-once right. The +caller may also supply a dead name, which loses a user reference, and +the receiving task will get @code{MACH_PORT_DEAD}. + +@item MACH_MSG_TYPE_MOVE_RECEIVE +The message will carry a receive right, and the caller should supply a +receive right. The caller loses the supplied receive right, but retains +any send rights with the same name. +@end table + +If a message carries a send or send-once right, and the port dies while +the message is in transit, then the receiving task will get +@code{MACH_PORT_DEAD} instead of a right. The following +@code{msgt_name} values in a received message indicate that it carries +port rights: + +@table @code +@item MACH_MSG_TYPE_PORT_SEND +This name is an alias for @code{MACH_MSG_TYPE_MOVE_SEND}. The message +carried a send right. If the receiving task already has send and/or +receive rights for the port, then that name for the port will be reused. +Otherwise, the new right will have a new name. If the task already has +send rights, it gains a user reference for the right (unless this would +cause the user-reference count to overflow). Otherwise, it acquires the +send right, with a user-reference count of one. + +@item MACH_MSG_TYPE_PORT_SEND_ONCE +This name is an alias for @code{MACH_MSG_TYPE_MOVE_SEND_ONCE}. The +message carried a send-once right. The right will have a new name. + +@item MACH_MSG_TYPE_PORT_RECEIVE +This name is an alias for @code{MACH_MSG_TYPE_MOVE_RECEIVE}. The +message carried a receive right. If the receiving task already has send +rights for the port, then that name for the port will be reused. +Otherwise, the right will have a new name. The make-send count of the +receive right is reset to zero, but the port retains other attributes +like queued messages, extant send and send-once rights, and requests for +port-destroyed and no-senders notifications. +@end table + +When the kernel chooses a new name for a port right, it can choose any +name, other than @code{MACH_PORT_NULL} and @code{MACH_PORT_DEAD}, which +is not currently being used for a port right or dead name. It might +choose a name which at some previous time denoted a port right, but is +currently unused. + + +@node Memory +@subsection Memory +@cindex sending memory +@cindex receiving memory + +A message body can contain the address of a region in the sender's +address space which should be transferred as part of the message. The +message carries a logical copy of the memory, but the kernel uses VM +techniques to defer any actual page copies. Unless the sender or the +receiver modifies the data, the physical pages remain shared. + +An out-of-line transfer occurs when the data's type descriptor specifies +@code{msgt_inline} as @code{FALSE}. The address of the memory region (a +@code{vm_offset_t} or @code{vm_address_t}) should follow the type +descriptor in the message body. The type descriptor and the address +contribute to the message's size (@code{send_size}, @code{msgh_size}). +The out-of-line data does not contribute to the message's size. + +The name, size, and number fields in the type descriptor describe the +type and length of the out-of-line data, not the in-line address. +Out-of-line memory frequently requires long type descriptors +(@code{mach_msg_type_long_t}), because the @code{msgt_number} field is +too small to describe a page of 4K bytes. + +Out-of-line memory arrives somewhere in the receiver's address space as +new memory. It has the same inheritance and protection attributes as +newly @code{vm_allocate}'d memory. The receiver has the responsibility +of deallocating (with @code{vm_deallocate}) the memory when it is no +longer needed. Security-conscious receivers should exercise caution +when using out-of-line memory from untrustworthy sources, because the +memory may be backed by an unreliable memory manager. + +Null out-of-line memory is legal. If the out-of-line region size is +zero (for example, because @code{msgtl_number} is zero), then the +region's specified address is ignored. A received null out-of-line +memory region always has a zero address. + +Unaligned addresses and region sizes that are not page multiples are +legal. A received message can also contain memory with unaligned +addresses and funny sizes. In the general case, the first and last +pages in the new memory region in the receiver do not contain only data +from the sender, but are partly zero.@footnote{Sending out-of-line +memory with a non-page-aligned address, or a size which is not a page +multiple, works but with a caveat. The extra bytes in the first and +last page of the received memory are not zeroed, so the receiver can +peek at more data than the sender intended to transfer. This might be a +security problem for the sender.} The received address points to the +start of the data in the first page. This possibility doesn't +complicate deallocation, because @code{vm_deallocate} does the right +thing, rounding the start address down and the end address up to +deallocate all arrived pages. + +Out-of-line memory has a deallocate option, controlled by the +@code{msgt_deallocate} bit. If it is @code{TRUE} and the out-of-line +memory region is not null, then the region is implicitly deallocated +from the sender, as if by @code{vm_deallocate}. In particular, the +start and end addresses are rounded so that every page overlapped by the +memory region is deallocated. The use of @code{msgt_deallocate} +effectively changes the memory copy into a memory movement. In a +received message, @code{msgt_deallocate} is @code{TRUE} in type +descriptors for out-of-line memory. + +Out-of-line memory can carry port rights. + + +@node Message Send +@subsection Message Send +@cindex sending messages + +The send operation queues a message to a port. The message carries a +copy of the caller's data. After the send, the caller can freely modify +the message buffer or the out-of-line memory regions and the message +contents will remain unchanged. + +Message delivery is reliable and sequenced. Messages are not lost, and +messages sent to a port, from a single thread, are received in the order +in which they were sent. + +If the destination port's queue is full, then several things can happen. +If the message is sent to a send-once right (@code{msgh_remote_port} +carries a send-once right), then the kernel ignores the queue limit and +delivers the message. Otherwise the caller blocks until there is room +in the queue, unless the @code{MACH_SEND_TIMEOUT} or +@code{MACH_SEND_NOTIFY} options are used. If a port has several blocked +senders, then any of them may queue the next message when space in the +queue becomes available, with the proviso that a blocked sender will not +be indefinitely starved. + +These options modify @code{MACH_SEND_MSG}. If @code{MACH_SEND_MSG} is +not also specified, they are ignored. + +@table @code +@item MACH_SEND_TIMEOUT +The timeout argument should specify a maximum time (in milliseconds) for +the call to block before giving up.@footnote{If MACH_SEND_TIMEOUT is +used without MACH_SEND_INTERRUPT, then the timeout duration might not be +accurate. When the call is interrupted and automatically retried, the +original timeout is used. If interrupts occur frequently enough, the +timeout interval might never expire.} If the message can't be queued +before the timeout interval elapses, then the call returns +@code{MACH_SEND_TIMED_OUT}. A zero timeout is legitimate. + +@item MACH_SEND_NOTIFY +The notify argument should specify a receive right for a notify port. +If the send were to block, then instead the message is queued, +@code{MACH_SEND_WILL_NOTIFY} is returned, and a msg-accepted +notification is requested. If @code{MACH_SEND_TIMEOUT} is also +specified, then @code{MACH_SEND_NOTIFY} doesn't take effect until the +timeout interval elapses. + +With @code{MACH_SEND_NOTIFY}, a task can forcibly queue to a send right +one message at a time. A msg-accepted notification is sent to the +notify port when another message can be forcibly queued. If an attempt +is made to use @code{MACH_SEND_NOTIFY} before then, the call returns a +@code{MACH_SEND_NOTIFY_IN_PROGRESS} error. + +The msg-accepted notification carries the name of the send right. If +the send right is deallocated before the msg-accepted notification is +generated, then the msg-accepted notification carries the value +@code{MACH_PORT_NULL}. If the destination port is destroyed before the +notification is generated, then a send-once notification is generated +instead. + +@item MACH_SEND_INTERRUPT +If specified, the @code{mach_msg} call will return +@code{MACH_SEND_INTERRUPTED} if a software interrupt aborts the call. +Otherwise, the send operation will be retried. + +@item MACH_SEND_CANCEL +The notify argument should specify a receive right for a notify port. +If the send operation removes the destination port right from the +caller, and the removed right had a dead-name request registered for it, +and notify is the notify port for the dead-name request, then the +dead-name request may be silently canceled (instead of resulting in a +port-deleted notification). + +This option is typically used to cancel a dead-name request made with +the @code{MACH_RCV_NOTIFY} option. It should only be used as an optimization. +@end table + +The send operation can generate the following return codes. These +return codes imply that the call did nothing: + +@table @code +@item MACH_SEND_MSG_TOO_SMALL +The specified send_size was smaller than the minimum size for a message. + +@item MACH_SEND_NO_BUFFER +A resource shortage prevented the kernel from allocating a message +buffer. + +@item MACH_SEND_INVALID_DATA +The supplied message buffer was not readable. + +@item MACH_SEND_INVALID_HEADER +The @code{msgh_bits} value was invalid. + +@item MACH_SEND_INVALID_DEST +The @code{msgh_remote_port} value was invalid. + +@item MACH_SEND_INVALID_REPLY +The @code{msgh_local_port} value was invalid. + +@item MACH_SEND_INVALID_NOTIFY +When using @code{MACH_SEND_CANCEL}, the notify argument did not denote a +valid receive right. +@end table + +These return codes imply that some or all of the message was destroyed: + +@table @code +@item MACH_SEND_INVALID_MEMORY +The message body specified out-of-line data that was not readable. + +@item MACH_SEND_INVALID_RIGHT +The message body specified a port right which the caller didn't possess. + +@item MACH_SEND_INVALID_TYPE +A type descriptor was invalid. + +@item MACH_SEND_MSG_TOO_SMALL +The last data item in the message ran over the end of the message. +@end table + +These return codes imply that the message was returned to the caller +with a pseudo-receive operation: + +@table @code +@item MACH_SEND_TIMED_OUT +The timeout interval expired. + +@item MACH_SEND_INTERRUPTED +A software interrupt occurred. + +@item MACH_SEND_INVALID_NOTIFY +When using @code{MACH_SEND_NOTIFY}, the notify argument did not denote a +valid receive right. + +@item MACH_SEND_NO_NOTIFY +A resource shortage prevented the kernel from setting up a msg-accepted +notification. + +@item MACH_SEND_NOTIFY_IN_PROGRESS +A msg-accepted notification was already requested, and hasn't yet been +generated. +@end table + +These return codes imply that the message was queued: + +@table @code +@item MACH_SEND_WILL_NOTIFY +The message was forcibly queued, and a msg-accepted notification was +requested. + +@item MACH_MSG_SUCCESS +The message was queued. +@end table + +Some return codes, like @code{MACH_SEND_TIMED_OUT}, imply that the +message was almost sent, but could not be queued. In these situations, +the kernel tries to return the message contents to the caller with a +pseudo-receive operation. This prevents the loss of port rights or +memory which only exist in the message. For example, a receive right +which was moved into the message, or out-of-line memory sent with the +deallocate bit. + +The pseudo-receive operation is very similar to a normal receive +operation. The pseudo-receive handles the port rights in the message +header as if they were in the message body. They are not reversed. +After the pseudo-receive, the message is ready to be resent. If the +message is not resent, note that out-of-line memory regions may have +moved and some port rights may have changed names. + +The pseudo-receive operation may encounter resource shortages. This is +similar to a @code{MACH_RCV_BODY_ERROR} return code from a receive +operation. When this happens, the normal send return codes are +augmented with the @code{MACH_MSG_IPC_SPACE}, @code{MACH_MSG_VM_SPACE}, +@code{MACH_MSG_IPC_KERNEL}, and @code{MACH_MSG_VM_KERNEL} bits to +indicate the nature of the resource shortage. + +The queueing of a message carrying receive rights may create a circular +loop of receive rights and messages, which can never be received. For +example, a message carrying a receive right can be sent to that receive +right. This situation is not an error, but the kernel will +garbage-collect such loops, destroying the messages and ports involved. + + +@node Message Receive +@subsection Message Receive + +The receive operation dequeues a message from a port. The receiving +task acquires the port rights and out-of-line memory regions carried in +the message. + +The @code{rcv_name} argument specifies a port or port set from which to +receive. If a port is specified, the caller must possess the receive +right for the port and the port must not be a member of a port set. If +no message is present, then the call blocks, subject to the +@code{MACH_RCV_TIMEOUT} option. + +If a port set is specified, the call will receive a message sent to any +of the member ports. It is permissible for the port set to have no +member ports, and ports may be added and removed while a receive from +the port set is in progress. The received message can come from any of +the member ports which have messages, with the proviso that a member +port with messages will not be indefinitely starved. The +@code{msgh_local_port} field in the received message header specifies +from which port in the port set the message came. + +The @code{rcv_size} argument specifies the size of the caller's message +buffer. The @code{mach_msg} call will not receive a message larger than +@code{rcv_size}. Messages that are too large are destroyed, unless the +@code{MACH_RCV_LARGE} option is used. + +The destination and reply ports are reversed in a received message +header. The @code{msgh_local_port} field names the destination port, +from which the message was received, and the @code{msgh_remote_port} +field names the reply port right. The bits in @code{msgh_bits} are also +reversed. The @code{MACH_MSGH_BITS_LOCAL} bits have the value +@code{MACH_MSG_TYPE_PORT_SEND} if the message was sent to a send right, +and the value @code{MACH_MSG_TYPE_PORT_SEND_ONCE} if was sent to a +send-once right. The @code{MACH_MSGH_BITS_REMOTE} bits describe the +reply port right. + +A received message can contain port rights and out-of-line memory. The +@code{msgh_local_port} field does not receive a port right; the act of +receiving the message destroys the send or send-once right for the +destination port. The msgh_remote_port field does name a received port +right, the reply port right, and the message body can carry port rights +and memory if @code{MACH_MSGH_BITS_COMPLEX} is present in msgh_bits. +Received port rights and memory should be consumed or deallocated in +some fashion. + +In almost all cases, @code{msgh_local_port} will specify the name of a +receive right, either @code{rcv_name} or if @code{rcv_name} is a port +set, a member of @code{rcv_name}. If other threads are concurrently +manipulating the receive right, the situation is more complicated. If +the receive right is renamed during the call, then +@code{msgh_local_port} specifies the right's new name. If the caller +loses the receive right after the message was dequeued from it, then +@code{mach_msg} will proceed instead of returning +@code{MACH_RCV_PORT_DIED}. If the receive right was destroyed, then +@code{msgh_local_port} specifies @code{MACH_PORT_DEAD}. If the receive +right still exists, but isn't held by the caller, then +@code{msgh_local_port} specifies @code{MACH_PORT_NULL}. + +Servers usually associate some state with a receive right. To that +end, they might use a hash table to look up the state for the port a +message was sent to. To optimize this, a task may associate an opaque +@var{payload} with a receive right using the +@code{mach_port_set_protected_payload} function. Once this is done, +the kernel will set the @code{msgh_protected_payload} field to +@var{payload} when delivering a message to this right and indicate +this by setting the local part of @code{msgh_bits} to +@code{MACH_MSG_TYPE_PROTECTED_PAYLOAD}. + +The support for protected payloads was added to GNU Mach. To preserve +binary compatibility, the @code{msgh_local_port} and +@code{msgh_local_port} share the same location. This makes it +possible to add the payload information without increasing the size of +@code{mach_msg_header_t}. This is an implementation detail. Which +field is valid is determined by the local part of the +@code{msgh_bits}. Existing software is not affected. When a receive +right is transferred to another task, its payload is cleared. + +Received messages are stamped with a sequence number, taken from the +port from which the message was received. (Messages received from a +port set are stamped with a sequence number from the appropriate member +port.) Newly created ports start with a zero sequence number, and the +sequence number is reset to zero whenever the port's receive right moves +between tasks. When a message is dequeued from the port, it is stamped +with the port's sequence number and the port's sequence number is then +incremented. The dequeue and increment operations are atomic, so that +multiple threads receiving messages from a port can use the +@code{msgh_seqno} field to reconstruct the original order of the +messages. + +These options modify @code{MACH_RCV_MSG}. If @code{MACH_RCV_MSG} is not +also specified, they are ignored. + +@table @code +@item MACH_RCV_TIMEOUT +The timeout argument should specify a maximum time (in milliseconds) for +the call to block before giving up.@footnote{If MACH_RCV_TIMEOUT is used +without MACH_RCV_INTERRUPT, then the timeout duration might not be +accurate. When the call is interrupted and automatically retried, the +original timeout is used. If interrupts occur frequently enough, the +timeout interval might never expire.} If no message arrives before the +timeout interval elapses, then the call returns +@code{MACH_RCV_TIMED_OUT}. A zero timeout is legitimate. + +@item MACH_RCV_NOTIFY +The notify argument should specify a receive right for a notify port. +If receiving the reply port creates a new port right in the caller, then +the notify port is used to request a dead-name notification for the new +port right. + +@item MACH_RCV_INTERRUPT +If specified, the @code{mach_msg} call will return +@code{MACH_RCV_INTERRUPTED} if a software interrupt aborts the call. +Otherwise, the receive operation will be retried. + +@item MACH_RCV_LARGE +If the message is larger than @code{rcv_size}, then the message remains +queued instead of being destroyed. The call returns +@code{MACH_RCV_TOO_LARGE} and the actual size of the message is returned +in the @code{msgh_size} field of the message header. +@end table + +The receive operation can generate the following return codes. These +return codes imply that the call did not dequeue a message: + +@table @code +@item MACH_RCV_INVALID_NAME +The specified @code{rcv_name} was invalid. + +@item MACH_RCV_IN_SET +The specified port was a member of a port set. + +@item MACH_RCV_TIMED_OUT +The timeout interval expired. + +@item MACH_RCV_INTERRUPTED +A software interrupt occurred. + +@item MACH_RCV_PORT_DIED +The caller lost the rights specified by @code{rcv_name}. + +@item MACH_RCV_PORT_CHANGED +@code{rcv_name} specified a receive right which was moved into a port +set during the call. + +@item MACH_RCV_TOO_LARGE +When using @code{MACH_RCV_LARGE}, and the message was larger than +@code{rcv_size}. The message is left queued, and its actual size is +returned in the @code{msgh_size} field of the message buffer. +@end table + +These return codes imply that a message was dequeued and destroyed: + +@table @code +@item MACH_RCV_HEADER_ERROR +A resource shortage prevented the reception of the port rights in the +message header. + +@item MACH_RCV_INVALID_NOTIFY +When using @code{MACH_RCV_NOTIFY}, the notify argument did not denote a +valid receive right. + +@item MACH_RCV_TOO_LARGE +When not using @code{MACH_RCV_LARGE}, a message larger than +@code{rcv_size} was dequeued and destroyed. +@end table + +In these situations, when a message is dequeued and then destroyed, the +reply port and all port rights and memory in the message body are +destroyed. However, the caller receives the message's header, with all +fields correct, including the destination port but excepting the reply +port, which is @code{MACH_PORT_NULL}. + +These return codes imply that a message was received: + +@table @code +@item MACH_RCV_BODY_ERROR +A resource shortage prevented the reception of a port right or +out-of-line memory region in the message body. The message header, +including the reply port, is correct. The kernel attempts to transfer +all port rights and memory regions in the body, and only destroys those +that can't be transferred. + +@item MACH_RCV_INVALID_DATA +The specified message buffer was not writable. The calling task did +successfully receive the port rights and out-of-line memory regions in +the message. + +@item MACH_MSG_SUCCESS +A message was received. +@end table + +Resource shortages can occur after a message is dequeued, while +transferring port rights and out-of-line memory regions to the receiving +task. The @code{mach_msg} call returns @code{MACH_RCV_HEADER_ERROR} or +@code{MACH_RCV_BODY_ERROR} in this situation. These return codes always +carry extra bits (bitwise-ored) that indicate the nature of the resource +shortage: + +@table @code +@item MACH_MSG_IPC_SPACE +There was no room in the task's IPC name space for another port name. + +@item MACH_MSG_VM_SPACE +There was no room in the task's VM address space for an out-of-line +memory region. + +@item MACH_MSG_IPC_KERNEL +A kernel resource shortage prevented the reception of a port right. + +@item MACH_MSG_VM_KERNEL +A kernel resource shortage prevented the reception of an out-of-line +memory region. +@end table + +If a resource shortage prevents the reception of a port right, the port +right is destroyed and the caller sees the name @code{MACH_PORT_NULL}. +If a resource shortage prevents the reception of an out-of-line memory +region, the region is destroyed and the caller receives a zero address. +In addition, the @code{msgt_size} (@code{msgtl_size}) field in the +data's type descriptor is changed to zero. If a resource shortage +prevents the reception of out-of-line memory carrying port rights, then +the port rights are always destroyed if the memory region can not be +received. A task never receives port rights or memory regions that it +isn't told about. + + +@node Atomicity +@subsection Atomicity + +The @code{mach_msg} call handles port rights in a message header +atomically. Port rights and out-of-line memory in a message body do not +enjoy this atomicity guarantee. The message body may be processed +front-to-back, back-to-front, first out-of-line memory then port rights, +in some random order, or even atomically. + +For example, consider sending a message with the destination port +specified as @code{MACH_MSG_TYPE_MOVE_SEND} and the reply port specified +as @code{MACH_MSG_TYPE_COPY_SEND}. The same send right, with one +user-reference, is supplied for both the @code{msgh_remote_port} and +@code{msgh_local_port} fields. Because @code{mach_msg} processes the +message header atomically, this succeeds. If @code{msgh_remote_port} +were processed before @code{msgh_local_port}, then @code{mach_msg} would +return @code{MACH_SEND_INVALID_REPLY} in this situation. + +On the other hand, suppose the destination and reply port are both +specified as @code{MACH_MSG_TYPE_MOVE_SEND}, and again the same send +right with one user-reference is supplied for both. Now the send +operation fails, but because it processes the header atomically, +mach_msg can return either @code{MACH_SEND_INVALID_DEST} or +@code{MACH_SEND_INVALID_REPLY}. + +For example, consider receiving a message at the same time another +thread is deallocating the destination receive right. Suppose the reply +port field carries a send right for the destination port. If the +deallocation happens before the dequeuing, then the receiver gets +@code{MACH_RCV_PORT_DIED}. If the deallocation happens after the +receive, then the @code{msgh_local_port} and the @code{msgh_remote_port} +fields both specify the same right, which becomes a dead name when the +receive right is deallocated. If the deallocation happens between the +dequeue and the receive, then the @code{msgh_local_port} and +@code{msgh_remote_port} fields both specify @code{MACH_PORT_DEAD}. +Because the header is processed atomically, it is not possible for just +one of the two fields to hold @code{MACH_PORT_DEAD}. + +The @code{MACH_RCV_NOTIFY} option provides a more likely example. +Suppose a message carrying a send-once right reply port is received with +@code{MACH_RCV_NOTIFY} at the same time the reply port is destroyed. If +the reply port is destroyed first, then @code{msgh_remote_port} +specifies @code{MACH_PORT_DEAD} and the kernel does not generate a +dead-name notification. If the reply port is destroyed after it is +received, then @code{msgh_remote_port} specifies a dead name for which +the kernel generates a dead-name notification. It is not possible to +receive the reply port right and have it turn into a dead name before +the dead-name notification is requested; as part of the message header +the reply port is received atomically. + + +@node Port Manipulation Interface +@section Port Manipulation Interface + +This section describes the interface to create, destroy and manipulate +ports, port rights and port sets. + +@cindex IPC space port +@cindex port representing an IPC space +@deftp {Data type} ipc_space_t +This is a @code{task_t} (and as such a @code{mach_port_t}), which holds +a port name associated with a port that represents an IPC space in the +kernel. An IPC space is used by the kernel to manage the port names and +rights available to a task. The IPC space doesn't get a port name of +its own. Instead the port name of the task containing the IPC space is +used to name the IPC space of the task (as is indicated by the fact that +the type of @code{ipc_space_t} is actually @code{task_t}). + +The IPC spaces of tasks are the only ones accessible outside of +the kernel. +@end deftp + +@menu +* Port Creation:: How to create new ports and port sets. +* Port Destruction:: How to destroy ports and port sets. +* Port Names:: How to query and manipulate port names. +* Port Rights:: How to work with port rights. +* Ports and other Tasks:: How to move rights between tasks. +* Receive Rights:: How to work with receive rights. +* Port Sets:: How to work with port sets. +* Request Notifications:: How to request notifications for events. +* Inherited Ports:: How to work with the inherited system ports. +@end menu + + +@node Port Creation +@subsection Port Creation + +@deftypefun kern_return_t mach_port_allocate (@w{ipc_space_t @var{task}}, @w{mach_port_right_t @var{right}}, @w{mach_port_t *@var{name}}) +The @code{mach_port_allocate} function creates a new right in the +specified task. The new right's name is returned in @var{name}, which +may be any name that wasn't in use. + +The @var{right} argument takes the following values: + +@table @code +@item MACH_PORT_RIGHT_RECEIVE +@code{mach_port_allocate} creates a port. The new port is not a member +of any port set. It doesn't have any extant send or send-once rights. +Its make-send count is zero, its sequence number is zero, its queue +limit is @code{MACH_PORT_QLIMIT_DEFAULT}, and it has no queued messages. +@var{name} denotes the receive right for the new port. + +@var{task} does not hold send rights for the new port, only the receive +right. @code{mach_port_insert_right} and @code{mach_port_extract_right} +can be used to convert the receive right into a combined send/receive +right. + +@item MACH_PORT_RIGHT_PORT_SET +@code{mach_port_allocate} creates a port set. The new port set has no +members. + +@item MACH_PORT_RIGHT_DEAD_NAME +@code{mach_port_allocate} creates a dead name. The new dead name has +one user reference. +@end table + +The function returns @code{KERN_SUCCESS} if the call succeeded, +@code{KERN_INVALID_TASK} if @var{task} was invalid, +@code{KERN_INVALID_VALUE} if @var{right} was invalid, @code{KERN_NO_SPACE} if +there was no room in @var{task}'s IPC name space for another right and +@code{KERN_RESOURCE_SHORTAGE} if the kernel ran out of memory. + +The @code{mach_port_allocate} call is actually an RPC to @var{task}, +normally a send right for a task port, but potentially any send right. +In addition to the normal diagnostic return codes from the call's server +(normally the kernel), the call may return @code{mach_msg} return codes. +@end deftypefun + +@deftypefun mach_port_t mach_reply_port () +The @code{mach_reply_port} system call creates a reply port in the +calling task. + +@code{mach_reply_port} creates a port, giving the calling task the +receive right for the port. The call returns the name of the new +receive right. + +This is very much like creating a receive right with the +@code{mach_port_allocate} call, with two differences. First, +@code{mach_reply_port} is a system call and not an RPC (which requires a +reply port). Second, the port created by @code{mach_reply_port} may be +optimized for use as a reply port. + +The function returns @code{MACH_PORT_NULL} if a resource shortage +prevented the creation of the receive right. +@end deftypefun + +@deftypefun kern_return_t mach_port_allocate_name (@w{ipc_space_t @var{task}}, @w{mach_port_right_t @var{right}}, @w{mach_port_t @var{name}}) +The function @code{mach_port_allocate_name} creates a new right in the +specified task, with a specified name for the new right. @var{name} +must not already be in use for some right, and it can't be the reserved +values @code{MACH_PORT_NULL} and @code{MACH_PORT_DEAD}. + +The @var{right} argument takes the following values: + +@table @code +@item MACH_PORT_RIGHT_RECEIVE +@code{mach_port_allocate_name} creates a port. The new port is not a +member of any port set. It doesn't have any extant send or send-once +rights. Its make-send count is zero, its sequence number is zero, its +queue limit is @code{MACH_PORT_QLIMIT_DEFAULT}, and it has no queued +messages. @var{name} denotes the receive right for the new port. + +@var{task} does not hold send rights for the new port, only the receive +right. @code{mach_port_insert_right} and @code{mach_port_extract_right} +can be used to convert the receive right into a combined send/receive +right. + +@item MACH_PORT_RIGHT_PORT_SET +@code{mach_port_allocate_name} creates a port set. The new port set has +no members. + +@item MACH_PORT_RIGHT_DEAD_NAME +@code{mach_port_allocate_name} creates a new dead name. The new dead +name has one user reference. +@end table + +The function returns @code{KERN_SUCCESS} if the call succeeded, +@code{KERN_INVALID_TASK} if @var{task} was invalid, +@code{KERN_INVALID_VALUE} if @var{right} was invalid or @var{name} was +@code{MACH_PORT_NULL} or @code{MACH_PORT_DEAD}, @code{KERN_NAME_EXISTS} +if @var{name} was already in use for a port right and +@code{KERN_RESOURCE_SHORTAGE} if the kernel ran out of memory. + +The @code{mach_port_allocate_name} call is actually an RPC to +@var{task}, normally a send right for a task port, but potentially any +send right. In addition to the normal diagnostic return codes from the +call's server (normally the kernel), the call may return @code{mach_msg} +return codes. +@end deftypefun + + +@node Port Destruction +@subsection Port Destruction + +@deftypefun kern_return_t mach_port_deallocate (@w{ipc_space_t @var{task}}, @w{mach_port_t @var{name}}) +The function @code{mach_port_deallocate} releases a user reference for a +right in @var{task}'s IPC name space. It allows a task to release a +user reference for a send or send-once right without failing if the port +has died and the right is now actually a dead name. + +If @var{name} denotes a dead name, send right, or send-once right, then +the right loses one user reference. If it only had one user reference, +then the right is destroyed. + +The function returns @code{KERN_SUCCESS} if the call succeeded, +@code{KERN_INVALID_TASK} if @var{task} was invalid, +@code{KERN_INVALID_NAME} if @var{name} did not denote a right and +@code{KERN_INVALID_RIGHT} if @var{name} denoted an invalid right. + +The @code{mach_port_deallocate} call is actually an RPC to +@var{task}, normally a send right for a task port, but potentially any +send right. In addition to the normal diagnostic return codes from the +call's server (normally the kernel), the call may return @code{mach_msg} +return codes. +@end deftypefun + +@deftypefun kern_return_t mach_port_destroy (@w{ipc_space_t @var{task}}, @w{mach_port_t @var{name}}) +The function @code{mach_port_destroy} deallocates all rights denoted by +a name. The name becomes immediately available for reuse. + +For most purposes, @code{mach_port_mod_refs} and +@code{mach_port_deallocate} are preferable. + +If @var{name} denotes a port set, then all members of the port set are +implicitly removed from the port set. + +If @var{name} denotes a receive right that is a member of a port set, +the receive right is implicitly removed from the port set. If there is +a port-destroyed request registered for the port, then the receive right +is not actually destroyed, but instead is sent in a port-destroyed +notification to the backup port. If there is no registered +port-destroyed request, remaining messages queued to the port are +destroyed and extant send and send-once rights turn into dead names. If +those send and send-once rights have dead-name requests registered, then +dead-name notifications are generated for them. + +If @var{name} denotes a send-once right, then the send-once right is +used to produce a send-once notification for the port. + +If @var{name} denotes a send-once, send, and/or receive right, and it +has a dead-name request registered, then the registered send-once right +is used to produce a port-deleted notification for the name. + +The function returns @code{KERN_SUCCESS} if the call succeeded, +@code{KERN_INVALID_TASK} if @var{task} was invalid, +@code{KERN_INVALID_NAME} if @var{name} did not denote a right. + +The @code{mach_port_destroy} call is actually an RPC to +@var{task}, normally a send right for a task port, but potentially any +send right. In addition to the normal diagnostic return codes from the +call's server (normally the kernel), the call may return @code{mach_msg} +return codes. +@end deftypefun + + +@node Port Names +@subsection Port Names + +@deftypefun kern_return_t mach_port_names (@w{ipc_space_t @var{task}}, @w{mach_port_array_t *@var{names}}, @w{mach_msg_type_number_t *@var{ncount}}, @w{mach_port_type_array_t *@var{types}}, @w{mach_msg_type_number_t *@var{tcount}}) +The function @code{mach_port_names} returns information about +@var{task}'s port name space. For each name, it also returns what type +of rights @var{task} holds. (The same information returned by +@code{mach_port_type}.) @var{names} and @var{types} are arrays that are +automatically allocated when the reply message is received. The user +should @code{vm_deallocate} them when the data is no longer needed. + +@code{mach_port_names} will return in @var{names} the names of the +ports, port sets, and dead names in the task's port name space, in no +particular order and in @var{ncount} the number of names returned. It +will return in @var{types} the type of each corresponding name, which +indicates what kind of rights the task holds with that name. +@var{tcount} should be the same as @var{ncount}. + +The function returns @code{KERN_SUCCESS} if the call succeeded, +@code{KERN_INVALID_TASK} if @var{task} was invalid, +@code{KERN_RESOURCE_SHORTAGE} if the kernel ran out of memory. + +The @code{mach_port_names} call is actually an RPC to @var{task}, +normally a send right for a task port, but potentially any send right. +In addition to the normal diagnostic return codes from the call's server +(normally the kernel), the call may return @code{mach_msg} return codes. +@end deftypefun + +@deftypefun kern_return_t mach_port_type (@w{ipc_space_t @var{task}}, @w{mach_port_t @var{name}}, @w{mach_port_type_t *@var{ptype}}) +The function @code{mach_port_type} returns information about +@var{task}'s rights for a specific name in its port name space. The +returned @var{ptype} is a bitmask indicating what rights @var{task} +holds for the port, port set or dead name. The bitmask is composed of +the following bits: + +@table @code +@item MACH_PORT_TYPE_SEND +The name denotes a send right. + +@item MACH_PORT_TYPE_RECEIVE +The name denotes a receive right. + +@item MACH_PORT_TYPE_SEND_ONCE +The name denotes a send-once right. + +@item MACH_PORT_TYPE_PORT_SET +The name denotes a port set. + +@item MACH_PORT_TYPE_DEAD_NAME +The name is a dead name. + +@item MACH_PORT_TYPE_DNREQUEST +A dead-name request has been registered for the right. + +@item MACH_PORT_TYPE_MAREQUEST +A msg-accepted request for the right is pending. + +@item MACH_PORT_TYPE_COMPAT +The port right was created in the compatibility mode. +@end table + +The function returns @code{KERN_SUCCESS} if the call succeeded, +@code{KERN_INVALID_TASK} if @var{task} was invalid and +@code{KERN_INVALID_NAME} if @var{name} did not denote a right. + +The @code{mach_port_type} call is actually an RPC to @var{task}, +normally a send right for a task port, but potentially any send right. +In addition to the normal diagnostic return codes from the call's server +(normally the kernel), the call may return @code{mach_msg} return codes. +@end deftypefun + +@deftypefun kern_return_t mach_port_rename (@w{ipc_space_t @var{task}}, @w{mach_port_t @var{old_name}}, @w{mach_port_t @var{new_name}}) +The function @code{mach_port_rename} changes the name by which a port, +port set, or dead name is known to @var{task}. @var{old_name} is the +original name and @var{new_name} the new name for the port right. +@var{new_name} must not already be in use, and it can't be the +distinguished values @code{MACH_PORT_NULL} and @code{MACH_PORT_DEAD}. + +The function returns @code{KERN_SUCCESS} if the call succeeded, +@code{KERN_INVALID_TASK} if @var{task} was invalid, +@code{KERN_INVALID_NAME} if @var{old_name} did not denote a right, +@code{KERN_INVALID_VALUE} if @var{new_name} was @code{MACH_PORT_NULL} or +@code{MACH_PORT_DEAD}, @code{KERN_NAME_EXISTS} if @code{new_name} +already denoted a right and @code{KERN_RESOURCE_SHORTAGE} if the kernel +ran out of memory. + +The @code{mach_port_rename} call is actually an RPC to @var{task}, +normally a send right for a task port, but potentially any send right. +In addition to the normal diagnostic return codes from the call's server +(normally the kernel), the call may return @code{mach_msg} return codes. +@end deftypefun + + +@node Port Rights +@subsection Port Rights + +@deftypefun kern_return_t mach_port_get_refs (@w{ipc_space_t @var{task}}, @w{mach_port_t @var{name}}, @w{mach_port_right_t @var{right}}, @w{mach_port_urefs_t *@var{refs}}) +The function @code{mach_port_get_refs} returns the number of user +references a task has for a right. + +The @var{right} argument takes the following values: +@itemize @bullet +@item @code{MACH_PORT_RIGHT_SEND} +@item @code{MACH_PORT_RIGHT_RECEIVE} +@item @code{MACH_PORT_RIGHT_SEND_ONCE} +@item @code{MACH_PORT_RIGHT_PORT_SET} +@item @code{MACH_PORT_RIGHT_DEAD_NAME} +@end itemize + +If @var{name} denotes a right, but not the type of right specified, then +zero is returned. Otherwise a positive number of user references is +returned. Note that a name may simultaneously denote send and receive +rights. + +The function returns @code{KERN_SUCCESS} if the call succeeded, +@code{KERN_INVALID_TASK} if @var{task} was invalid, +@code{KERN_INVALID_VALUE} if @var{right} was invalid and +@code{KERN_INVALID_NAME} if @var{name} did not denote a right. + +The @code{mach_port_get_refs} call is actually an RPC to @var{task}, +normally a send right for a task port, but potentially any send right. +In addition to the normal diagnostic return codes from the call's server +(normally the kernel), the call may return @code{mach_msg} return codes. +@end deftypefun + +@deftypefun kern_return_t mach_port_mod_refs (@w{ipc_space_t @var{task}}, @w{mach_port_t @var{name}}, @w{mach_port_right_t @var{right}}, @w{mach_port_delta_t @var{delta}}) +The function @code{mach_port_mod_refs} requests that the number of user +references a task has for a right be changed. This results in the right +being destroyed, if the number of user references is changed to zero. +The task holding the right is @var{task}, @var{name} should denote the +specified right. @var{right} denotes the type of right being modified. +@var{delta} is the signed change to the number of user references. + +The @var{right} argument takes the following values: +@itemize @bullet +@item @code{MACH_PORT_RIGHT_SEND} +@item @code{MACH_PORT_RIGHT_RECEIVE} +@item @code{MACH_PORT_RIGHT_SEND_ONCE} +@item @code{MACH_PORT_RIGHT_PORT_SET} +@item @code{MACH_PORT_RIGHT_DEAD_NAME} +@end itemize + +The number of user references for the right is changed by the amount +@var{delta}, subject to the following restrictions: port sets, receive +rights, and send-once rights may only have one user reference. The +resulting number of user references can't be negative. If the resulting +number of user references is zero, the effect is to deallocate the +right. For dead names and send rights, there is an +implementation-defined maximum number of user references. + +If the call destroys the right, then the effect is as described for +@code{mach_port_destroy}, with the exception that +@code{mach_port_destroy} simultaneously destroys all the rights denoted +by a name, while @code{mach_port_mod_refs} can only destroy one right. +The name will be available for reuse if it only denoted the one right. + +The function returns @code{KERN_SUCCESS} if the call succeeded, +@code{KERN_INVALID_TASK} if @var{task} was invalid, +@code{KERN_INVALID_VALUE} if @var{right} was invalid or the +user-reference count would become negative, @code{KERN_INVALID_NAME} if +@var{name} did not denote a right, @code{KERN_INVALID_RIGHT} if +@var{name} denoted a right, but not the specified right and +@code{KERN_UREFS_OVERFLOW} if the user-reference count would overflow. + +The @code{mach_port_mod_refs} call is actually an RPC to @var{task}, +normally a send right for a task port, but potentially any send right. +In addition to the normal diagnostic return codes from the call's server +(normally the kernel), the call may return @code{mach_msg} return codes. +@end deftypefun + + +@node Ports and other Tasks +@subsection Ports and other Tasks + +@deftypefun kern_return_t mach_port_insert_right (@w{ipc_space_t @var{task}}, @w{mach_port_t @var{name}}, @w{mach_port_t @var{right}}, @w{mach_msg_type_name_t @var{right_type}}) +The function @var{mach_port_insert_right} inserts into @var{task} the +caller's right for a port, using a specified name for the right in the +target task. + +The specified @var{name} can't be one of the reserved values +@code{MACH_PORT_NULL} or @code{MACH_PORT_DEAD}. The @var{right} can't +be @code{MACH_PORT_NULL} or @code{MACH_PORT_DEAD}. + +The argument @var{right_type} specifies a right to be inserted and how +that right should be extracted from the caller. It should be a value +appropriate for @var{msgt_name}; see @code{mach_msg}. @c XXX cross ref + +If @var{right_type} is @code{MACH_MSG_TYPE_MAKE_SEND}, +@code{MACH_MSG_TYPE_MOVE_SEND}, or @code{MACH_MSG_TYPE_COPY_SEND}, then +a send right is inserted. If the target already holds send or receive +rights for the port, then @var{name} should denote those rights in the +target. Otherwise, @var{name} should be unused in the target. If the +target already has send rights, then those send rights gain an +additional user reference. Otherwise, the target gains a send right, +with a user reference count of one. + +If @var{right_type} is @code{MACH_MSG_TYPE_MAKE_SEND_ONCE} or +@code{MACH_MSG_TYPE_MOVE_SEND_ONCE}, then a send-once right is inserted. +The name should be unused in the target. The target gains a send-once +right. + +If @var{right_type} is @code{MACH_MSG_TYPE_MOVE_RECEIVE}, then a receive +right is inserted. If the target already holds send rights for the +port, then name should denote those rights in the target. Otherwise, +name should be unused in the target. The receive right is moved into +the target task. + +The function returns @code{KERN_SUCCESS} if the call succeeded, +@code{KERN_INVALID_TASK} if @var{task} was invalid, +@code{KERN_INVALID_VALUE} if @var{right} was not a port right or +@var{name} was @code{MACH_PORT_NULL} or @code{MACH_PORT_DEAD}, +@code{KERN_NAME_EXISTS} if @var{name} already denoted a right, +@code{KERN_INVALID_CAPABILITY} if @var{right} was @code{MACH_PORT_NULL} +or @code{MACH_PORT_DEAD} @code{KERN_RIGHT_EXISTS} if @var{task} already +had rights for the port, with a different name, +@code{KERN_UREFS_OVERFLOW} if the user-reference count would overflow +and @code{KERN_RESOURCE_SHORTAGE} if the kernel ran out of memory. + +The @code{mach_port_insert_right} call is actually an RPC to @var{task}, +normally a send right for a task port, but potentially any send right. +In addition to the normal diagnostic return codes from the call's server +(normally the kernel), the call may return @code{mach_msg} return codes. +@end deftypefun + +@deftypefun kern_return_t mach_port_extract_right (@w{ipc_space_t @var{task}}, @w{mach_port_t @var{name}}, @w{mach_msg_type_name_t @var{desired_type}}, @w{mach_port_t *@var{right}}, @w{mach_msg_type_name_t *@var{acquired_type}}) +The function @var{mach_port_extract_right} extracts a port right from +the target @var{task} and returns it to the caller as if the task sent +the right voluntarily, using @var{desired_type} as the value of +@var{msgt_name}. @xref{Mach Message Call}. + +The returned value of @var{acquired_type} will be +@code{MACH_MSG_TYPE_PORT_SEND} if a send right is extracted, +@code{MACH_MSG_TYPE_PORT_RECEIVE} if a receive right is extracted, and +@code{MACH_MSG_TYPE_PORT_SEND_ONCE} if a send-once right is extracted. + +The function returns @code{KERN_SUCCESS} if the call succeeded, +@code{KERN_INVALID_TASK} if @var{task} was invalid, +@code{KERN_INVALID_NAME} if @var{name} did not denote a right, +@code{KERN_INVALID_RIGHT} if @var{name} denoted a right, but an invalid one, +@code{KERN_INVALID_VALUE} if @var{desired_type} was invalid. + +The @code{mach_port_extract_right} call is actually an RPC to +@var{task}, normally a send right for a task port, but potentially any +send right. In addition to the normal diagnostic return codes from the +call's server (normally the kernel), the call may return @code{mach_msg} +return codes. +@end deftypefun + + +@node Receive Rights +@subsection Receive Rights + +@deftp {Data type} mach_port_seqno_t +The @code{mach_port_seqno_t} data type is an @code{unsigned int} which +contains the sequence number of a port. +@end deftp + +@deftp {Data type} mach_port_mscount_t +The @code{mach_port_mscount_t} data type is an @code{unsigned int} which +contains the make-send count for a port. +@end deftp + +@deftp {Data type} mach_port_msgcount_t +The @code{mach_port_msgcount_t} data type is an @code{unsigned int} which +contains a number of messages. +@end deftp + +@deftp {Data type} mach_port_rights_t +The @code{mach_port_rights_t} data type is an @code{unsigned int} which +contains a number of rights for a port. +@end deftp + +@deftp {Data type} mach_port_status_t +This structure contains some status information about a port, which can +be queried with @code{mach_port_get_receive_status}. It has the following +members: + +@table @code +@item mach_port_t mps_pset +The containing port set. + +@item mach_port_seqno_t mps_seqno +The sequence number. + +@item mach_port_mscount_t mps_mscount +The make-send count. + +@item mach_port_msgcount_t mps_qlimit +The maximum number of messages in the queue. + +@item mach_port_msgcount_t mps_msgcount +The current number of messages in the queue. + +@item mach_port_rights_t mps_sorights +The number of send-once rights that exist. + +@item boolean_t mps_srights +@code{TRUE} if send rights exist. + +@item boolean_t mps_pdrequest +@code{TRUE} if port-deleted notification is requested. + +@item boolean_t mps_nsrequest +@code{TRUE} if no-senders notification is requested. +@end table +@end deftp + +@deftypefun kern_return_t mach_port_get_receive_status (@w{ipc_space_t @var{task}}, @w{mach_port_t @var{name}}, @w{mach_port_status_t *@var{status}}) +The function @code{mach_port_get_receive_status} returns the current +status of the specified receive right. + +The function returns @code{KERN_SUCCESS} if the call succeeded, +@code{KERN_INVALID_TASK} if @var{task} was invalid, +@code{KERN_INVALID_NAME} if @var{name} did not denote a right and +@code{KERN_INVALID_RIGHT} if @var{name} denoted a right, but not a +receive right. + +The @code{mach_port_get_receive_status} call is actually an RPC to @var{task}, +normally a send right for a task port, but potentially any send right. +In addition to the normal diagnostic return codes from the call's server +(normally the kernel), the call may return @code{mach_msg} return codes. +@end deftypefun + +@deftypefun kern_return_t mach_port_set_mscount (@w{ipc_space_t @var{task}}, @w{mach_port_t @var{name}}, @w{mach_port_mscount_t @var{mscount}}) +The function @code{mach_port_set_mscount} changes the make-send count of +@var{task}'s receive right named @var{name} to @var{mscount}. All +values for @var{mscount} are valid. + +The function returns @code{KERN_SUCCESS} if the call succeeded, +@code{KERN_INVALID_TASK} if @var{task} was invalid, +@code{KERN_INVALID_NAME} if @var{name} did not denote a right and +@code{KERN_INVALID_RIGHT} if @var{name} denoted a right, but not a +receive right. + +The @code{mach_port_set_mscount} call is actually an RPC to @var{task}, +normally a send right for a task port, but potentially any send right. +In addition to the normal diagnostic return codes from the call's server +(normally the kernel), the call may return @code{mach_msg} return codes. +@end deftypefun + +@deftypefun kern_return_t mach_port_set_qlimit (@w{ipc_space_t @var{task}}, @w{mach_port_t @var{name}}, @w{mach_port_msgcount_t @var{qlimit}}) +The function @code{mach_port_set_qlimit} changes the queue limit +@var{task}'s receive right named @var{name} to @var{qlimit}. Valid +values for @var{qlimit} are between zero and +@code{MACH_PORT_QLIMIT_MAX}, inclusive. + +The function returns @code{KERN_SUCCESS} if the call succeeded, +@code{KERN_INVALID_TASK} if @var{task} was invalid, +@code{KERN_INVALID_NAME} if @var{name} did not denote a right, +@code{KERN_INVALID_RIGHT} if @var{name} denoted a right, but not a +receive right and @code{KERN_INVALID_VALUE} if @var{qlimit} was invalid. + +The @code{mach_port_set_qlimit} call is actually an RPC to @var{task}, +normally a send right for a task port, but potentially any send right. +In addition to the normal diagnostic return codes from the call's server +(normally the kernel), the call may return @code{mach_msg} return codes. +@end deftypefun + +@deftypefun kern_return_t mach_port_set_seqno (@w{ipc_space_t @var{task}}, @w{mach_port_t @var{name}}, @w{mach_port_seqno_t @var{seqno}}) +The function @code{mach_port_set_seqno} changes the sequence number +@var{task}'s receive right named @var{name} to @var{seqno}. All +sequence number values are valid. The next message received from the +port will be stamped with the specified sequence number. + +The function returns @code{KERN_SUCCESS} if the call succeeded, +@code{KERN_INVALID_TASK} if @var{task} was invalid, +@code{KERN_INVALID_NAME} if @var{name} did not denote a right and +@code{KERN_INVALID_RIGHT} if @var{name} denoted a right, but not a +receive right. + +The @code{mach_port_set_seqno} call is actually an RPC to @var{task}, +normally a send right for a task port, but potentially any send right. +In addition to the normal diagnostic return codes from the call's server +(normally the kernel), the call may return @code{mach_msg} return codes. +@end deftypefun + +@deftypefun kern_return_t mach_port_set_protected_payload (@w{ipc_space_t @var{task}}, @w{mach_port_t @var{name}}, @w{unsigned long @var{payload}}) +The function @code{mach_port_set_protected_payload} sets the protected +payload associated with the right @var{name} to @var{payload}. +Section @ref{Message Receive} describes how setting a protected +payload affects the messages delivered to @var{name}. + +The function returns @code{KERN_SUCCESS} if the call succeeded, +@code{KERN_INVALID_TASK} if @var{task} was invalid, +@code{KERN_INVALID_NAME} if @var{name} did not denote a right and +@code{KERN_INVALID_RIGHT} if @var{name} denoted a right, but not a +receive right. + +The @code{mach_port_set_protected_payload} call is actually an RPC to +@var{task}, normally a send right for a task port, but potentially any +send right. In addition to the normal diagnostic return codes from +the call's server (normally the kernel), the call may return +@code{mach_msg} return codes. +@end deftypefun + +@deftypefun kern_return_t mach_port_clear_protected_payload (@w{ipc_space_t @var{task}}, @w{mach_port_t @var{name}}, @w{unsigned long @var{payload}}) +The function @code{mach_port_clear_protected_payload} clears the +protected payload associated with the right @var{name}. + +The function returns @code{KERN_SUCCESS} if the call succeeded, +@code{KERN_INVALID_TASK} if @var{task} was invalid, +@code{KERN_INVALID_NAME} if @var{name} did not denote a right and +@code{KERN_INVALID_RIGHT} if @var{name} denoted a right, but not a +receive right. + +The @code{mach_port_clear_protected_payload} call is actually an RPC +to @var{task}, normally a send right for a task port, but potentially +any send right. In addition to the normal diagnostic return codes +from the call's server (normally the kernel), the call may return +@code{mach_msg} return codes. +@end deftypefun + +@node Port Sets +@subsection Port Sets + +@deftypefun kern_return_t mach_port_get_set_status (@w{ipc_space_t @var{task}}, @w{mach_port_t @var{name}}, @w{mach_port_array_t *@var{members}}, @w{mach_msg_type_number_t *@var{count}}) +The function @code{mach_port_get_set_status} returns the members of a +port set. @var{members} is an array that is automatically allocated +when the reply message is received. The user should +@code{vm_deallocate} it when the data is no longer needed. + +The function returns @code{KERN_SUCCESS} if the call succeeded, +@code{KERN_INVALID_TASK} if @var{task} was invalid, +@code{KERN_INVALID_NAME} if @var{name} did not denote a right, +@code{KERN_INVALID_RIGHT} if @var{name} denoted a right, but not a +receive right and @code{KERN_RESOURCE_SHORTAGE} if the kernel ran out of +memory. + +The @code{mach_port_get_set_status} call is actually an RPC to +@var{task}, normally a send right for a task port, but potentially any +send right. In addition to the normal diagnostic return codes from the +call's server (normally the kernel), the call may return @code{mach_msg} +return codes. +@end deftypefun + +@deftypefun kern_return_t mach_port_move_member (@w{ipc_space_t @var{task}}, @w{mach_port_t @var{member}}, @w{mach_port_t @var{after}}) +The function @var{mach_port_move_member} moves the receive right +@var{member} into the port set @var{after}. If the receive right is +already a member of another port set, it is removed from that set first +(the whole operation is atomic). If the port set is +@code{MACH_PORT_NULL}, then the receive right is not put into a port +set, but removed from its current port set. + +The function returns @code{KERN_SUCCESS} if the call succeeded, +@code{KERN_INVALID_TASK} if @var{task} was invalid, +@code{KERN_INVALID_NAME} if @var{member} or @var{after} did not denote a +right, @code{KERN_INVALID_RIGHT} if @var{member} denoted a right, but +not a receive right or @var{after} denoted a right, but not a port set, +and @code{KERN_NOT_IN_SET} if @var{after} was @code{MACH_PORT_NULL}, but +@code{member} wasn't currently in a port set. + +The @code{mach_port_move_member} call is actually an RPC to @var{task}, +normally a send right for a task port, but potentially any send right. +In addition to the normal diagnostic return codes from the call's server +(normally the kernel), the call may return @code{mach_msg} return codes. +@end deftypefun + + +@node Request Notifications +@subsection Request Notifications + +@deftypefun kern_return_t mach_port_request_notification (@w{ipc_space_t @var{task}}, @w{mach_port_t @var{name}}, @w{mach_msg_id_t @var{variant}}, @w{mach_port_mscount_t @var{sync}}, @w{mach_port_t @var{notify}}, @w{mach_msg_type_name_t @var{notify_type}}, @w{mach_port_t *@var{previous}}) +The function @code{mach_port_request_notification} registers a request +for a notification and supplies the send-once right @var{notify} to +which the notification will be sent. The @var{notify_type} denotes the +IPC type for the send-once right, which can be +@code{MACH_MSG_TYPE_MAKE_SEND_ONCE} or +@code{MACH_MSG_TYPE_MOVE_SEND_ONCE}. It is an atomic swap, returning +the previously registered send-once right (or @code{MACH_PORT_NULL} for +none) in @var{previous}. A previous notification request may be +cancelled by providing @code{MACH_PORT_NULL} for @var{notify}. + +The @var{variant} argument takes the following values: + +@table @code +@item MACH_NOTIFY_PORT_DESTROYED +@var{sync} must be zero. The @var{name} must specify a receive right, +and the call requests a port-destroyed notification for the receive +right. If the receive right were to have been destroyed, say by +@code{mach_port_destroy}, then instead the receive right will be sent in +a port-destroyed notification to the registered send-once right. + +@item MACH_NOTIFY_DEAD_NAME +The call requests a dead-name notification. @var{name} specifies send, +receive, or send-once rights for a port. If the port is destroyed (and +the right remains, becoming a dead name), then a dead-name notification +which carries the name of the right will be sent to the registered +send-once right. If @var{notify} is not null and sync is non-zero, the +name may specify a dead name, and a dead-name notification is +immediately generated. + +Whenever a dead-name notification is generated, the user reference count +of the dead name is incremented. For example, a send right with two +user refs has a registered dead-name request. If the port is destroyed, +the send right turns into a dead name with three user refs (instead of +two), and a dead-name notification is generated. + +If the name is made available for reuse, perhaps because of +@code{mach_port_destroy} or @code{mach_port_mod_refs}, or the name +denotes a send-once right which has a message sent to it, then the +registered send-once right is used to generate a port-deleted +notification. + +@item MACH_NOTIFY_NO_SENDERS +The call requests a no-senders notification. @var{name} must specify a +receive right. If @var{notify} is not null, and the receive right's +make-send count is greater than or equal to the sync value, and it has +no extant send rights, than an immediate no-senders notification is +generated. Otherwise the notification is generated when the receive +right next loses its last extant send right. In either case, any +previously registered send-once right is returned. + +The no-senders notification carries the value the port's make-send count +had when it was generated. The make-send count is incremented whenever +@code{MACH_MSG_TYPE_MAKE_SEND} is used to create a new send right from +the receive right. The make-send count is reset to zero when the +receive right is carried in a message. +@end table + +The function returns @code{KERN_SUCCESS} if the call succeeded, +@code{KERN_INVALID_TASK} if @var{task} was invalid, +@code{KERN_INVALID_VALUE} if @var{variant} was invalid, +@code{KERN_INVALID_NAME} if @var{name} did not denote a right, +@code{KERN_INVALID_RIGHT} if @var{name} denoted an invalid right and +@code{KERN_INVALID_CAPABILITY} if @var{notify} was invalid. + +When using @code{MACH_NOTIFY_PORT_DESTROYED}, the function returns +@code{KERN_INVALID_VALUE} if @var{sync} wasn't zero. + +When using @code{MACH_NOTIFY_DEAD_NAME}, the function returns +@code{KERN_RESOURCE_SHORTAGE} if the kernel ran out of memory, +@code{KERN_INVALID_ARGUMENT} if @var{name} denotes a dead name, but +@var{sync} is zero or @var{notify} is @code{MACH_PORT_NULL}, and +@code{KERN_UREFS_OVERFLOW} if @var{name} denotes a dead name, but +generating an immediate dead-name notification would overflow the name's +user-reference count. + +The @code{mach_port_request_notification} call is actually an RPC to +@var{task}, normally a send right for a task port, but potentially any +send right. In addition to the normal diagnostic return codes from the +call's server (normally the kernel), the call may return @code{mach_msg} +return codes. +@end deftypefun + +@node Inherited Ports +@subsection Inherited Ports + +The inherited ports concept is not used in the Hurd, and so the _SLOT +macros are not defined in GNU Mach. + +The following section documents how @code{mach_ports_register} and +@code{mach_ports_lookup} were originally intended to be used. + +@deftypefun kern_return_t mach_ports_register (@w{task_t @var{target_task}}, @w{port_array_t @var{init_port_set}}, @w{int @var{init_port_array_count}}) +@deftypefunx kern_return_t mach_ports_lookup (@w{task_t @var{target_task}}, @w{port_array_t *@var{init_port_set}}, @w{int *@var{init_port_array_count}}) +@code{mach_ports_register} manipulates the inherited ports array, +@code{mach_ports_lookup} is used to acquire specific parent ports. +@var{target_task} is the task to be affected. @var{init_port_set} is an +array of system ports to be registered, or returned. Although the array +size is given as variable, the kernel will only accept a limited number +of ports. @var{init_port_array_count} is the number of ports returned +in @var{init_port_set}. + +@code{mach_ports_register} registers an array of well-known system ports +with the kernel on behalf of a specific task. Currently the ports to be +registered are: the port to the Network Name Server, the port to the +Environment Manager, and a port to the Service server. These port +values must be placed in specific slots in the init_port_set. The slot +numbers are given by the global constants defined in @file{mach_init.h}: +@code{NAME_SERVER_SLOT}, @code{ENVIRONMENT_SLOT}, and +@code{SERVICE_SLOT}. These ports may later be retrieved with +@code{mach_ports_lookup}. + +When a new task is created (see @code{task_create}), the child task will +be given access to these ports. Only port send rights may be +registered. Furthermore, the number of ports which may be registered is +fixed and given by the global constant @code{MACH_PORT_SLOTS_USED} +Attempts to register too many ports will fail. + +It is intended that this mechanism be used only for task initialization, +and then only by runtime support modules. A parent task has three +choices in passing these system ports to a child task. Most commonly it +can do nothing and its child will inherit access to the same +@var{init_port_set} that the parent has; or a parent task may register a +set of ports it wishes to have passed to all of its children by calling +@code{mach_ports_register} using its task port; or it may make necessary +modifications to the set of ports it wishes its child to see, and then +register those ports using the child's task port prior to starting the +child's thread(s). The @code{mach_ports_lookup} call which is done by +@code{mach_init} in the child task will acquire these initial ports for +the child. + +Tasks other than the Network Name Server and the Environment Manager +should not need access to the Service port. The Network Name Server port +is the same for all tasks on a given machine. The Environment port is +the only port likely to have different values for different tasks. + +Since the number of ports which may be registered is limited, ports +other than those used by the runtime system to initialize a task should +be passed to children either through an initial message, or through the +Network Name Server for public ports, or the Environment Manager for +private ports. + +The function returns @code{KERN_SUCCESS} if the memory was allocated, +and @code{KERN_INVALID_ARGUMENT} if an attempt was made to register more +ports than the current kernel implementation allows. +@end deftypefun + + +@node Virtual Memory Interface +@chapter Virtual Memory Interface + +@cindex virtual memory map port +@cindex port representing a virtual memory map +@deftp {Data type} vm_task_t +This is a @code{task_t} (and as such a @code{mach_port_t}), which holds +a port name associated with a port that represents a virtual memory map +in the kernel. An virtual memory map is used by the kernel to manage +the address space of a task. The virtual memory map doesn't get a port +name of its own. Instead the port name of the task provided with the +virtual memory is used to name the virtual memory map of the task (as is +indicated by the fact that the type of @code{vm_task_t} is actually +@code{task_t}). + +The virtual memory maps of tasks are the only ones accessible outside of +the kernel. +@end deftp + +@menu +* Memory Allocation:: Allocation of new virtual memory. +* Memory Deallocation:: Freeing unused virtual memory. +* Data Transfer:: Reading, writing and copying memory. +* Memory Attributes:: Tweaking memory regions. +* Mapping Memory Objects:: How to map memory objects. +* Memory Statistics:: How to get statistics about memory usage. +* Memory physical addresses:: How to get physical addresses of memory. +@end menu + +@node Memory Allocation +@section Memory Allocation + +@deftypefun kern_return_t vm_allocate (@w{vm_task_t @var{target_task}}, @w{vm_address_t *@var{address}}, @w{vm_size_t @var{size}}, @w{boolean_t @var{anywhere}}) +The function @code{vm_allocate} allocates a region of virtual memory, +placing it in the specified @var{task}'s address space. + +The starting address is @var{address}. If the @var{anywhere} option is +false, an attempt is made to allocate virtual memory starting at this +virtual address. If this address is not at the beginning of a virtual +page, it will be rounded down to one. If there is not enough space at +this address, no memory will be allocated. If the @var{anywhere} option +is true, the input value of this address will be ignored, and the space +will be allocated wherever it is available. In either case, the address +at which memory was actually allocated will be returned in +@var{address}. + +@var{size} is the number of bytes to allocate (rounded by the system in +a machine dependent way to an integral number of virtual pages). + +If @var{anywhere} is true, the kernel should find and allocate any +region of the specified size, and return the address of the resulting +region in address address, rounded to a virtual page boundary if there +is sufficient space. + +The physical memory is not actually allocated until the new virtual +memory is referenced. By default, the kernel rounds all addresses down +to the nearest page boundary and all memory sizes up to the nearest page +size. The global variable @code{vm_page_size} contains the page size. +@code{mach_task_self} returns the value of the current task port which +should be used as the @var{target_task} argument in order to allocate +memory in the caller's address space. For languages other than C, these +values can be obtained by the calls @code{vm_statistics} and +@code{mach_task_self}. Initially, the pages of allocated memory will be +protected to allow all forms of access, and will be inherited in child +tasks as a copy. Subsequent calls to @code{vm_protect} and +@code{vm_inherit} may be used to change these properties. The allocated +region is always zero-filled. + +The function returns @code{KERN_SUCCESS} if the memory was successfully +allocated, @code{KERN_INVALID_ADDRESS} if an invalid address was +specified and @code{KERN_NO_SPACE} if there was not enough space left to +satisfy the request. +@end deftypefun + +@deftypefun kern_return_t vm_allocate_contiguous (@w{host_priv_t @var{host_priv}}, @w{vm_task_t @var{target_task}}, @w{vm_address_t *@var{vaddr}}, @w{phys_addr_t *@var{paddr}}, @w{vm_size_t @var{size}}, @w{phys_addr_t @var{pmin}}, @w{phys_addr_t @var{pmax}}, @w{phys_addr_t @var{palign}}) +The function @code{vm_allocate} allocates a region of physical memory, +placing virtual mapping of the physical pages in the specified @var{task}'s +address space. + +The virtual space will be allocated wherever it is available. The virtual +address at which the physical memory was mapped will be returned in +@var{vaddr}. The physical address of the start of the allocated physical +memory will be returned in @var{paddr}. + +@var{size} is the number of bytes to allocate (rounded by the system in +a machine dependent way to an integral number of virtual pages). + +Constraints can be set on the physical address, to cope with hardware physical +memory access constraints, e.g. DMAs. @var{pmin} is the minimum physical address +at which the allocated memory should start. @var{pmax} is the maximum physical +address at which the allocated memory should end. @var{palign} is the alignment +restriction, which has to be a power of two. + +The function returns @code{KERN_SUCCESS} if the memory was successfully +allocated, @code{KERN_RESOURCE_SHORTAGE} if there was not enough physical memory +left to satisfy the request, and @code{KERN_NO_SPACE} if there was not enough +virtual space left to satisfy the request. +@end deftypefun + +@node Memory Deallocation +@section Memory Deallocation + +@deftypefun kern_return_t vm_deallocate (@w{vm_task_t @var{target_task}}, @w{vm_address_t @var{address}}, @w{vm_size_t @var{size}}) +@code{vm_deallocate} relinquishes access to a region of a @var{task}'s +address space, causing further access to that memory to fail. This +address range will be available for reallocation. @var{address} is the +starting address, which will be rounded down to a page boundary. +@var{size} is the number of bytes to deallocate, which will be rounded +up to give a page boundary. Note, that because of the rounding to +virtual page boundaries, more than @var{size} bytes may be deallocated. +Use @code{vm_page_size} or @code{vm_statistics} to find out the current +virtual page size. + +This call may be used to deallocate memory that was passed to a task in a +message (via out of line data). In that case, the rounding should cause +no trouble, since the region of memory was allocated as a set of pages. + +The @code{vm_deallocate} call affects only the task specified by the +@var{target_task}. Other tasks which may have access to this memory may +continue to reference it. + +The function returns @code{KERN_SUCCESS} if the memory was successfully +deallocated and @code{KERN_INVALID_ADDRESS} if an invalid or +non-allocated address was specified. +@end deftypefun + + +@node Data Transfer +@section Data Transfer + +@deftypefun kern_return_t vm_read (@w{vm_task_t @var{target_task}}, @w{vm_address_t @var{address}}, @w{vm_size_t @var{size}}, @w{vm_offset_t *@var{data}}, @w{mach_msg_type_number_t *@var{data_count}}) +The function @code{vm_read} allows one task's virtual memory to be read +by another task. The @var{target_task} is the task whose memory is to +be read. @var{address} is the first address to be read and must be on a +page boundary. @var{size} is the number of bytes of data to be read and +must be an integral number of pages. @var{data} is the array of data +copied from the given task, and @var{data_count} is the size of the data +array in bytes (will be an integral number of pages). + +Note that the data array is returned in a newly allocated region; the +task reading the data should @code{vm_deallocate} this region when it is +done with the data. + +The function returns @code{KERN_SUCCESS} if the memory was successfully +read, @code{KERN_INVALID_ADDRESS} if an invalid or non-allocated address +was specified or there was not @var{size} bytes of data following the +address, @code{KERN_INVALID_ARGUMENT} if the address does not start on a +page boundary or the size is not an integral number of pages, +@code{KERN_PROTECTION_FAILURE} if the address region in the target task +is protected against reading and @code{KERN_NO_SPACE} if there was not +enough room in the callers virtual memory to allocate space for the data +to be returned. +@end deftypefun + +@deftypefun kern_return_t vm_write (@w{vm_task_t @var{target_task}}, @w{vm_address_t @var{address}}, @w{vm_offset_t @var{data}}, @w{mach_msg_type_number_t @var{data_count}}) +The function @code{vm_write} allows a task to write to the virtual memory +of @var{target_task}. @var{address} is the starting address in task to +be affected. @var{data} is an array of bytes to be written, and +@var{data_count} the size of the @var{data} array. + +The current implementation requires that @var{address}, @var{data} and +@var{data_count} all be page-aligned. Otherwise, +@code{KERN_INVALID_ARGUMENT} is returned. + +The function returns @code{KERN_SUCCESS} if the memory was successfully +written, @code{KERN_INVALID_ADDRESS} if an invalid or non-allocated +address was specified or there was not @var{data_count} bytes of +allocated memory starting at @var{address} and +@code{KERN_PROTECTION_FAILURE} if the address region in the target task +is protected against writing. +@end deftypefun + +@deftypefun kern_return_t vm_copy (@w{vm_task_t @var{target_task}}, @w{vm_address_t @var{source_address}}, @w{vm_size_t @var{count}}, @w{vm_offset_t @var{dest_address}}) +The function @code{vm_copy} causes the source memory range to be copied +to the destination address. The source and destination memory ranges +may overlap. The destination address range must already be allocated +and writable; the source range must be readable. + +@code{vm_copy} is equivalent to @code{vm_read} followed by +@code{vm_write}. + +The current implementation requires that @var{address}, @var{data} and +@var{data_count} all be page-aligned. Otherwise, +@code{KERN_INVALID_ARGUMENT} is returned. + +The function returns @code{KERN_SUCCESS} if the memory was successfully +written, @code{KERN_INVALID_ADDRESS} if an invalid or non-allocated +address was specified or there was insufficient memory allocated at one +of the addresses and @code{KERN_PROTECTION_FAILURE} if the destination +region was not writable or the source region was not readable. +@end deftypefun + + +@node Memory Attributes +@section Memory Attributes + +@deftypefun kern_return_t vm_region (@w{vm_task_t @var{target_task}}, @w{vm_address_t *@var{address}}, @w{vm_size_t *@var{size}}, @w{vm_prot_t *@var{protection}}, @w{vm_prot_t *@var{max_protection}}, @w{vm_inherit_t *@var{inheritance}}, @w{boolean_t *@var{shared}}, @w{memory_object_name_t *@var{object_name}}, @w{vm_offset_t *@var{offset}}) +The function @code{vm_region} returns a description of the specified +region of @var{target_task}'s virtual address space. @code{vm_region} +begins at @var{address} and looks forward through memory until it comes +to an allocated region. If address is within a region, then that region +is used. Various bits of information about the region are returned. If +@var{address} was not within a region, then @var{address} is set to the +start of the first region which follows the incoming value. In this way +an entire address space can be scanned. + +The @var{size} returned is the size of the located region in bytes. +@var{protection} is the current protection of the region, +@var{max_protection} is the maximum allowable protection for this +region. @var{inheritance} is the inheritance attribute for this region. +@var{shared} tells if the region is shared or not. The port +@var{object_name} identifies the memory object associated with this +region, and @var{offset} is the offset into the pager object that this +region begins at. +@c XXX cross ref pager_init + +The function returns @code{KERN_SUCCESS} if the memory region was +successfully located and the information returned and @code{KERN_NO_SPACE} if +there is no region at or above @var{address} in the specified task. +@end deftypefun + +@deftypefun kern_return_t vm_protect (@w{vm_task_t @var{target_task}}, @w{vm_address_t @var{address}}, @w{vm_size_t @var{size}}, @w{boolean_t @var{set_maximum}}, @w{vm_prot_t @var{new_protection}}) +The function @code{vm_protect} sets the virtual memory access privileges +for a range of allocated addresses in @var{target_task}'s virtual +address space. The protection argument describes a combination of read, +write, and execute accesses that should be @emph{permitted}. + +@var{address} is the starting address, which will be rounded down to a +page boundary. @var{size} is the size in bytes of the region for which +protection is to change, and will be rounded up to give a page boundary. +If @var{set_maximum} is set, make the protection change apply to the +maximum protection associated with this address range; otherwise, the +current protection on this range is changed. If the maximum protection +is reduced below the current protection, both will be changed to reflect +the new maximum. @var{new_protection} is the new protection value for +this region; a set of: @code{VM_PROT_READ}, @code{VM_PROT_WRITE}, +@code{VM_PROT_EXECUTE}. + +The enforcement of virtual memory protection is machine-dependent. +Nominally read access requires @code{VM_PROT_READ} permission, write +access requires @code{VM_PROT_WRITE} permission, and execute access +requires @code{VM_PROT_EXECUTE} permission. However, some combinations +of access rights may not be supported. In particular, the kernel +interface allows write access to require @code{VM_PROT_READ} and +@code{VM_PROT_WRITE} permission and execute access to require +@code{VM_PROT_READ} permission. + +If a region is wired, changing its protection also updates the +access types for which no page faults must occur. + +The function returns @code{KERN_SUCCESS} if the memory was successfully +protected, @code{KERN_INVALID_ADDRESS} if an invalid or non-allocated +address was specified and @code{KERN_PROTECTION_FAILURE} if an attempt +was made to increase the current or maximum protection beyond the +existing maximum protection value. +@end deftypefun + +@deftypefun kern_return_t vm_inherit (@w{vm_task_t @var{target_task}}, @w{vm_address_t @var{address}}, @w{vm_size_t @var{size}}, @w{vm_inherit_t @var{new_inheritance}}) +The function @code{vm_inherit} specifies how a region of +@var{target_task}'s address space is to be passed to child tasks at the +time of task creation. Inheritance is an attribute of virtual pages, so +@var{address} to start from will be rounded down to a page boundary and +@var{size}, the size in bytes of the region for which inheritance is to +change, will be rounded up to give a page boundary. How this memory is +to be inherited in child tasks is specified by @var{new_inheritance}. +Inheritance is specified by using one of these following three values: + +@table @code +@item VM_INHERIT_SHARE +Child tasks will share this memory with this task. + +@item VM_INHERIT_COPY +Child tasks will receive a copy of this region. + +@item VM_INHERIT_NONE +This region will be absent from child tasks. +@end table + +Setting @code{vm_inherit} to @code{VM_INHERIT_SHARE} and forking a child +task is the only way two Mach tasks can share physical memory. Remember +that all the threads of a given task share all the same memory. + +The function returns @code{KERN_SUCCESS} if the memory inheritance was +successfully set and @code{KERN_INVALID_ADDRESS} if an invalid or +non-allocated address was specified. +@end deftypefun + +@deftypefun kern_return_t vm_wire (@w{host_t @var{host}}, @w{vm_task_t @var{target_task}}, @w{vm_address_t @var{address}}, @w{vm_size_t @var{size}}, @w{vm_prot_t @var{access}}) +The function @code{vm_wire} allows applications to control +memory pageability. @var{host} is the host port for the +host on which @var{target_task} resides. @var{address} is the starting +address, which will be rounded down to a page boundary. @var{size} is +the size in bytes of the region for which protection is to change, and +will be rounded up to give a page boundary. @var{access} specifies the +types of accesses that must not cause page faults. If the host port is +not privileged, the amount of memory is limited per task. + +The semantics of a successful @code{vm_wire} operation are that memory +in the specified range will not cause page faults for any accesses +included in access. Data memory can be made non-pageable (wired) with a +access argument of @code{VM_PROT_READ | VM_PROT_WRITE}. A special case +is that @code{VM_PROT_NONE} makes the memory pageable. + +Wiring doesn't stack, i.e. a single call to @code{vm_wire} with +@var{access} @code{VM_PROT_NONE} unwires the specified range, +regardless of how many times it was previously wired. Conversely, +a single call to @code{vm_wire} with @var{access} +@code{VM_PROT_READ | VM_PROT_WRITE} wires the specified range, +regardless of how many times it was previously unwired. + +The function returns @code{KERN_SUCCESS} if the call succeeded, +@code{KERN_INVALID_HOST} if @var{host} was not a valid host +port, @code{KERN_INVALID_TASK} if @var{task} was not a valid task, +@code{KERN_INVALID_VALUE} if @var{access} specified an invalid access +mode, and @code{KERN_NO_SPACE} if some memory in the specified range +is not present or has an inappropriate protection value. + +The @code{vm_wire} call is actually an RPC to @var{host}, normally +a send right for a privileged host port, but potentially any send right. +In addition to the normal diagnostic return codes from the call's server +(normally the kernel), the call may return @code{mach_msg} return codes. +@end deftypefun + +@deftypefun kern_return_t vm_wire_all (@w{host_t @var{host}}, @w{vm_task_t @var{target_task}}, @w{vm_wire_t @var{flags}}) +The function @code{vm_wire_all} allows applications to control +memory pageability, as with @code{vm_wire}, but applies to all +current and/or future mappings. + +The argument @var{flags} are bit values, combined with bitwise-or. + +@table @code +@item VM_WIRE_CURRENT +All currently existing entries are wired, with access types matching +their protection. + +@item VM_WIRE_FUTURE +All future entries are automatically wired, with access types matching +their protection. +@end table + +If flags specifies no bits (@code{VM_WIRE_NONE}), all current entries +are unwired, and future entries are no longer automatically wired. + +The function returns @code{KERN_SUCCESS} if the call succeeded, +@code{KERN_INVALID_HOST} if @var{host} was not a valid host port, +@code{KERN_INVALID_TASK} if @var{task} was not a valid task, +and @code{KERN_INVALID_VALUE} if @var{flags} specifies invalid bits. + +The @code{vm_wire_all} call is actually an RPC to @var{host}, normally +a send right for a privileged host port, but potentially any send right. +In addition to the normal diagnostic return codes from the call's server +(normally the kernel), the call may return @code{mach_msg} return codes. +@end deftypefun + +@deftypefun kern_return_t vm_machine_attribute (@w{vm_task_t @var{task}}, @w{vm_address_t @var{address}}, @w{vm_size_t @var{size}}, @w{vm_prot_t @var{access}}, @w{vm_machine_attribute_t @var{attribute}}, @w{vm_machine_attribute_val_t @var{value}}) +The function @code{vm_machine_attribute} specifies machine-specific +attributes for a VM mapping, such as cachability, migrability, +replicability. This is used on machines that allow the user control +over the cache (this is the case for MIPS architectures) or placement of +memory pages as in NUMA architectures (Non-Uniform Memory Access time) +such as the IBM ACE multiprocessor. + +Machine-specific attributes can be consider additions to the +machine-independent ones such as protection and inheritance, but they +are not guaranteed to be supported by any given machine. Moreover, +implementations of Mach on new architectures might find the need for new +attribute types and or values besides the ones defined in the initial +implementation. + +The types currently defined are +@table @code +@item MATTR_CACHE +Controls caching of memory pages + +@item MATTR_MIGRATE +Controls migrability of memory pages + +@item MATTR_REPLICATE +Controls replication of memory pages +@end table + +Corresponding values, and meaning of a specific call to +@code{vm_machine_attribute} +@table @code +@item MATTR_VAL_ON +Enables the attribute. Being enabled is the default value for any +applicable attribute. + +@item MATTR_VAL_OFF +Disables the attribute, making memory non-cached, or non-migratable, or +non-replicatable. + +@item MATTR_VAL_GET +Returns the current value of the attribute for the memory segment. If +the attribute does not apply uniformly to the given range the value +returned applies to the initial portion of the segment only. + +@item MATTR_VAL_CACHE_FLUSH +Flush the memory pages from the Cache. The size value in this case +might be meaningful even if not a multiple of the page size, depending +on the implementation. + +@item MATTR_VAL_ICACHE_FLUSH +Same as above, applied to the Instruction Cache alone. + +@item MATTR_VAL_DCACHE_FLUSH +Same as above, applied to the Data Cache alone. +@end table + +The function returns @code{KERN_SUCCESS} if call succeeded, and +@code{KERN_INVALID_ARGUMENT} if @var{task} is not a task, or +@var{address} and @var{size} do not define a valid address range in +task, or @var{attribute} is not a valid attribute type, or it is not +implemented, or @var{value} is not a permissible value for attribute. +@end deftypefun + + +@node Mapping Memory Objects +@section Mapping Memory Objects + +@deftypefun kern_return_t vm_map (@w{vm_task_t @var{target_task}}, @w{vm_address_t *@var{address}}, @w{vm_size_t @var{size}}, @w{vm_address_t @var{mask}}, @w{boolean_t @var{anywhere}}, @w{memory_object_t @var{memory_object}}, @w{vm_offset_t @var{offset}}, @w{boolean_t @var{copy}}, @w{vm_prot_t @var{cur_protection}}, @w{vm_prot_t @var{max_protection}}, @w{vm_inherit_t @var{inheritance}}) +The function @code{vm_map} maps a region of virtual memory at the +specified address, for which data is to be supplied by the given memory +object, starting at the given offset within that object. In addition to +the arguments used in @code{vm_allocate}, the @code{vm_map} call allows +the specification of an address alignment parameter, and of the initial +protection and inheritance values. +@c XXX See the descriptions of vm_allocate, vm_protect , and vm_inherit + +If the memory object in question is not currently in use, the kernel +will perform a @code{memory_object_init} call at this time. If the copy +parameter is asserted, the specified region of the memory object will be +copied to this address space; changes made to this object by other tasks +will not be visible in this mapping, and changes made in this mapping +will not be visible to others (or returned to the memory object). + +The @code{vm_map} call returns once the mapping is established. +Completion of the call does not require any action on the part of the +memory manager. + +Warning: Only memory objects that are provided by bona fide memory +managers should be used in the @code{vm_map} call. A memory manager +must implement the memory object interface described elsewhere in this +manual. If other ports are used, a thread that accesses the mapped +virtual memory may become permanently hung or may receive a memory +exception. + +@var{target_task} is the task to be affected. The starting address is +@var{address}. If the @var{anywhere} option is used, this address is +ignored. The address actually allocated will be returned in +@var{address}. @var{size} is the number of bytes to allocate (rounded by +the system in a machine dependent way). The alignment and maximum address +restrictions are specified by @var{mask}. Bits asserted in this mask must not be +asserted in the address returned. If @var{anywhere} is set, the kernel +should find and allocate any region of the specified size, and return +the address of the resulting region in @var{address}. + +@var{memory_object} is the port that represents the memory object: used +by user tasks in @code{vm_map}; used by the make requests for data or +other management actions. If this port is @code{MEMORY_OBJECT_NULL}, +then zero-filled memory is allocated instead. Within a memory object, +@var{offset} specifies an offset in bytes. This must be page aligned. +If @var{copy} is set, the range of the memory object should be copied to +the target task, rather than mapped read-write. + +The function returns @code{KERN_SUCCESS} if the object is mapped, +@code{KERN_NO_SPACE} if no unused region of the task's virtual address +space that meets the address, size, and alignment criteria could be +found, and @code{KERN_INVALID_ARGUMENT} if an invalid argument was provided. +@end deftypefun + + +@node Memory Statistics +@section Memory Statistics + +@deftp {Data type} vm_statistics_data_t +This structure is returned in @var{vm_stats} by the @code{vm_statistics} +function and provides virtual memory statistics for the system. It has +the following members: + +@table @code +@item long pagesize +The page size in bytes. + +@item long free_count +The number of free pages. + +@item long active_count +The umber of active pages. + +@item long inactive_count +The number of inactive pages. + +@item long wire_count +The number of pages wired down. + +@item long zero_fill_count +The number of zero filled pages. + +@item long reactivations +The number of reactivated pages. + +@item long pageins +The number of pageins. + +@item long pageouts +The number of pageouts. + +@item long faults +The number of faults. + +@item long cow_faults +The number of copy-on-writes. + +@item long lookups +The number of object cache lookups. + +@item long hits +The number of object cache hits. +@end table +@end deftp + +@deftypefun kern_return_t vm_statistics (@w{vm_task_t @var{target_task}}, @w{vm_statistics_data_t *@var{vm_stats}}) +The function @code{vm_statistics} returns the statistics about the +kernel's use of virtual memory since the kernel was booted. +@code{pagesize} can also be found as a global variable +@code{vm_page_size} which is set at task initialization and remains +constant for the life of the task. +@end deftypefun + + +@node Memory physical addresses +@section Memory physical addresses + +@deftypefun kern_return_t vm_pages_phys (@w{host_t @var{host}}, @w{vm_task_t @var{target_task}}, @w{vm_address_t @var{address}}, @w{vm_size_t @var{size}}, @w{rpc_phys_addr_array_t *@var{pages}, @w{mach_msg_type_number_t *@var{pagesCnt}}}) +The function @code{vm_pages_phys} retrieves the physical addresses of the +specified region (@var{size} bytes starting from @var{address}) of +@var{target_task}'s virtual address space. + +Both @var{address} and @var{size} have to be aligned on @code{vm_page_size}. + +@var{pages} is an array of @code{rpc_phys_addr_array_t} that is supplied by the +caller and returned filled with the physical page numbers. @var{pagesCnt} is +supplied as the maximum number of elements in the @var{pages} array. On +return, it contains the actual number of integers in @var{pages}. +@end deftypefun + + +@node External Memory Management +@chapter External Memory Management + +@menu +* Memory Object Server:: The basics of external memory management. +* Memory Object Creation:: How new memory objects are created. +* Memory Object Termination:: How memory objects are terminated. +* Memory Objects and Data:: Data transfer to and from memory objects. +* Memory Object Locking:: How memory objects are locked. +* Memory Object Attributes:: Manipulating attributes of memory objects. +* Default Memory Manager:: Setting and using the default memory manager. +@end menu + + +@node Memory Object Server +@section Memory Object Server + +@deftypefun boolean_t memory_object_server (@w{msg_header_t *@var{in_msg}}, @w{msg_header_t *@var{out_msg}}) +@deftypefunx boolean_t memory_object_default_server (@w{msg_header_t *@var{in_msg}}, @w{msg_header_t *@var{out_msg}}) +@deftypefunx boolean_t seqnos_memory_object_server (@w{msg_header_t *@var{in_msg}}, @w{msg_header_t *@var{out_msg}}) +@deftypefunx boolean_t seqnos_memory_object_default_server (@w{msg_header_t *@var{in_msg}}, @w{msg_header_t *@var{out_msg}}) +A memory manager is a server task that responds to specific messages +from the kernel in order to handle memory management functions for the +kernel. + +In order to isolate the memory manager from the specifics of message +formatting, the remote procedure call generator produces a procedure, +@code{memory_object_server}, to handle a received message. This +function does all necessary argument handling, and actually calls one of +the following functions: @code{memory_object_init}, +@code{memory_object_data_return}, +@code{memory_object_data_request}, @code{memory_object_data_unlock}, +@code{memory_object_lock_completed}, @code{memory_object_copy}, +@code{memory_object_terminate}. The @strong{default memory manager} may +get two additional requests from the kernel: @code{memory_object_create} +and @code{memory_object_data_initialize}. The remote procedure call +generator produces a procedure @code{memory_object_default_server} to +handle those functions specific to the default memory manager. + +The @code{seqnos_memory_object_server} and +@code{seqnos_memory_object_default_server} differ from +@code{memory_object_server} and @code{memory_object_default_server} in +that they supply message sequence numbers to the server interfaces. +They call the @code{seqnos_memory_object_*} functions, which complement +the @code{memory_object_*} set of functions. + +The return value from the @code{memory_object_server} function indicates +that the message was appropriate to the memory management interface +(returning @code{TRUE}), or that it could not handle this message +(returning @code{FALSE}). + +The @var{in_msg} argument is the message that has been received from the +kernel. The @var{out_msg} is a reply message, but this is not used for +this server. + +The function returns @code{TRUE} to indicate that the message in +question was applicable to this interface, and that the appropriate +routine was called to interpret the message. It returns @code{FALSE} to +indicate that the message did not apply to this interface, and that no +other action was taken. +@end deftypefun + + +@node Memory Object Creation +@section Memory Object Creation + +@deftypefun kern_return_t memory_object_init (@w{memory_object_t @var{memory_object}}, @w{memory_object_control_t @var{memory_control}}, @w{memory_object_name_t @var{memory_object_name}}, @w{vm_size_t @var{memory_object_page_size}}) +@deftypefunx kern_return_t seqnos_memory_object_init (@w{memory_object_t @var{memory_object}}, @w{mach_port_seqno_t @var{seqno}}, @w{memory_object_control_t @var{memory_control}}, @w{memory_object_name_t @var{memory_object_name}}, @w{vm_size_t @var{memory_object_page_size}}) +The function @code{memory_object_init} serves as a notification that the +kernel has been asked to map the given memory object into a task's +virtual address space. Additionally, it provides a port on which the +memory manager may issue cache management requests, and a port which the +kernel will use to name this data region. In the event that different +each will perform a @code{memory_object_init} call with new request and +name ports. The virtual page size that is used by the calling kernel is +included for planning purposes. + +When the memory manager is prepared to accept requests for data for +this object, it must call @code{memory_object_ready}. +Otherwise the kernel will not process requests on this object. To +reject all mappings of this object, the memory manager may use +@code{memory_object_destroy}. + +The argument @var{memory_object} is the port that represents the memory +object data, as supplied to the kernel in a @code{vm_map} call. +@var{memory_control} is the request port to which a response is +requested. (In the event that a memory object has been supplied to more +than one the kernel that has made the request.) +@var{memory_object_name} is a port used by the kernel to refer to the +memory object data in response to @code{vm_region} calls. +@code{memory_object_page_size} is the page size to be used by this +kernel. All data sizes in calls involving this kernel must be an +integral multiple of the page size. Note that different kernels, +indicated by a different @code{memory_control}, may have different page +sizes. + +The function should return @code{KERN_SUCCESS}, but since this routine +is called by the kernel, which does not wait for a reply message, this +value is ignored. +@end deftypefun + +@deftypefun kern_return_t memory_object_ready (@w{memory_object_control_t @var{memory_control}}, @w{boolean_t @var{may_cache_object}}, @w{memory_object_copy_strategy_t @var{copy_strategy}}) +The function @code{memory_object_ready} informs the kernel that the +memory manager is ready to receive data or unlock requests on behalf of +the clients. The argument @var{memory_control} is the port, provided by +the kernel in a @code{memory_object_init} call, to which cache +management requests may be issued. If @var{may_cache_object} is set, +the kernel may keep data associated with this memory object, even after +virtual memory references to it are gone. + +@var{copy_strategy} tells how the kernel should copy regions of the +associated memory object. There are three possible caching strategies: +@code{MEMORY_OBJECT_COPY_NONE} which specifies that nothing special +should be done when data in the object is copied; +@code{MEMORY_OBJECT_COPY_CALL} which specifies that the memory manager +should be notified via a @code{memory_object_copy} call before any part +of the object is copied; and @code{MEMORY_OBJECT_COPY_DELAY} which +guarantees that the memory manager does not externally modify the data +so that the kernel can use its normal copy-on-write algorithms. +@code{MEMORY_OBJECT_COPY_DELAY} is the strategy most commonly used. + +This routine does not receive a reply message (and consequently has no +return value), so only message transmission errors apply. +@end deftypefun + + +@node Memory Object Termination +@section Memory Object Termination + +@deftypefun kern_return_t memory_object_terminate (@w{memory_object_t @var{memory_object}}, @w{memory_object_control_t @var{memory_control}}, @w{memory_object_name_t @var{memory_object_name}}) +@deftypefunx kern_return_t seqnos_memory_object_terminate (@w{memory_object_t @var{memory_object}}, @w{mach_port_seqno_t @var{seqno}}, @w{memory_object_control_t @var{memory_control}}, @w{memory_object_name_t @var{memory_object_name}}) +The function @code{memory_object_terminate} indicates that the kernel +has completed its use of the given memory object. All rights to the +memory object control and name ports are included, so that the memory +manager can destroy them (using @code{mach_port_deallocate}) after doing +appropriate bookkeeping. The kernel will terminate a memory object only +after all address space mappings of that memory object have been +deallocated, or upon explicit request by the memory manager. + +The argument @var{memory_object} is the port that represents the memory +object data, as supplied to the kernel in a @code{vm_map} call. +@var{memory_control} is the request port to which a response is +requested. (In the event that a memory object has been supplied to more +than one the kernel that has made the request.) +@var{memory_object_name} is a port used by the kernel to refer to the +memory object data in response to @code{vm_region} calls. + +The function should return @code{KERN_SUCCESS}, but since this routine +is called by the kernel, which does not wait for a reply message, this +value is ignored. +@end deftypefun + +@deftypefun kern_return_t memory_object_destroy (@w{memory_object_control_t @var{memory_control}}, @w{kern_return_t @var{reason}}) +The function @code{memory_object_destroy} tells the kernel to shut down +the memory object. As a result of this call the kernel will no longer +support paging activity or any @code{memory_object} calls on this +object, and all rights to the memory object port, the memory control +port and the memory name port will be returned to the memory manager in +a memory_object_terminate call. If the memory manager is concerned that +any modified cached data be returned to it before the object is +terminated, it should call @code{memory_object_lock_request} with +@var{should_flush} set and a lock value of @code{VM_PROT_WRITE} before +making this call. + +The argument @var{memory_control} is the port, provided by the kernel in +a @code{memory_object_init} call, to which cache management requests may +be issued. @var{reason} is an error code indicating why the object +must be destroyed. +@c The error code is currently ignored. + +This routine does not receive a reply message (and consequently has no +return value), so only message transmission errors apply. +@end deftypefun + + +@node Memory Objects and Data +@section Memory Objects and Data + +@deftypefun kern_return_t memory_object_data_return (@w{memory_object_t @var{memory_object}}, @w{memory_object_control_t @var{memory_control}}, @w{vm_offset_t @var{offset}}, @w{vm_offset_t @var{data}}, @w{vm_size_t @var{data_count}}, @w{boolean_t @var{dirty}}, @w{boolean_t @var{kernel_copy}}) +@deftypefunx kern_return_t seqnos_memory_object_data_return (@w{memory_object_t @var{memory_object}}, @w{mach_port_seqno_t @var{seqno}}, @w{memory_object_control_t @var{memory_control}}, @w{vm_offset_t @var{offset}}, @w{vm_offset_t @var{data}}, @w{vm_size_t @var{data_count}}, @w{boolean_t @var{dirty}}, @w{boolean_t @var{kernel_copy}}) +The function @code{memory_object_data_return} provides the memory +manager with data that has been modified while cached in physical +memory. Once the memory manager no longer needs this data (e.g., it has +been written to another storage medium), it should be deallocated using +@code{vm_deallocate}. + +The argument @var{memory_object} is the port that represents the memory +object data, as supplied to the kernel in a @code{vm_map} call. +@var{memory_control} is the request port to which a response is +requested. (In the event that a memory object has been supplied to more +than one the kernel that has made the request.) @var{offset} is the +offset within a memory object to which this call refers. This will be +page aligned. @var{data} is the data which has been modified while +cached in physical memory. @var{data_count} is the amount of data to be +written, in bytes. This will be an integral number of memory object +pages. + +The kernel will also use this call to return precious pages. If an +unmodified precious age is returned, @var{dirty} is set to @code{FALSE}, +otherwise it is @code{TRUE}. If @var{kernel_copy} is @code{TRUE}, the +kernel kept a copy of the page. Precious data remains precious if the +kernel keeps a copy. The indication that the kernel kept a copy is only +a hint if the data is not precious; the cleaned copy may be discarded +without further notifying the manager. + +The function should return @code{KERN_SUCCESS}, but since this routine +is called by the kernel, which does not wait for a reply message, this +value is ignored. +@end deftypefun + +@deftypefun kern_return_t memory_object_data_request (@w{memory_object_t @var{memory_object}}, @w{memory_object_control_t @var{memory_control}}, @w{vm_offset_t @var{offset}}, @w{vm_offset_t @var{length}}, @w{vm_prot_t @var{desired_access}}) +@deftypefunx kern_return_t seqnos_memory_object_data_request (@w{memory_object_t @var{memory_object}}, @w{mach_port_seqno_t @var{seqno}}, @w{memory_object_control_t @var{memory_control}}, @w{vm_offset_t @var{offset}}, @w{vm_offset_t @var{length}}, @w{vm_prot_t @var{desired_access}}) +The function @code{memory_object_data_request} is a request for data +from the specified memory object, for at least the access specified. +The memory manager is expected to return at least the specified data, +with as much access as it can allow, using +@code{memory_object_data_supply}. If the memory manager is unable to +provide the data (for example, because of a hardware error), it may use +the @code{memory_object_data_error} call. The +@code{memory_object_data_unavailable} call may be used to tell the +kernel to supply zero-filled memory for this region. + +The argument @var{memory_object} is the port that represents the memory +object data, as supplied to the kernel in a @code{vm_map} call. +@var{memory_control} is the request port to which a response is +requested. (In the event that a memory object has been supplied to more +than one the kernel that has made the request.) @var{offset} is the +offset within a memory object to which this call refers. This will be +page aligned. @var{length} is the number of bytes of data, starting at +@var{offset}, to which this call refers. This will be an integral +number of memory object pages. @var{desired_access} is a protection +value describing the memory access modes which must be permitted on the +specified cached data. One or more of: @code{VM_PROT_READ}, +@code{VM_PROT_WRITE} or @code{VM_PROT_EXECUTE}. + +The function should return @code{KERN_SUCCESS}, but since this routine +is called by the kernel, which does not wait for a reply message, this +value is ignored. +@end deftypefun + +@deftypefun kern_return_t memory_object_data_supply (@w{memory_object_control_t @var{memory_control}}, @w{vm_offset_t @var{offset}}, @w{vm_offset_t @var{data}}, @w{vm_size_t @var{data_count}}, @w{vm_prot_t @var{lock_value}}, @w{boolean_t @var{precious}}, @w{mach_port_t @var{reply}}) +The function @code{memory_object_data_supply} supplies the kernel with +data for the specified memory object. Ordinarily, memory managers +should only provide data in response to @code{memory_object_data_request} +calls from the kernel (but they may provide data in advance as desired). +When data already held by this kernel is provided again, the new data is +ignored. The kernel may not provide any data (or protection) +consistency among pages with different virtual page alignments within +the same object. + +The argument @var{memory_control} is the port, provided by the kernel in +a @code{memory_object_init} call, to which cache management requests may +be issued. @var{offset} is an offset within a memory object in bytes. +This must be page aligned. @var{data} is the data that is being +provided to the kernel. This is a pointer to the data. +@var{data_count} is the amount of data to be provided. Only whole +virtual pages of data can be accepted; partial pages will be discarded. + +@var{lock_value} is a protection value indicating those forms of access +that should @strong{not} be permitted to the specified cached data. The +lock values must be one or more of the set: @code{VM_PROT_NONE}, +@code{VM_PROT_READ}, @code{VM_PROT_WRITE}, @code{VM_PROT_EXECUTE} and +@code{VM_PROT_ALL} as defined in @file{mach/vm_prot.h}. + +If @var{precious} is @code{FALSE}, the kernel treats the data as a +temporary and may throw it away if it hasn't been changed. If the +@var{precious} value is @code{TRUE}, the kernel treats its copy as a +data repository and promises to return it to the manager; the manager +may tell the kernel to throw it away instead by flushing and not +cleaning the data (see @code{memory_object_lock_request}). + +If @var{reply_to} is not @code{MACH_PORT_NULL}, the kernel will send a +completion message to the provided port (see +@code{memory_object_supply_completed}). + +This routine does not receive a reply message (and consequently has no +return value), so only message transmission errors apply. +@end deftypefun + +@deftypefun kern_return_t memory_object_supply_completed (@w{memory_object_t @var{memory_object}}, @w{memory_object_control_t @var{memory_control}}, @w{vm_offset_t @var{offset}}, @w{vm_size_t @var{length}}, @w{kern_return_t @var{result}}, @w{vm_offset_t @var{error_offset}}) +@deftypefunx kern_return_t seqnos_memory_object_supply_completed (@w{memory_object_t @var{memory_object}}, @w{mach_port_seqno_t @var{seqno}}, @w{memory_object_control_t @var{memory_control}}, @w{vm_offset_t @var{offset}}, @w{vm_size_t @var{length}}, @w{kern_return_t @var{result}}, @w{vm_offset_t @var{error_offset}}) +The function @code{memory_object_supply_completed} indicates that a +previous @code{memory_object_data_supply} has been completed. Note that +this call is made on whatever port was specified in the +@code{memory_object_data_supply} call; that port need not be the memory +object port itself. No reply is expected after this call. + +The argument @var{memory_object} is the port that represents the memory +object data, as supplied to the kernel in a @code{vm_map} call. +@var{memory_control} is the request port to which a response is +requested. (In the event that a memory object has been supplied to more +than one the kernel that has made the request.) @var{offset} is the +offset within a memory object to which this call refers. @var{length} +is the length of the data covered by the lock request. The @var{result} +parameter indicates what happened during the supply. If it is not +@code{KERN_SUCCESS}, then @var{error_offset} identifies the first offset +at which a problem occurred. The pagein operation stopped at this +point. Note that the only failures reported by this mechanism are +@code{KERN_MEMORY_PRESENT}. All other failures (invalid argument, error +on pagein of supplied data in manager's address space) cause the entire +operation to fail. + + +@end deftypefun + +@deftypefun kern_return_t memory_object_data_error (@w{memory_object_control_t @var{memory_control}}, @w{vm_offset_t @var{offset}}, @w{vm_size_t @var{size}}, @w{kern_return_t @var{reason}}) +The function @code{memory_object_data_error} indicates that the memory +manager cannot return the data requested for the given region, +specifying a reason for the error. This is typically used when a +hardware error is encountered. + +The argument @var{memory_control} is the port, provided by the kernel in +a @code{memory_object_init} call, to which cache management requests may +be issued. @var{offset} is an offset within a memory object in bytes. +This must be page aligned. @var{data} is the data that is being +provided to the kernel. This is a pointer to the data. @var{size} is +the amount of cached data (starting at @var{offset}) to be handled. +This must be an integral number of the memory object page size. +@var{reason} is an error code indicating what type of error occurred. +@c The error code is currently ignored. + +This routine does not receive a reply message (and consequently has no +return value), so only message transmission errors apply. +@end deftypefun + +@deftypefun kern_return_t memory_object_data_unavailable (@w{memory_object_control_t @var{memory_control}}, @w{vm_offset_t @var{offset}}, @w{vm_size_t @var{size}}, @w{kern_return_t @var{reason}}) +The function @code{memory_object_data_unavailable} indicates that the +memory object does not have data for the given region and that the +kernel should provide the data for this range. The memory manager may +use this call in three different situations. + +@enumerate +@item +The object was created by @code{memory_object_create} and the kernel has +not yet provided data for this range (either via a +@code{memory_object_data_initialize}, or +a @code{memory_object_data_return} for the object. + +@item +The object was created by an @code{memory_object_data_copy} and the +kernel should copy this region from the original memory object. + +@item +The object is a normal user-created memory object and the kernel should +supply unlocked zero-filled pages for the range. +@end enumerate + +The argument @var{memory_control} is the port, provided by the kernel in +a @code{memory_object_init} call, to which cache management requests may +be issued. @var{offset} is an offset within a memory object, in bytes. +This must be page aligned. @var{size} is the amount of cached data +(starting at @var{offset}) to be handled. This must be an integral +number of the memory object page size. + +This routine does not receive a reply message (and consequently has no +return value), so only message transmission errors apply. +@end deftypefun + +@deftypefun kern_return_t memory_object_copy (@w{memory_object_t @var{old_memory_object}}, @w{memory_object_control_t @var{old_memory_control}}, @w{vm_offset_t @var{offset}}, @w{vm_size_t @var{length}}, @w{memory_object_t @var{new_memory_object}}) +@deftypefunx kern_return_t seqnos_memory_object_copy (@w{memory_object_t @var{old_memory_object}}, @w{mach_port_seqno_t @var{seqno}}, @w{memory_object_control_t @var{old_memory_control}}, @w{vm_offset_t @var{offset}}, @w{vm_size_t @var{length}}, @w{memory_object_t @var{new_memory_object}}) +The function @code{memory_object_copy} indicates that a copy has been +made of the specified range of the given original memory object. This +call includes only the new memory object itself; a +@code{memory_object_init} call will be made on the new memory object +after the currently cached pages of the original object are prepared. +After the memory manager receives the init call, it must reply with the +@code{memory_object_ready} call to assert the "ready" attribute. The +kernel will use the new memory object, control and name ports to refer +to the new copy. + +This call is made when the original memory object had the caching +parameter set to @code{MEMORY_OBJECT_COPY_CALL} and a user of the object +has asked the kernel to copy it. + +Cached pages from the original memory object at the time of the copy +operation are handled as follows: Readable pages may be silently copied +to the new memory object (with all access permissions). Pages not +copied are locked to prevent write access. + +The new memory object is @strong{temporary}, meaning that the memory +manager should not change its contents or allow the memory object to be +mapped in another client. The memory manager may use the +@code{memory_object_data_unavailable} call to indicate that the +appropriate pages of the original memory object may be used to fulfill +the data request. + +The argument @var{old_memory_object} is the port that represents the old +memory object data. @var{old_memory_control} is the kernel port for the +old object. @var{offset} is the offset within a memory object to which +this call refers. This will be page aligned. @var{length} is the +number of bytes of data, starting at @var{offset}, to which this call +refers. This will be an integral number of memory object pages. +@var{new_memory_object} is a new memory object created by the kernel; +see synopsis for further description. Note that all port rights +(including receive rights) are included for the new memory object. + +The function should return @code{KERN_SUCCESS}, but since this routine +is called by the kernel, which does not wait for a reply message, this +value is ignored. +@end deftypefun + + +@node Memory Object Locking +@section Memory Object Locking + +@deftypefun kern_return_t memory_object_lock_request (@w{memory_object_control_t @var{memory_control}}, @w{vm_offset_t @var{offset}}, @w{vm_size_t @var{size}}, @w{memory_object_return_t @var{should_clean}}, @w{boolean_t @var{should_flush}}, @w{vm_prot_t @var{lock_value}}, @w{mach_port_t @var{reply_to}}) +The function @code{memory_object_lock_request} allows a memory manager +to make cache management requests. As specified in arguments to the +call, the kernel will: +@itemize +@item +clean (i.e., write back using @code{memory_object_data_supply} any cached data which has been modified +since the last time it was written + +@item +flush (i.e., remove any uses of) that data from memory + +@item +lock (i.e., prohibit the specified uses of) the cached data +@end itemize + +Locks applied to cached data are not cumulative; new lock values +override previous ones. Thus, data may also be unlocked using this +primitive. The lock values must be one or more of the following values: +@code{VM_PROT_NONE}, @code{VM_PROT_READ}, @code{VM_PROT_WRITE}, +@code{VM_PROT_EXECUTE} and @code{VM_PROT_ALL} as defined in +@file{mach/vm_prot.h}. + +Only data which is cached at the time of this call is affected. When a +running thread requires a prohibited access to cached data, the kernel +will issue a @code{memory_object_data_unlock} call specifying the forms +of access required. + +Once all of the actions requested by this call have been completed, the +kernel issues a @code{memory_object_lock_completed} call on the +specified reply port. + +The argument @var{memory_control} is the port, provided by the kernel in +a @code{memory_object_init} call, to which cache management requests may +be issued. @var{offset} is an offset within a memory object, in bytes. +This must be page aligned. @var{size} is the amount of cached data +(starting at @var{offset}) to be handled. This must be an integral +number of the memory object page size. If @var{should_clean} is set, +modified data should be written back to the memory manager. If +@var{should_flush} is set, the specified cached data should be +invalidated, and all uses of that data should be revoked. +@var{lock_value} is a protection value indicating those forms of access +that should @strong{not} be permitted to the specified cached data. +@var{reply_to} is a port on which a @code{memory_object_lock_completed} +call should be issued, or @code{MACH_PORT_NULL} if no acknowledgement is +desired. + +This routine does not receive a reply message (and consequently has no +return value), so only message transmission errors apply. +@end deftypefun + +@deftypefun kern_return_t memory_object_lock_completed (@w{memory_object_t @var{memory_object}}, @w{memory_object_control_t @var{memory_control}}, @w{vm_offset_t @var{offset}}, @w{vm_size_t @var{length}}) +@deftypefunx kern_return_t seqnos_memory_object_lock_completed (@w{memory_object_t @var{memory_object}}, @w{mach_port_seqno_t @var{seqno}}, @w{memory_object_control_t @var{memory_control}}, @w{vm_offset_t @var{offset}}, @w{vm_size_t @var{length}}) +The function @code{memory_object_lock_completed} indicates that a +previous @code{memory_object_lock_request} has been completed. Note +that this call is made on whatever port was specified in the +@code{memory_object_lock_request} call; that port need not be the memory +object port itself. No reply is expected after this call. + +The argument @var{memory_object} is the port that represents the memory +object data, as supplied to the kernel in a @code{vm_map} call. +@var{memory_control} is the request port to which a response is +requested. (In the event that a memory object has been supplied to more +than one the kernel that has made the request.) @var{offset} is the +offset within a memory object to which this call refers. @var{length} +is the length of the data covered by the lock request. + +The function should return @code{KERN_SUCCESS}, but since this routine +is called by the kernel, which does not wait for a reply message, this +value is ignored. +@end deftypefun + +@deftypefun kern_return_t memory_object_data_unlock (@w{memory_object_t @var{memory_object}}, @w{memory_object_control_t @var{memory_control}}, @w{vm_offset_t @var{offset}}, @w{vm_size_t @var{length}}, @w{vm_prot_t @var{desired_access}}) +@deftypefunx kern_return_t seqnos_memory_object_data_unlock (@w{memory_object_t @var{memory_object}}, @w{mach_port_seqno_t @var{seqno}}, @w{memory_object_control_t @var{memory_control}}, @w{vm_offset_t @var{offset}}, @w{vm_size_t @var{length}}, @w{vm_prot_t @var{desired_access}}) +The function @code{memory_object_data_unlock} is a request that the +memory manager permit at least the desired access to the specified data +cached by the kernel. A call to @code{memory_object_lock_request} is +expected in response. + +The argument @var{memory_object} is the port that represents the memory +object data, as supplied to the kernel in a @code{vm_map} call. +@var{memory_control} is the request port to which a response is +requested. (In the event that a memory object has been supplied to more +than one the kernel that has made the request.) @var{offset} is the +offset within a memory object to which this call refers. This will be +page aligned. @var{length} is the number of bytes of data, starting at +@var{offset}, to which this call refers. This will be an integral +number of memory object pages. @var{desired_access} a protection value +describing the memory access modes which must be permitted on the +specified cached data. One or more of: @code{VM_PROT_READ}, +@code{VM_PROT_WRITE} or @code{VM_PROT_EXECUTE}. + +The function should return @code{KERN_SUCCESS}, but since this routine +is called by the kernel, which does not wait for a reply message, this +value is ignored. +@end deftypefun + + +@node Memory Object Attributes +@section Memory Object Attributes + +@deftypefun kern_return_t memory_object_get_attributes (@w{memory_object_control_t @var{memory_control}}, @w{boolean_t *@var{object_ready}}, @w{boolean_t *@var{may_cache_object}}, @w{memory_object_copy_strategy_t *@var{copy_strategy}}) +The function @code{memory_object_get_attribute} retrieves the current +attributes associated with the memory object. + +The argument @var{memory_control} is the port, provided by the kernel in +a @code{memory_object_init} call, to which cache management requests may +be issued. If @var{object_ready} is set, the kernel may issue new data +and unlock requests on the associated memory object. If +@var{may_cache_object} is set, the kernel may keep data associated with +this memory object, even after virtual memory references to it are gone. +@var{copy_strategy} tells how the kernel should copy regions of the +associated memory object. + +This routine does not receive a reply message (and consequently has no +return value), so only message transmission errors apply. +@end deftypefun + +@deftypefun kern_return_t memory_object_change_attributes (@w{memory_object_control_t @var{memory_control}}, @w{boolean_t @var{may_cache_object}}, @w{memory_object_copy_strategy_t @var{copy_strategy}}, @w{mach_port_t @var{reply_to}}) +The function @code{memory_object_change_attribute} informs the kernel +that the memory manager is ready to receive data or unlock requests on +behalf of the clients and sets performance-related attributes for the +specified memory object. If the caching attribute is asserted, the +kernel is permitted (and encouraged) to maintain cached data for this +memory object even after no virtual address space contains this data. + +There are three possible caching strategies: +@code{MEMORY_OBJECT_COPY_NONE} which specifies that nothing special +should be done when data in the object is copied; +@code{MEMORY_OBJECT_COPY_CALL} which specifies that the memory manager +should be notified via a @code{memory_object_copy} call before any part +of the object is copied; and @code{MEMORY_OBJECT_COPY_DELAY} which +guarantees that the memory manager does not externally modify the data +so that the kernel can use its normal copy-on-write algorithms. +@code{MEMORY_OBJECT_COPY_DELAY} is the strategy most commonly used. + +The argument @var{memory_control} is the port, provided by the kernel in +a @code{memory_object_init} call, to which cache management requests may +be issued. If @var{may_cache_object} is set, the kernel may keep data +associated with this memory object, even after virtual memory references +to it are gone. @var{copy_strategy} tells how the kernel should copy +regions of the associated memory object. @var{reply_to} is a port on +which a @code{memory_object_change_completed} call will be issued upon +completion of the attribute change, or @code{MACH_PORT_NULL} if no +acknowledgement is desired. + +This routine does not receive a reply message (and consequently has no +return value), so only message transmission errors apply. +@end deftypefun + +@deftypefun kern_return_t memory_object_change_completed (@w{memory_object_t @var{memory_object}}, @w{boolean_t @var{may_cache_object}}, @w{memory_object_copy_strategy_t @var{copy_strategy}}) +@deftypefunx kern_return_t seqnos_memory_object_change_completed (@w{memory_object_t @var{memory_object}}, @w{mach_port_seqno_t @var{seqno}}, @w{boolean_t @var{may_cache_object}}, @w{memory_object_copy_strategy_t @var{copy_strategy}}) +The function @code{memory_object_change_completed} indicates the +completion of an attribute change call. + +@c Warning: This routine does NOT contain a memory_object_control_t because +@c the memory_object_change_attributes call may cause memory object +@c termination (by uncaching the object). This would yield an invalid +@c port. +@end deftypefun + + +@node Default Memory Manager +@section Default Memory Manager + +@deftypefun kern_return_t vm_set_default_memory_manager (@w{host_t @var{host}}, @w{mach_port_t *@var{default_manager}}) +The function @code{vm_set_default_memory_manager} sets the kernel's +default memory manager. It sets the port to which newly-created +temporary memory objects are delivered by @code{memory_object_create} to +the host. The old memory manager port is returned. If +@var{default_manager} is @code{MACH_PORT_NULL} then this routine just returns +the current default manager port without changing it. + +The argument @var{host} is a task port to the kernel whose default +memory manager is to be changed. @var{default_manager} is an in/out +parameter. As input, @var{default_manager} is the port that the new +memory manager is listening on for @code{memory_object_create} calls. +As output, it is the old default memory manager's port. + +The function returns @code{KERN_SUCCESS} if the new memory manager is +installed, and @code{KERN_INVALID_ARGUMENT} if this task does not have +the privileges required for this call. +@end deftypefun + +@deftypefun kern_return_t memory_object_create (@w{memory_object_t @var{old_memory_object}}, @w{memory_object_t @var{new_memory_object}}, @w{vm_size_t @var{new_object_size}}, @w{memory_object_control_t @var{new_control}}, @w{memory_object_name_t @var{new_name}}, @w{vm_size_t @var{new_page_size}}) +@deftypefunx kern_return_t seqnos_memory_object_create (@w{memory_object_t @var{old_memory_object}}, @w{mach_port_seqno_t @var{seqno}}, @w{memory_object_t @var{new_memory_object}}, @w{vm_size_t @var{new_object_size}}, @w{memory_object_control_t @var{new_control}}, @w{memory_object_name_t @var{new_name}}, @w{vm_size_t @var{new_page_size}}) +The function @code{memory_object_create} is a request that the given +memory manager accept responsibility for the given memory object created +by the kernel. This call will only be made to the system +@strong{default memory manager}. The memory object in question +initially consists of zero-filled memory; only memory pages that are +actually written will ever be provided to +@code{memory_object_data_request} calls, the default memory manager must +use @code{memory_object_data_unavailable} for any pages that have not +previously been written. + +No reply is expected after this call. Since this call is directed to +the default memory manager, the kernel assumes that it will be ready to +handle data requests to this object and does not need the confirmation +of a @code{memory_object_ready} call. + +The argument @var{old_memory_object} is a memory object provided by the +default memory manager on which the kernel can make +@code{memory_object_create} calls. @var{new_memory_object} is a new +memory object created by the kernel; see synopsis for further +description. Note that all port rights (including receive rights) are +included for the new memory object. @var{new_object_size} is the +maximum size of the new object. @var{new_control} is a port, created by +the kernel, on which a memory manager may issue cache management +requests for the new object. @var{new_name} a port used by the kernel +to refer to the new memory object data in response to @code{vm_region} +calls. @var{new_page_size} is the page size to be used by this kernel. +All data sizes in calls involving this kernel must be an integral +multiple of the page size. Note that different kernels, indicated by +different a @code{memory_control}, may have different page sizes. + +The function should return @code{KERN_SUCCESS}, but since this routine +is called by the kernel, which does not wait for a reply message, this +value is ignored. +@end deftypefun + +@deftypefun kern_return_t memory_object_data_initialize (@w{memory_object_t @var{memory_object}}, @w{memory_object_control_t @var{memory_control}}, @w{vm_offset_t @var{offset}}, @w{vm_offset_t @var{data}}, @w{vm_size_t @var{data_count}}) +@deftypefunx kern_return_t seqnos_memory_object_data_initialize (@w{memory_object_t @var{memory_object}}, @w{mach_port_seqno_t @var{seqno}}, @w{memory_object_control_t @var{memory_control}}, @w{vm_offset_t @var{offset}}, @w{vm_offset_t @var{data}}, @w{vm_size_t @var{data_count}}) +The function @code{memory_object_data_initialize} provides the memory +manager with initial data for a kernel-created memory object. If the +memory manager already has been supplied data (by a previous +@code{memory_object_data_initialize}, or +@code{memory_object_data_return}), then this data should be ignored. +Otherwise, this call behaves exactly as does +@code{memory_object_data_return} on memory objects created by the kernel +via @code{memory_object_create} and thus will only be made to default +memory managers. This call will not be made on objects created via +@code{memory_object_copy}. + +The argument @var{memory_object} the port that represents the memory +object data, as supplied by the kernel in a @code{memory_object_create} +call. @var{memory_control} is the request port to which a response is +requested. (In the event that a memory object has been supplied to more +than one the kernel that has made the request.) @var{offset} is the +offset within a memory object to which this call refers. This will be +page aligned. @var{data} is the data which has been modified while +cached in physical memory. @var{data_count} is the amount of data to be +written, in bytes. This will be an integral number of memory object +pages. + +The function should return @code{KERN_SUCCESS}, but since this routine +is called by the kernel, which does not wait for a reply message, this +value is ignored. +@end deftypefun + + +@node Threads and Tasks +@chapter Threads and Tasks + +@menu +* Thread Interface:: Manipulating threads. +* Task Interface:: Manipulating tasks. +* Profiling:: Profiling threads and tasks. +@end menu + + +@node Thread Interface +@section Thread Interface + +@cindex thread port +@cindex port representing a thread +@deftp {Data type} thread_t +This is a @code{mach_port_t} and used to hold the port name of a +thread port that represents the thread. Manipulations of the thread are +implemented as remote procedure calls to the thread port. A thread can +get a port to itself with the @code{mach_thread_self} system call. +@end deftp + +@menu +* Thread Creation:: Creating new threads. +* Thread Termination:: Terminating existing threads. +* Thread Information:: How to get informations on threads. +* Thread Settings:: How to set threads related informations. +* Thread Execution:: How to control the thread's machine state. +* Scheduling:: Operations on thread scheduling. +* Thread Special Ports:: How to handle the thread's special ports. +* Exceptions:: Managing exceptions. +@end menu + + +@node Thread Creation +@subsection Thread Creation + +@deftypefun kern_return_t thread_create (@w{task_t @var{parent_task}}, @w{thread_t *@var{child_thread}}) +The function @code{thread_create} creates a new thread within the task +specified by @var{parent_task}. The new thread has no processor state, +and has a suspend count of 1. To get a new thread to run, first +@code{thread_create} is called to get the new thread's identifier, +(@var{child_thread}). Then @code{thread_set_state} is called to set a +processor state, and finally @code{thread_resume} is called to get the +thread scheduled to execute. + +When the thread is created send rights to its thread kernel port are +given to it and returned to the caller in @var{child_thread}. The new +thread's exception port is set to @code{MACH_PORT_NULL}. + +The function returns @code{KERN_SUCCESS} if a new thread has been +created, @code{KERN_INVALID_ARGUMENT} if @var{parent_task} is not a +valid task and @code{KERN_RESOURCE_SHORTAGE} if some critical kernel +resource is not available. +@end deftypefun + + +@node Thread Termination +@subsection Thread Termination + +@deftypefun kern_return_t thread_terminate (@w{thread_t @var{target_thread}}) +The function @code{thread_terminate} destroys the thread specified by +@var{target_thread}. + +The function returns @code{KERN_SUCCESS} if the thread has been killed +and @code{KERN_INVALID_ARGUMENT} if @var{target_thread} is not a thread. +@end deftypefun + + +@node Thread Information +@subsection Thread Information + +@deftypefun thread_t mach_thread_self () +The @code{mach_thread_self} system call returns the calling thread's +thread port. + +@code{mach_thread_self} has an effect equivalent to receiving a send +right for the thread port. @code{mach_thread_self} returns the name of +the send right. In particular, successive calls will increase the +calling task's user-reference count for the send right. + +@c author{marcus} +As a special exception, the kernel will overrun the user reference count +of the thread name port, so that this function can not fail for that +reason. Because of this, the user should not deallocate the port right +if an overrun might have happened. Otherwise the reference count could +drop to zero and the send right be destroyed while the user still +expects to be able to use it. As the kernel does not make use of the +number of extant send rights anyway, this is safe to do (the thread port +itself is not destroyed, even when there are no send rights anymore). + +The function returns @code{MACH_PORT_NULL} if a resource shortage +prevented the reception of the send right or if the thread port is +currently null and @code{MACH_PORT_DEAD} if the thread port is currently +dead. +@end deftypefun + +@deftypefun kern_return_t thread_info (@w{thread_t @var{target_thread}}, @w{int @var{flavor}}, @w{thread_info_t @var{thread_info}}, @w{mach_msg_type_number_t *@var{thread_infoCnt}}) +The function @code{thread_info} returns the selected information array +for a thread, as specified by @var{flavor}. + +@var{thread_info} is an array of integers that is supplied by the caller +and returned filled with specified information. @var{thread_infoCnt} is +supplied as the maximum number of integers in @var{thread_info}. On +return, it contains the actual number of integers in @var{thread_info}. +The maximum number of integers returned by any flavor is +@code{THREAD_INFO_MAX}. + +The type of information returned is defined by @var{flavor}, which can +be one of the following: + +@table @code +@item THREAD_BASIC_INFO +The function returns basic information about the thread, as defined by +@code{thread_basic_info_t}. This includes the user and system time, the +run state, and scheduling priority. The number of integers returned is +@code{THREAD_BASIC_INFO_COUNT}. + +@item THREAD_SCHED_INFO +The function returns information about the scheduling policy for the +thread as defined by @code{thread_sched_info_t}. The number of integers +returned is @code{THREAD_SCHED_INFO_COUNT}. +@end table + +The function returns @code{KERN_SUCCESS} if the call succeeded and +@code{KERN_INVALID_ARGUMENT} if @var{target_thread} is not a thread or +@var{flavor} is not recognized. The function returns +@code{MIG_ARRAY_TOO_LARGE} if the returned info array is too large for +@var{thread_info}. In this case, @var{thread_info} is filled as much as +possible and @var{thread_infoCnt} is set to the number of elements that +would have been returned if there were enough room. +@end deftypefun + +@deftp {Data type} {struct thread_basic_info} +This structure is returned in @var{thread_info} by the +@code{thread_info} function and provides basic information about the +thread. You can cast a variable of type @code{thread_info_t} to a +pointer of this type if you provided it as the @var{thread_info} +parameter for the @code{THREAD_BASIC_INFO} flavor of @code{thread_info}. +It has the following members: + +@table @code +@item time_value_t user_time +user run time + +@item time_value_t system_time +system run time +@item int cpu_usage +Scaled cpu usage percentage. The scale factor is @code{TH_USAGE_SCALE}. + +@item int base_priority +The base scheduling priority of the thread. + +@item int cur_priority +The current scheduling priority of the thread. + +@item integer_t run_state +The run state of the thread. The possible values of this field are: +@table @code +@item TH_STATE_RUNNING +The thread is running normally. + +@item TH_STATE_STOPPED +The thread is suspended. + +@item TH_STATE_WAITING +The thread is waiting normally. + +@item TH_STATE_UNINTERRUPTIBLE +The thread is in an uninterruptible wait. + +@item TH_STATE_HALTED +The thread is halted at a clean point. +@end table + +@item flags +Various flags. The possible values of this field are: +@table @code +@item TH_FLAGS_SWAPPED +The thread is swapped out. + +@item TH_FLAGS_IDLE +The thread is an idle thread. +@end table + +@item int suspend_count +The suspend count for the thread. + +@item int sleep_time +The number of seconds that the thread has been sleeping. + +@item time_value_t creation_time +The time stamp of creation. +@end table +@end deftp + +@deftp {Data type} thread_basic_info_t +This is a pointer to a @code{struct thread_basic_info}. +@end deftp + +@deftp {Data type} {struct thread_sched_info} +This structure is returned in @var{thread_info} by the +@code{thread_info} function and provides schedule information about the +thread. You can cast a variable of type @code{thread_info_t} to a +pointer of this type if you provided it as the @var{thread_info} +parameter for the @code{THREAD_SCHED_INFO} flavor of @code{thread_info}. +It has the following members: + +@table @code +@item int policy +The scheduling policy of the thread, @ref{Scheduling Policy}. + +@item integer_t data +Policy-dependent scheduling information, @ref{Scheduling Policy}. + +@item int base_priority +The base scheduling priority of the thread. + +@item int max_priority +The maximum scheduling priority of the thread. + +@item int cur_priority +The current scheduling priority of the thread. + +@item int depressed +@code{TRUE} if the thread is depressed. + +@item int depress_priority +The priority the thread was depressed from. + +@item int last_processor +The last processor used by the thread. +@end table +@end deftp + +@deftp {Data type} thread_sched_info_t +This is a pointer to a @code{struct thread_sched_info}. +@end deftp + +@deftypefun kern_return_t thread_set_name (@w{thread_t @var{target_thread}}, @w{const_kernel_debug_name_t @var{name}}) + +The function @code{thread_set_name} sets the name of @var{target_thread} +to @var{name}, truncating it if necessary. + +This is a debugging aid. The name is used in diagnostic messages +printed by the kernel. + +The function returns @code{KERN_SUCCESS} if the call succeeded. +@end deftypefun + + +@node Thread Settings +@subsection Thread Settings + +@deftypefun kern_return_t thread_wire (@w{host_priv_t @var{host_priv}}, @w{thread_t @var{thread}}, @w{boolean_t @var{wired}}) +The function @code{thread_wire} controls the VM privilege level of the +thread @var{thread}. A VM-privileged thread never waits inside the +kernel for memory allocation from the kernel's free list of pages or for +allocation of a kernel stack. + +Threads that are part of the default pageout path should be +VM-privileged, to prevent system deadlocks. Threads that are not part +of the default pageout path should not be VM-privileged, to prevent the +kernel's free list of pages from being exhausted. + +The functions returns @code{KERN_SUCCESS} if the call succeeded, +@code{KERN_INVALID_ARGUMENT} if @var{host_priv} or @var{thread} was +invalid. + +The @code{thread_wire} call is actually an RPC to @var{host_priv}, +normally a send right for a privileged host port, but potentially any +send right. In addition to the normal diagnostic return codes from the +call's server (normally the kernel), the call may return @code{mach_msg} +return codes. +@c See also: vm_wire(2), vm_set_default_memory_manager(2). +@end deftypefun + + +@node Thread Execution +@subsection Thread Execution + +@deftypefun kern_return_t thread_suspend (@w{thread_t @var{target_thread}}) +Increments the thread's suspend count and prevents the thread from +executing any more user level instructions. In this context a user +level instruction is either a machine instruction executed in user mode +or a system trap instruction including page faults. Thus if a thread is +currently executing within a system trap the kernel code may continue to +execute until it reaches the system return code or it may suspend within +the kernel code. In either case, when the thread is resumed the system +trap will return. This could cause unpredictable results if the user +did a suspend and then altered the user state of the thread in order to +change its direction upon a resume. The call @code{thread_abort} is +provided to allow the user to abort any system call that is in progress +in a predictable way. + +The suspend count may become greater than one with the effect that it +will take more than one resume call to restart the thread. + +The function returns @code{KERN_SUCCESS} if the thread has been +suspended and @code{KERN_INVALID_ARGUMENT} if @var{target_thread} is not +a thread. +@end deftypefun + +@deftypefun kern_return_t thread_resume (@w{thread_t @var{target_thread}}) +Decrements the thread's suspend count. If the count becomes zero the +thread is resumed. If it is still positive, the thread is left +suspended. The suspend count may not become negative. + +The function returns @code{KERN_SUCCESS} if the thread has been resumed, +@code{KERN_FAILURE} if the suspend count is already zero and +@code{KERN_INVALID_ARGUMENT} if @var{target_thread} is not a thread. +@end deftypefun + +@deftypefun kern_return_t thread_abort (@w{thread_t @var{target_thread}}) +The function @code{thread_abort} aborts the kernel primitives: +@code{mach_msg}, @code{msg_send}, @code{msg_receive} and @code{msg_rpc} +and page-faults, making the call return a code indicating that it was +interrupted. The call is interrupted whether or not the thread (or task +containing it) is currently suspended. If it is suspended, the thread +receives the interrupt when it is resumed. + +A thread will retry an aborted page-fault if its state is not modified +before it is resumed. @code{msg_send} returns @code{SEND_INTERRUPTED}; +@code{msg_receive} returns @code{RCV_INTERRUPTED}; @code{msg_rpc} +returns either @code{SEND_INTERRUPTED} or @code{RCV_INTERRUPTED}, +depending on which half of the RPC was interrupted. + +The main reason for this primitive is to allow one thread to cleanly +stop another thread in a manner that will allow the future execution of +the target thread to be controlled in a predictable way. +@code{thread_suspend} keeps the target thread from executing any further +instructions at the user level, including the return from a system call. +@code{thread_get_state}/@code{thread_set_state} allows the examination +or modification of the user state of a target thread. However, if a +suspended thread was executing within a system call, it also has +associated with it a kernel state. This kernel state can not be +modified by @code{thread_set_state} with the result that when the thread +is resumed the system call may return changing the user state and +possibly user memory. @code{thread_abort} aborts the kernel call from +the target thread's point of view by resetting the kernel state so that +the thread will resume execution at the system call return with the +return code value set to one of the interrupted codes. The system call +itself will either be entirely completed or entirely aborted, depending +on the precise moment at which the abort was received. Thus if the +thread's user state has been changed by @code{thread_set_state}, it will +not be modified by any unexpected system call side effects. + +For example to simulate a Unix signal, the following sequence of calls +may be used: + +@enumerate +@item +@code{thread_suspend}: Stops the thread. + +@item +@code{thread_abort}: Interrupts any system call in progress, setting the +return value to `interrupted'. Since the thread is stopped, it will not +return to user code. + +@item +@code{thread_set_state}: Alters thread's state to simulate a procedure +call to the signal handler + +@item +@code{thread_resume}: Resumes execution at the signal handler. If the +thread's stack has been correctly set up, the thread may return to the +interrupted system call. (Of course, the code to push an extra stack +frame and change the registers is VERY machine-dependent.) +@end enumerate + +Calling @code{thread_abort} on a non-suspended thread is pretty risky, +since it is very difficult to know exactly what system trap, if any, the +thread might be executing and whether an interrupt return would cause +the thread to do something useful. + +The function returns @code{KERN_SUCCESS} if the thread received an +interrupt and @code{KERN_INVALID_ARGUMENT} if @var{target_thread} is not +a thread. +@end deftypefun + +@deftypefun kern_return_t thread_get_state (@w{thread_t @var{target_thread}}, @w{int @var{flavor}}, @w{thread_state_t @var{old_state}}, @w{mach_msg_type_number_t *@var{old_stateCnt}}) +The function @code{thread_get_state} returns the execution state +(e.g. the machine registers) of @var{target_thread} as specified by +@var{flavor}. The @var{old_state} is an array of integers that is +provided by the caller and returned filled with the specified +information. @var{old_stateCnt} is input set to the maximum number of +integers in @var{old_state} and returned equal to the actual number of +integers in @var{old_state}. + +@var{target_thread} may not be @code{mach_thread_self()}. + +The definition of the state structures can be found in +@file{machine/thread_status.h}. + +The function returns @code{KERN_SUCCESS} if the state has been returned, +@code{KERN_INVALID_ARGUMENT} if @var{target_thread} is not a thread or +is @code{mach_thread_self} or @var{flavor} is unrecognized for this machine. +The function returns @code{MIG_ARRAY_TOO_LARGE} if the returned state is +too large for @var{old_state}. In this case, @var{old_state} is filled +as much as possible and @var{old_stateCnt} is set to the number of +elements that would have been returned if there were enough room. +@end deftypefun + +@deftypefun kern_return_t thread_set_state (@w{thread_t @var{target_thread}}, @w{int @var{flavor}}, @w{thread_state_t @var{new_state}}, @w{mach_msg_type_number_t @var{new_state_count}}) +The function @code{thread_set_state} sets the execution state (e.g. the +machine registers) of @var{target_thread} as specified by @var{flavor}. +The @var{new_state} is an array of integers. @var{new_state_count} is +the number of elements in @var{new_state}. The entire set of registers +is reset. This will do unpredictable things if @var{target_thread} is +not suspended. + +@var{target_thread} may not be @code{mach_thread_self}. + +The definition of the state structures can be found in +@file{machine/thread_status.h}. + +The function returns @code{KERN_SUCCESS} if the state has been set and +@code{KERN_INVALID_ARGUMENT} if @var{target_thread} is not a thread or +is @code{mach_thread_self} or @var{flavor} is unrecognized for this +machine. +@end deftypefun + + +@node Scheduling +@subsection Scheduling + +@menu +* Thread Priority:: Changing the priority of a thread. +* Hand-Off Scheduling:: Switching to a new thread. +* Scheduling Policy:: Setting the scheduling policy. +@end menu + + +@node Thread Priority +@subsubsection Thread Priority + +Threads have three priorities associated with them by the system, a +priority, a maximum priority, and a scheduled priority. The scheduled +priority is used to make scheduling decisions about the thread. It is +determined from the priority by the policy (for timesharing, this means +adding an increment derived from cpu usage). The priority can be set +under user control, but may never exceed the maximum priority. Changing +the maximum priority requires presentation of the control port for the +thread's processor set; since the control port for the default processor +set is privileged, users cannot raise their maximum priority to unfairly +compete with other users on that set. Newly created threads obtain +their priority from their task and their max priority from the thread. + +@deftypefun kern_return_t thread_priority (@w{thread_t @var{thread}}, @w{int @var{prority}}, @w{boolean_t @var{set_max}}) +The function @code{thread_priority} changes the priority and optionally +the maximum priority of @var{thread}. Priorities range from 0 to 49, +where lower numbers denote higher priorities. If the new priority is +higher than the priority of the current thread, preemption may occur as +a result of this call. The maximum priority of the thread is also set +if @var{set_max} is @code{TRUE}. This call will fail if @var{priority} +is greater than the current maximum priority of the thread. As a +result, this call can only lower the value of a thread's maximum +priority. + +The functions returns @code{KERN_SUCCESS} if the operation completed +successfully, @code{KERN_INVALID_ARGUMENT} if @var{thread} is not a +thread or @var{priority} is out of range (not in 0..49), and +@code{KERN_FAILURE} if the requested operation would violate the +thread's maximum priority (thread_priority). +@end deftypefun + +@deftypefun kern_return_t thread_max_priority (@w{thread_t @var{thread}}, @w{processor_set_t @var{processor_set}}, @w{int @var{priority}}) +The function @code{thread_max_priority} changes the maximum priority of +the thread. Because it requires presentation of the corresponding +processor set port, this call can reset the maximum priority to any +legal value. + +The functions returns @code{KERN_SUCCESS} if the operation completed +successfully, @code{KERN_INVALID_ARGUMENT} if @var{thread} is not a +thread or @var{processor_set} is not a control port for a processor set +or @var{priority} is out of range (not in 0..49), and +@code{KERN_FAILURE} if the thread is not assigned to the processor set +whose control port was presented. +@end deftypefun + + +@node Hand-Off Scheduling +@subsubsection Hand-Off Scheduling + +@deftypefun kern_return_t thread_switch (@w{thread_t @var{new_thread}}, @w{int @var{option}}, @w{int @var{time}}) +The function @code{thread_switch} provides low-level access to the +scheduler's context switching code. @var{new_thread} is a hint that +implements hand-off scheduling. The operating system will attempt to +switch directly to the new thread (bypassing the normal logic that +selects the next thread to run) if possible. Since this is a hint, it +may be incorrect; it is ignored if it doesn't specify a thread on the +same host as the current thread or if that thread can't be switched to +(i.e., not runnable or already running on another processor or giving +a plainly invalid hint, such as @code{MACH_PORT_NULL}). In this case, +the normal logic to select the next thread to run is used; the current +thread may continue running if there is no other appropriate thread to +run. + +Options for @var{option} are defined in @file{mach/thread_switch.h} and +specify the interpretation of @var{time}. The possible values for +@var{option} are: + +@table @code +@item SWITCH_OPTION_NONE +No options, the time argument is ignored. + +@item SWITCH_OPTION_WAIT +The thread is blocked for the specified time (in milliseconds; +specifying @code{0} will wait for the next tick). This can be aborted +by @code{thread_abort}. + +@item SWITCH_OPTION_DEPRESS +The thread's priority is depressed to the lowest possible value for the +specified time. This can be aborted by @code{thread_depress_abort}. +This depression is independent of operations that change the thread's +priority (e.g. @code{thread_priority} will not abort the depression). +The minimum time and units of time can be obtained as the +@code{min_timeout} value from @code{host_info}. The depression is also +aborted when the current thread is next run (either via hand-off +scheduling or because the processor set has nothing better to do). +@end table + +@code{thread_switch} is often called when the current thread can proceed +no further for some reason; the various options and arguments allow +information about this reason to be transmitted to the kernel. The +@var{new_thread} argument (handoff scheduling) is useful when the +identity of the thread that must make progress before the current thread +runs again is known. The @code{WAIT} option is used when the amount of +time that the current thread must wait before it can do anything useful +can be estimated and is fairly long. The @code{DEPRESS} option is used +when the amount of time that must be waited is fairly short, especially +when the identity of the thread that is being waited for is not known. + +Users should beware of calling @code{thread_switch} with an invalid hint +(e.g. @code{MACH_PORT_NULL}) and no option. Because the time-sharing +scheduler varies the priority of threads based on usage, this may result +in a waste of cpu time if the thread that must be run is of lower +priority. The use of the @code{DEPRESS} option in this situation is +highly recommended. + +@code{thread_switch} ignores policies. Users relying on the preemption +semantics of a fixed time policy should be aware that +@code{thread_switch} ignores these semantics; it will run the specified +@var{new_thread} independent of its priority and the priority of any other +threads that could be run instead. + +The function returns @code{KERN_SUCCESS} if the call succeeded, +@code{KERN_INVALID_ARGUMENT} if @var{thread} is not a thread or +@var{option} is not a recognized option, and @code{KERN_FAILURE} if +@code{kern_depress_abort} failed because the thread was not depressed. +@end deftypefun + +@deftypefun kern_return_t thread_depress_abort (@w{thread_t @var{thread}}) +The function @code{thread_depress_abort} cancels any priority depression +for @var{thread} caused by a @code{swtch_pri} or @code{thread_switch} +call. + +The function returns @code{KERN_SUCCESS} if the call succeeded and +@code{KERN_INVALID_ARGUMENT} if @var{thread} is not a valid thread. +@end deftypefun + +@deftypefun boolean_t swtch () +@c XXX Clear up wording. +The system trap @code{swtch} attempts to switch the current thread off +the processor. The return value indicates if more than the current +thread is running in the processor set. This is useful for lock +management routines. + +The call returns @code{FALSE} if the thread is justified in becoming a +resource hog by continuing to spin because there's nothing else useful +that the processor could do. @code{TRUE} is returned if the thread +should make one more check on the lock and then be a good citizen and +really suspend. +@end deftypefun + +@deftypefun boolean_t swtch_pri (@w{int @var{priority}}) +The system trap @code{swtch_pri} attempts to switch the current thread +off the processor as @code{swtch} does, but depressing the priority of +the thread to the minimum possible value during the time. +@var{priority} is not used currently. + +The return value is as for @code{swtch}. +@end deftypefun + + +@node Scheduling Policy +@subsubsection Scheduling Policy + +@deftypefun kern_return_t thread_policy (@w{thread_t @var{thread}}, @w{int @var{policy}}, @w{int @var{data}}) +The function @code{thread_policy} changes the scheduling policy for +@var{thread} to @var{policy}. + +@var{data} is policy-dependent scheduling information. There are +currently two supported policies: @code{POLICY_TIMESHARE} and +@code{POLICY_FIXEDPRI} defined in @file{mach/policy.h}; this file is +included by @file{mach.h}. @var{data} is meaningless for timesharing, +but is the quantum to be used (in milliseconds) for the fixed priority +policy. To be meaningful, this quantum must be a multiple of the basic +system quantum (min_quantum) which can be obtained from +@code{host_info}. The system will always round up to the next multiple +of the quantum. + +Processor sets may restrict the allowed policies, so this call will fail +if the processor set to which @var{thread} is currently assigned does +not permit @var{policy}. + +The function returns @code{KERN_SUCCESS} if the call succeeded. +@code{KERN_INVALID_ARGUMENT} if @var{thread} is not a thread or +@var{policy} is not a recognized policy, and @code{KERN_FAILURE} if the +processor set to which @var{thread} is currently assigned does not +permit @var{policy}. +@end deftypefun + + +@node Thread Special Ports +@subsection Thread Special Ports + +@deftypefun kern_return_t thread_get_special_port (@w{thread_t @var{thread}}, @w{int @var{which_port}}, @w{mach_port_t *@var{special_port}}) +The function @code{thread_get_special_port} returns send rights to one +of a set of special ports for the thread specified by @var{thread}. + +The possible values for @var{which_port} are @code{THREAD_KERNEL_PORT} +and @code{THREAD_EXCEPTION_PORT}. A thread also has access to its +task's special ports. + +The function returns @code{KERN_SUCCESS} if the port was returned and +@code{KERN_INVALID_ARGUMENT} if @var{thread} is not a thread or +@var{which_port} is an invalid port selector. +@end deftypefun + +@deftypefun kern_return_t thread_get_kernel_port (@w{thread_t @var{thread}}, @w{mach_port_t *@var{kernel_port}}) +The function @code{thread_get_kernel_port} is equivalent to the function +@code{thread_get_special_port} with the @var{which_port} argument set to +@code{THREAD_KERNEL_PORT}. +@end deftypefun + +@deftypefun kern_return_t thread_get_exception_port (@w{thread_t @var{thread}}, @w{mach_port_t *@var{exception_port}}) +The function @code{thread_get_exception_port} is equivalent to the +function @code{thread_get_special_port} with the @var{which_port} +argument set to @code{THREAD_EXCEPTION_PORT}. +@end deftypefun + +@deftypefun kern_return_t thread_set_special_port (@w{thread_t @var{thread}}, @w{int @var{which_port}}, @w{mach_port_t @var{special_port}}) +The function @code{thread_set_special_port} sets one of a set of special +ports for the thread specified by @var{thread}. + +The possible values for @var{which_port} are @code{THREAD_KERNEL_PORT} +and @code{THREAD_EXCEPTION_PORT}. A thread also has access to its +task's special ports. + +The function returns @code{KERN_SUCCESS} if the port was set and +@code{KERN_INVALID_ARGUMENT} if @var{thread} is not a thread or +@var{which_port} is an invalid port selector. +@end deftypefun + +@deftypefun kern_return_t thread_set_kernel_port (@w{thread_t @var{thread}}, @w{mach_port_t @var{kernel_port}}) +The function @code{thread_set_kernel_port} is equivalent to the function +@code{thread_set_special_port} with the @var{which_port} argument set to +@code{THREAD_KERNEL_PORT}. +@end deftypefun + +@deftypefun kern_return_t thread_set_exception_port (@w{thread_t @var{thread}}, @w{mach_port_t @var{exception_port}}) +The function @code{thread_set_exception_port} is equivalent to the +function @code{thread_set_special_port} with the @var{which_port} +argument set to @code{THREAD_EXCEPTION_PORT}. +@end deftypefun + + +@node Exceptions +@subsection Exceptions + +@deftypefun kern_return_t catch_exception_raise (@w{mach_port_t @var{exception_port}}, @w{thread_t @var{thread}}, @w{task_t @var{task}}, @w{int @var{exception}}, @w{int @var{code}}, @w{long @var{subcode}}) +XXX Fixme +@end deftypefun + +@deftypefun kern_return_t exception_raise (@w{mach_port_t @var{exception_port}}, @w{mach_port_t @var{thread}}, @w{mach_port_t @var{task}}, @w{integer_t @var{exception}}, @w{integer_t @var{code}}, @w{long_integer_t @var{subcode}}) +XXX Fixme +@end deftypefun + +@deftypefun kern_return_t evc_wait (@w{unsigned int @var{event}}) +@c XXX This is for user space drivers, the description is incomplete. +The system trap @code{evc_wait} makes the calling thread wait for the +event specified by @var{event}. + +The call returns @code{KERN_SUCCESS} if the event has occurred, +@code{KERN_NO_SPACE} if another thread is waiting for the same event and +@code{KERN_INVALID_ARGUMENT} if the event object is invalid. +@end deftypefun + + +@node Task Interface +@section Task Interface + +@cindex task port +@cindex port representing a task +@deftp {Data type} task_t +This is a @code{mach_port_t} and used to hold the port name of a task +port that represents the thread. Manipulations of the task are +implemented as remote procedure calls to the task port. A task can get +a port to itself with the @code{mach_task_self} system call. + +The task port name is also used to identify the task's IPC space +(@pxref{Port Manipulation Interface}) and the task's virtual memory map +(@pxref{Virtual Memory Interface}). +@end deftp + +@menu +* Task Creation:: Creating tasks. +* Task Termination:: Terminating tasks. +* Task Information:: Informations on tasks. +* Task Execution:: Thread scheduling in a task. +* Task Special Ports:: How to get and set the task's special ports. +* Syscall Emulation:: How to emulate system calls. +@end menu + + +@node Task Creation +@subsection Task Creation + +@deftypefun kern_return_t task_create (@w{task_t @var{parent_task}}, @w{boolean_t @var{inherit_memory}}, @w{task_t *@var{child_task}}) +The function @code{task_create} creates a new task from +@var{parent_task}; the resulting task (@var{child_task}) acquires shared +or copied parts of the parent's address space (see @code{vm_inherit}). +The child task initially contains no threads. + +If @var{inherit_memory} is set, the child task's address space is built +from the parent task according to its memory inheritance values; +otherwise, the child task is given an empty address space. + +The child task gets the three special ports created or copied for it at +task creation. The @code{TASK_KERNEL_PORT} is created and send rights +for it are given to the child and returned to the caller. +@c The following is only relevant if MACH_IPC_COMPAT is used. +@c The @code{TASK_NOTIFY_PORT} is created and receive, ownership and send rights +@c for it are given to the child. The caller has no access to it. +The @code{TASK_BOOTSTRAP_PORT} and the @code{TASK_EXCEPTION_PORT} are +inherited from the parent task. The new task can get send rights to +these ports with the call @code{task_get_special_port}. + +The function returns @code{KERN_SUCCESS} if a new task has been created, +@code{KERN_INVALID_ARGUMENT} if @var{parent_task} is not a valid task +port and @code{KERN_RESOURCE_SHORTAGE} if some critical kernel resource +is unavailable. +@end deftypefun + + +@node Task Termination +@subsection Task Termination + +@deftypefun kern_return_t task_terminate (@w{task_t @var{target_task}}) +The function @code{task_terminate} destroys the task specified by +@var{target_task} and all its threads. All resources that are used only +by this task are freed. Any port to which this task has receive and +ownership rights is destroyed. + +The function returns @code{KERN_SUCCESS} if the task has been killed, +@code{KERN_INVALID_ARGUMENT} if @var{target_task} is not a task. +@end deftypefun + + +@node Task Information +@subsection Task Information +@deftypefun task_t mach_task_self () +The @code{mach_task_self} system call returns the calling thread's task +port. + +@code{mach_task_self} has an effect equivalent to receiving a send right +for the task port. @code{mach_task_self} returns the name of the send +right. In particular, successive calls will increase the calling task's +user-reference count for the send right. + +As a special exception, the kernel will overrun the user reference count +of the task name port, so that this function can not fail for that +reason. Because of this, the user should not deallocate the port right +if an overrun might have happened. Otherwise the reference count could +drop to zero and the send right be destroyed while the user still +expects to be able to use it. As the kernel does not make use of the +number of extant send rights anyway, this is safe to do (the task port +itself is not destroyed, even when there are no send rights anymore). + +The function returns @code{MACH_PORT_NULL} if a resource shortage +prevented the reception of the send right, @code{MACH_PORT_NULL} if the +task port is currently null, @code{MACH_PORT_DEAD} if the task port is +currently dead. +@end deftypefun + +@deftypefun kern_return_t task_threads (@w{task_t @var{target_task}}, @w{thread_array_t *@var{thread_list}}, @w{mach_msg_type_number_t *@var{thread_count}}) +The function @code{task_threads} gets send rights to the kernel port for +each thread contained in @var{target_task}. @var{thread_list} is an +array that is created as a result of this call. The caller may wish to +@code{vm_deallocate} this array when the data is no longer needed. + +The function returns @code{KERN_SUCCESS} if the call succeeded and +@code{KERN_INVALID_ARGUMENT} if @var{target_task} is not a task. +@end deftypefun + +@deftypefun kern_return_t task_info (@w{task_t @var{target_task}}, @w{int @var{flavor}}, @w{task_info_t @var{task_info}}, @w{mach_msg_type_number_t *@var{task_info_count}}) +The function @code{task_info} returns the selected information array for +a task, as specified by @var{flavor}. @var{task_info} is an array of +integers that is supplied by the caller, and filled with specified +information. @var{task_info_count} is supplied as the maximum number of +integers in @var{task_info}. On return, it contains the actual number +of integers in @var{task_info}. The maximum number of integers returned +by any flavor is @code{TASK_INFO_MAX}. + +The type of information returned is defined by @var{flavor}, which can +be one of the following: + +@table @code +@item TASK_BASIC_INFO +The function returns basic information about the task, as defined by +@code{task_basic_info_t}. This includes the user and system time and +memory consumption. The number of integers returned is +@code{TASK_BASIC_INFO_COUNT}. + +@item TASK_EVENTS_INFO +The function returns information about events for the task as defined by +@code{thread_sched_info_t}. This includes statistics about virtual +memory and IPC events like pageouts, pageins and messages sent and +received. The number of integers returned is +@code{TASK_EVENTS_INFO_COUNT}. + +@item TASK_THREAD_TIMES_INFO +The function returns information about the total time for live threads +as defined by @code{task_thread_times_info_t}. The number of integers +returned is @code{TASK_THREAD_TIMES_INFO_COUNT}. +@end table + +The function returns @code{KERN_SUCCESS} if the call succeeded and +@code{KERN_INVALID_ARGUMENT} if @var{target_task} is not a thread or +@var{flavor} is not recognized. The function returns +@code{MIG_ARRAY_TOO_LARGE} if the returned info array is too large for +@var{task_info}. In this case, @var{task_info} is filled as much as +possible and @var{task_infoCnt} is set to the number of elements that +would have been returned if there were enough room. +@end deftypefun + +@deftp {Data type} {struct task_basic_info} +This structure is returned in @var{task_info} by the @code{task_info} +function and provides basic information about the task. You can cast a +variable of type @code{task_info_t} to a pointer of this type if you +provided it as the @var{task_info} parameter for the +@code{TASK_BASIC_INFO} flavor of @code{task_info}. It has the following +members: + +@table @code +@item integer_t suspend_count +suspend count for task + +@item integer_t base_priority +base scheduling priority + +@item rpc_vm_size_t virtual_size +number of virtual pages + +@item rpc_vm_size_t resident_size +number of resident pages + +@item time_value_t user_time +total user run time for terminated threads + +@item time_value_t system_time +total system run time for terminated threads + +@item time_value_t creation_time +creation time stamp +@end table +@end deftp + +@deftp {Data type} task_basic_info_t +This is a pointer to a @code{struct task_basic_info}. +@end deftp + +@deftp {Data type} {struct task_events_info} +This structure is returned in @var{task_info} by the @code{task_info} +function and provides event statistics for the task. You can cast a +variable of type @code{task_info_t} to a pointer of this type if you +provided it as the @var{task_info} parameter for the +@code{TASK_EVENTS_INFO} flavor of @code{task_info}. It has the +following members: + +@table @code +@item rpc_long_natural_t faults +number of page faults + +@item rpc_long_natural_t zero_fills +number of zero fill pages + +@item rpc_long_natural_t reactivations +number of reactivated pages + +@item rpc_long_natural_t pageins +number of actual pageins + +@item rpc_long_natural_t cow_faults +number of copy-on-write faults + +@item rpc_long_natural_t messages_sent +number of messages sent + +@item rpc_long_natural_t messages_received +number of messages received +@end table +@end deftp + +@deftp {Data type} task_events_info_t +This is a pointer to a @code{struct task_events_info}. +@end deftp + +@deftp {Data type} {struct task_thread_times_info} +This structure is returned in @var{task_info} by the @code{task_info} +function and provides event statistics for the task. You can cast a +variable of type @code{task_info_t} to a pointer of this type if you +provided it as the @var{task_info} parameter for the +@code{TASK_THREAD_TIMES_INFO} flavor of @code{task_info}. It has the +following members: + +@table @code +@item time_value_t user_time +total user run time for live threads + +@item time_value_t system_time +total system run time for live threads +@end table +@end deftp + +@deftp {Data type} task_thread_times_info_t +This is a pointer to a @code{struct task_thread_times_info}. +@end deftp + +@deftypefun kern_return_t task_set_name (@w{task_t @var{target_task}}, @w{const_kernel_debug_name_t @var{name}}) + +The function @code{task_set_name} sets the name of @var{target_task} +to @var{name}, truncating it if necessary. + +This is a debugging aid. The name is used in diagnostic messages +printed by the kernel. + +The function returns @code{KERN_SUCCESS} if the call succeeded. +@end deftypefun + +@deftypefun kern_return_t task_set_essential (@w{task_t @var{target_task}}, @w{boolean_t @var{essential}}) + +The function @code{task_set_essential} sets whether @var{target_task} is +essential for the system, i.e. the system will completely crash and reboot if +that task crashes. This means that when the debugger is enabled, it should be +triggered on the crash, so as to get the opportunity to debug the issue instead +of just rebooting. + +The function returns @code{KERN_SUCCESS} if the call succeeded. +@end deftypefun + + +@node Task Execution +@subsection Task Execution + +@deftypefun kern_return_t task_suspend (@w{task_t @var{target_task}}) +The function @code{task_suspend} increments the task's suspend count and +stops all threads in the task. As long as the suspend count is positive +newly created threads will not run. This call does not return until all +threads are suspended. + +The count may become greater than one, with the effect that it will take +more than one resume call to restart the task. + +The function returns @code{KERN_SUCCESS} if the task has been suspended +and @code{KERN_INVALID_ARGUMENT} if @var{target_task} is not a task. +@end deftypefun + +@deftypefun kern_return_t task_resume (@w{task_t @var{target_task}}) +The function @code{task_resume} decrements the task's suspend count. If +it becomes zero, all threads with zero suspend counts in the task are +resumed. The count may not become negative. + +The function returns @code{KERN_SUCCESS} if the task has been resumed, +@code{KERN_FAILURE} if the suspend count is already at zero and +@code{KERN_INVALID_ARGUMENT} if @var{target_task} is not a task. +@end deftypefun + +@c XXX Should probably be in the "Scheduling" node of the Thread Interface. +@deftypefun kern_return_t task_priority (@w{task_t @var{task}}, @w{int @var{priority}}, @w{boolean_t @var{change_threads}}) +The priority of a task is used only for creation of new threads; a new +thread's priority is set to the enclosing task's priority. +@code{task_priority} changes this task priority. It also sets the +priorities of all threads in the task to this new priority if +@var{change_threads} is @code{TRUE}. Existing threads are not affected +otherwise. If this priority change violates the maximum priority of +some threads, as many threads as possible will be changed and an error +code will be returned. + +The function returns @code{KERN_SUCCESS} if the call succeeded, +@code{KERN_INVALID_ARGUMENT} if @var{task} is not a task, or +@var{priority} is not a valid priority and @code{KERN_FAILURE} if +@var{change_threads} was @code{TRUE} and the attempt to change the +priority of at least one existing thread failed because the new priority +would have exceeded that thread's maximum priority. +@end deftypefun + +@deftypefun kern_return_t task_ras_control (@w{task_t @var{target_task}}, @w{vm_address_t @var{start_pc}}, @w{vm_address_t @var{end_pc}}, @w{int @var{flavor}}) +The function @code{task_ras_control} manipulates a task's set of +restartable atomic sequences. If a sequence is installed, and any +thread in the task is preempted within the range +[@var{start_pc},@var{end_pc}], then the thread is resumed at +@var{start_pc}. This enables applications to build atomic sequences +which, when executed to completion, will have executed atomically. +Restartable atomic sequences are intended to be used on systems that do +not have hardware support for low-overhead atomic primitives. + +As a thread can be rolled-back, the code in the sequence should have no +side effects other than a final store at @var{end_pc}. The kernel does +not guarantee that the sequence is restartable. It assumes the +application knows what it's doing. + +A task may have a finite number of atomic sequences that is defined at +compile time. + +The flavor specifies the particular operation that should be applied to +this restartable atomic sequence. Possible values for flavor can be: + +@table @code +@item TASK_RAS_CONTROL_PURGE_ALL +Remove all registered sequences for this task. + +@item TASK_RAS_CONTROL_PURGE_ONE +Remove the named registered sequence for this task. + +@item TASK_RAS_CONTROL_PURGE_ALL_AND_INSTALL_ONE +Atomically remove all registered sequences and install the named +sequence. + +@item TASK_RAS_CONTROL_INSTALL_ONE +Install this sequence. +@end table + +The function returns @code{KERN_SUCCESS} if the operation has been +performed, @code{KERN_INVALID_ADDRESS} if the @var{start_pc} or +@var{end_pc} values are not a valid address for the requested operation +(for example, it is invalid to purge a sequence that has not been +registered), @code{KERN_RESOURCE_SHORTAGE} if an attempt was made to +install more restartable atomic sequences for a task than can be +supported by the kernel, @code{KERN_INVALID_VALUE} if a bad flavor was +specified, @code{KERN_INVALID_ARGUMENT} if @var{target_task} is not a +task and @code{KERN_FAILURE} if the call is not not supported on this +configuration. +@end deftypefun + + +@node Task Special Ports +@subsection Task Special Ports + +@deftypefun kern_return_t task_get_special_port (@w{task_t @var{task}}, @w{int @var{which_port}}, @w{mach_port_t *@var{special_port}}) +The function @code{task_get_special_port} returns send rights to one of +a set of special ports for the task specified by @var{task}. + +The special ports associated with a task are the kernel port +(@code{TASK_KERNEL_PORT}), the bootstrap port +(@code{TASK_BOOTSTRAP_PORT}) and the exception port +(@code{TASK_EXCEPTION_PORT}). The bootstrap port is a port to which a +task may send a message requesting other system service ports. This +port is not used by the kernel. The task's exception port is the port +to which messages are sent by the kernel when an exception occurs and +the thread causing the exception has no exception port of its own. + +The following macros to call @code{task_get_special_port} for a specific +port are defined in @code{mach/task_special_ports.h}: +@code{task_get_exception_port} and @code{task_get_bootstrap_port}. + +The function returns @code{KERN_SUCCESS} if the port was returned and +@code{KERN_INVALID_ARGUMENT} if @var{task} is not a task or +@var{which_port} is an invalid port selector. +@end deftypefun + +@deftypefun kern_return_t task_get_kernel_port (@w{task_t @var{task}}, @w{mach_port_t *@var{kernel_port}}) +The function @code{task_get_kernel_port} is equivalent to the function +@code{task_get_special_port} with the @var{which_port} argument set to +@code{TASK_KERNEL_PORT}. +@end deftypefun + +@deftypefun kern_return_t task_get_exception_port (@w{task_t @var{task}}, @w{mach_port_t *@var{exception_port}}) +The function @code{task_get_exception_port} is equivalent to the +function @code{task_get_special_port} with the @var{which_port} argument +set to @code{TASK_EXCEPTION_PORT}. +@end deftypefun + +@deftypefun kern_return_t task_get_bootstrap_port (@w{task_t @var{task}}, @w{mach_port_t *@var{bootstrap_port}}) +The function @code{task_get_bootstrap_port} is equivalent to the +function @code{task_get_special_port} with the @var{which_port} argument +set to @code{TASK_BOOTSTRAP_PORT}. +@end deftypefun + +@deftypefun kern_return_t task_set_special_port (@w{task_t @var{task}}, @w{int @var{which_port}}, @w{mach_port_t @var{special_port}}) +The function @code{thread_set_special_port} sets one of a set of special +ports for the task specified by @var{task}. + +The special ports associated with a task are the kernel port +(@code{TASK_KERNEL_PORT}), the bootstrap port +(@code{TASK_BOOTSTRAP_PORT}) and the exception port +(@code{TASK_EXCEPTION_PORT}). The bootstrap port is a port to which a +thread may send a message requesting other system service ports. This +port is not used by the kernel. The task's exception port is the port +to which messages are sent by the kernel when an exception occurs and +the thread causing the exception has no exception port of its own. + +The function returns @code{KERN_SUCCESS} if the port was set and +@code{KERN_INVALID_ARGUMENT} if @var{task} is not a task or +@var{which_port} is an invalid port selector. +@end deftypefun + +@deftypefun kern_return_t task_set_kernel_port (@w{task_t @var{task}}, @w{mach_port_t @var{kernel_port}}) +The function @code{task_set_kernel_port} is equivalent to the function +@code{task_set_special_port} with the @var{which_port} argument set to +@code{TASK_KERNEL_PORT}. +@end deftypefun + +@deftypefun kern_return_t task_set_exception_port (@w{task_t @var{task}}, @w{mach_port_t @var{exception_port}}) +The function @code{task_set_exception_port} is equivalent to the +function @code{task_set_special_port} with the @var{which_port} argument +set to @code{TASK_EXCEPTION_PORT}. +@end deftypefun + +@deftypefun kern_return_t task_set_bootstrap_port (@w{task_t @var{task}}, @w{mach_port_t @var{bootstrap_port}}) +The function @code{task_set_bootstrap_port} is equivalent to the +function @code{task_set_special_port} with the @var{which_port} argument +set to @code{TASK_BOOTSTRAP_PORT}. +@end deftypefun + + +@node Syscall Emulation +@subsection Syscall Emulation + +@deftypefun kern_return_t task_get_emulation_vector (@w{task_t @var{task}}, @w{int *@var{vector_start}}, @w{emulation_vector_t *@var{emulation_vector}}, @w{mach_msg_type_number_t *@var{emulation_vector_count}}) +The function @code{task_get_emulation_vector} gets the user-level +handler entry points for all emulated system calls. +@c XXX Fixme +@end deftypefun + +@deftypefun kern_return_t task_set_emulation_vector (@w{task_t @var{task}}, @w{int @var{vector_start}}, @w{emulation_vector_t @var{emulation_vector}}, @w{mach_msg_type_number_t @var{emulation_vector_count}}) +The function @code{task_set_emulation_vector} establishes user-level +handlers for the specified system calls. Non-emulated system calls are +specified with an entry of @code{EML_ROUTINE_NULL}. System call +emulation handlers are inherited by the children of @var{task}. +@c XXX Fixme +@end deftypefun + +@deftypefun kern_return_t task_set_emulation (@w{task_t @var{task}}, @w{vm_address_t @var{routine_entry_pt}}, @w{int @var{routine_number}}) +The function @code{task_set_emulation} establishes a user-level handler +for the specified system call. System call emulation handlers are +inherited by the children of @var{task}. +@c XXX Fixme +@end deftypefun + +@c XXX Fixme datatype emulation_vector_t + + +@node Profiling +@section Profiling + +@deftypefun kern_return_t task_enable_pc_sampling (@w{task_t @var{task}}, @w{int *@var{ticks}}, @w{sampled_pc_flavor_t @var{flavor}}) +@deftypefunx kern_return_t thread_enable_pc_sampling (@w{thread_t @var{thread}}, @w{int *@var{ticks}}, @w{sampled_pc_flavor_t @var{flavor}}) +The function @code{task_enable_pc_sampling} enables PC sampling for +@var{task}, the function @code{thread_enable_pc_sampling} enables PC +sampling for @var{thread}. The kernel's idea of clock granularity is +returned in @var{ticks} in usecs. (this value should not be trusted). The +sampling flavor is specified by @var{flavor}. + +The function returns @code{KERN_SUCCESS} if the operation is completed successfully +and @code{KERN_INVALID_ARGUMENT} if @var{thread} is not a valid thread. +@end deftypefun + +@deftypefun kern_return_t task_disable_pc_sampling (@w{task_t @var{task}}, @w{int *@var{sample_count}}) +@deftypefunx kern_return_t thread_disable_pc_sampling (@w{thread_t @var{thread}}, @w{int *@var{sample_count}}) +The function @code{task_disable_pc_sampling} disables PC sampling for +@var{task}, the function @code{thread_disable_pc_sampling} disables PC +sampling for @var{thread}. The number of sample elements in the kernel +for the thread is returned in @var{sample_count}. + +The function returns @code{KERN_SUCCESS} if the operation is completed successfully +and @code{KERN_INVALID_ARGUMENT} if @var{thread} is not a valid thread. +@end deftypefun + +@deftypefun kern_return_t task_get_sampled_pcs (@w{task_t @var{task}}, @w{sampled_pc_seqno_t *@var{seqno}}, @w{sampled_pc_array_t @var{sampled_pcs}}, @w{mach_msg_type_number_t *@var{sample_count}}) +@deftypefunx kern_return_t thread_get_sampled_pcs (@w{thread_t @var{thread}}, @w{sampled_pc_seqno_t *@var{seqno}}, @w{sampled_pc_array_t @var{sampled_pcs}}, @w{int *@var{sample_count}}) +The function @code{task_get_sampled_pcs} extracts the PC samples for +@var{task}, the function @code{thread_get_sampled_pcs} extracts the PC +samples for @var{thread}. @var{seqno} is the sequence number of the +sampled PCs. This is useful for determining when a collector thread has +missed a sample. The sampled PCs for the thread are returned in +@var{sampled_pcs}. @var{sample_count} contains the number of sample +elements returned. + +The function returns @code{KERN_SUCCESS} if the operation is completed successfully, +@code{KERN_INVALID_ARGUMENT} if @var{thread} is not a valid thread and +@code{KERN_FAILURE} if @var{thread} is not sampled. +@end deftypefun + + +@deftp {Data type} sampled_pc_t +This structure is returned in @var{sampled_pcs} by the +@code{thread_get_sampled_pcs} and @code{task_get_sampled_pcs} functions +and provides pc samples for threads or tasks. It has the following +members: + +@table @code +@item natural_t id +A thread-specific unique identifier. + +@item vm_offset_t pc +A pc value. + +@item sampled_pc_flavor_t sampletype +The type of the sample as per flavor. +@end table +@end deftp + + +@deftp {Data type} sampled_pc_flavor_t +This data type specifies a pc sample flavor, either as argument passed +in @var{flavor} to the @code{thread_enable_pc_sample} and +@code{thread_disable_pc_sample} functions, or as member +@code{sampletype} in the @code{sample_pc_t} data type. The flavor is a +bitwise-or of the possible flavors defined in @file{mach/pc_sample.h}: + +@table @code +@item SAMPLED_PC_PERIODIC +default +@item SAMPLED_PC_VM_ZFILL_FAULTS +zero filled fault +@item SAMPLED_PC_VM_REACTIVATION_FAULTS +reactivation fault +@item SAMPLED_PC_VM_PAGEIN_FAULTS +pagein fault +@item SAMPLED_PC_VM_COW_FAULTS +copy-on-write fault +@item SAMPLED_PC_VM_FAULTS_ANY +any fault +@item SAMPLED_PC_VM_FAULTS +the bitwise-or of @code{SAMPLED_PC_VM_ZFILL_FAULTS}, +@code{SAMPLED_PC_VM_REACTIVATION_FAULTS}, +@code{SAMPLED_PC_VM_PAGEIN_FAULTS} and @code{SAMPLED_PC_VM_COW_FAULTS}. +@end table +@end deftp + +@c XXX sampled_pc_array_t, sampled_pc_seqno_t + + +@node Host Interface +@chapter Host Interface +@cindex host interface + +This section describes the Mach interface to a host executing a Mach +kernel. The interface allows to query statistics about a host and +control its behaviour. + +A host is represented by two ports, a name port @var{host} used to query +information about the host accessible to everyone, and a control port +@var{host_priv} used to manipulate it. For example, you can query the +current time using the name port, but to change the time you need to +send a message to the host control port. + +Everything described in this section is declared in the header file +@file{mach.h}. + +@menu +* Host Ports:: Ports representing a host. +* Host Information:: Retrieval of information about a host. +* Host Time:: Operations on the time as seen by a host. +* Host Reboot:: Rebooting the system. +@end menu + + +@node Host Ports +@section Host Ports +@cindex host ports +@cindex ports representing a host + +@cindex host name port +@deftp {Data type} host_t +This is a @code{mach_port_t} and used to hold the port name of a host +name port (or short: host port). Any task can get a send right to the +name port of the host running the task using the @code{mach_host_self} +system call. The name port can be used query information about the +host, for example the current time. +@end deftp + +@deftypefun host_t mach_host_self () +The @code{mach_host_self} system call returns the calling thread's host +name port. It has an effect equivalent to receiving a send right for +the host port. @code{mach_host_self} returns the name of the send +right. In particular, successive calls will increase the calling task's +user-reference count for the send right. + +As a special exception, the kernel will overrun the user reference count +of the host name port, so that this function can not fail for that +reason. Because of this, the user should not deallocate the port right +if an overrun might have happened. Otherwise the reference count could +drop to zero and the send right be destroyed while the user still +expects to be able to use it. As the kernel does not make use of the +number of extant send rights anyway, this is safe to do (the host port +itself is never destroyed). + +The function returns @code{MACH_PORT_NULL} if a resource shortage +prevented the reception of the send right. + +This function is also available in @file{mach/mach_traps.h}. +@end deftypefun + +@cindex host control port +@deftp {Data type} host_priv_t +This is a @code{mach_port_t} and used to hold the port name of a +privileged host control port. A send right to the host control port is +inserted into the first task at bootstrap (@pxref{Modules}). This is +the only way to get access to the host control port in Mach, so the +initial task has to preserve the send right carefully, moving a copy of +it to other privileged tasks if necessary and denying access to +unprivileged tasks. +@end deftp + + +@node Host Information +@section Host Information + +@deftypefun kern_return_t host_info (@w{host_t @var{host}}, @w{int @var{flavor}}, @w{host_info_t @var{host_info}}, @w{mach_msg_type_number_t *@var{host_info_count}}) +The @code{host_info} function returns various information about +@var{host}. @var{host_info} is an array of integers that is supplied by +the caller. It will be filled with the requested information. +@var{host_info_count} is supplied as the maximum number of integers in +@var{host_info}. On return, it contains the actual number of integers +in @var{host_info}. The maximum number of integers returned by any +flavor is @code{HOST_INFO_MAX}. + +The type of information returned is defined by @var{flavor}, which can +be one of the following: + +@table @code +@item HOST_BASIC_INFO +The function returns basic information about the host, as defined by +@code{host_basic_info_t}. This includes the number of processors, their +type, and the amount of memory installed in the system. The number of +integers returned is @code{HOST_BASIC_INFO_COUNT}. For how to get more +information about the processor, see @ref{Processor Interface}. + +@item HOST_PROCESSOR_SLOTS +The function returns the numbers of the slots with active processors in +them. The number of integers returned can be up to @code{max_cpus}, as +returned by the @code{HOST_BASIC_INFO} flavor of @code{host_info}. + +@item HOST_SCHED_INFO +The function returns information of interest to schedulers as defined by +@code{host_sched_info_t}. The number of integers returned is +@code{HOST_SCHED_INFO_COUNT}. +@end table + +The function returns @code{KERN_SUCCESS} if the call succeeded and +@code{KERN_INVALID_ARGUMENT} if @var{host} is not a host or @var{flavor} +is not recognized. The function returns @code{MIG_ARRAY_TOO_LARGE} if +the returned info array is too large for @var{host_info}. In this case, +@var{host_info} is filled as much as possible and @var{host_info_count} +is set to the number of elements that would be returned if there were +enough room. +@c BUGS Availability limited. Systems without this call support a +@c host_info call with an incompatible calling sequence. +@end deftypefun + +@deftp {Data type} {struct host_basic_info} +A pointer to this structure is returned in @var{host_info} by the +@code{host_info} function and provides basic information about the host. +You can cast a variable of type @code{host_info_t} to a pointer of this +type if you provided it as the @var{host_info} parameter for the +@code{HOST_BASIC_INFO} flavor of @code{host_info}. It has the following +members: + +@table @code +@item int max_cpus +The maximum number of possible processors for which the kernel is +configured. + +@item int avail_cpus +The number of cpus currently available. + +@item vm_size_t memory_size +The size of physical memory in bytes. + +@item cpu_type_t cpu_type +The type of the master processor. + +@item cpu_subtype_t cpu_subtype +The subtype of the master processor. +@end table + +The type and subtype of the individual processors are also available +by @code{processor_info}, see @ref{Processor Interface}. +@end deftp + +@deftp {Data type} host_basic_info_t +This is a pointer to a @code{struct host_basic_info}. +@end deftp + +@deftp {Data type} {struct host_sched_info} +A pointer to this structure is returned in @var{host_info} by the +@code{host_info} function and provides information of interest to +schedulers. You can cast a variable of type @code{host_info_t} to a +pointer of this type if you provided it as the @var{host_info} parameter +for the @code{HOST_SCHED_INFO} flavor of @code{host_info}. It has the +following members: + +@table @code +@item int min_timeout +The minimum timeout and unit of time in milliseconds. + +@item int min_quantum +The minimum quantum and unit of quantum in milliseconds. +@end table +@end deftp + +@deftp {Data type} host_sched_info_t +This is a pointer to a @code{struct host_sched_info}. +@end deftp + +@deftypefun kern_return_t host_get_kernel_version (@w{host_t @var{host}}, @w{kernel_version_t *@var{version}}) +The @code{host_get_kernel_version} function returns the version string +compiled into the kernel executing on @var{host} at the time it was +built in the character string @var{version}. This string describes the +version of the kernel. The constant @code{KERNEL_VERSION_MAX} should be +used to dimension storage for the returned string if the +@code{kernel_version_t} declaration is not used. + +If the version string compiled into the kernel is longer than +@code{KERNEL_VERSION_MAX}, the result is truncated and not necessarily +null-terminated. + +If @var{host} is not a valid send right to a host port, the function +returns @code{KERN_INVALID_ARGUMENT}. If @var{version} points to +inaccessible memory, it returns @code{KERN_INVALID_ADDRESS}, and +@code{KERN_SUCCESS} otherwise. +@end deftypefun + +@node Host Time +@section Host Time + +@deftp {Data type} time_value64_t +This is the representation of a time in Mach. It is a @code{struct +time_value64} and consists of the following members: + +@table @code +@item int64_t seconds +The number of seconds. +@item int64_t nanoseconds +The number of nanoseconds. +@end table +@end deftp + +The number of nanoseconds should always be smaller than +@code{TIME_NANOS_MAX} (100000000). A time with this property is +@dfn{normalized}. Normalized time values can be manipulated with the +following macros: + +@defmac time_value64_add_nanos (@w{time_value64_t *@var{val}}, @w{int64_t *@var{nanos}}) +Add @var{nanos} nanoseconds to @var{val}. If @var{val} is normalized +and @var{nanos} smaller than @code{TIME_NANOS_MAX}, @var{val} will be +normalized afterwards. +@end defmac + +@defmac time_value64_add (@w{time_value64_t *@var{result}}, @w{time_value64_t *@var{addend}}) +Add the values in @var{addend} to @var{result}. If both are normalized, +@var{result} will be normalized afterwards. +@end defmac + +A variable of type @code{time_value64_t} can either represent a duration +or a fixed point in time. In the latter case, it shall be interpreted as +the number of seconds and nanoseconds after the epoch 1. Jan 1970. + +@deftypefun kern_return_t host_get_time64 (@w{host_t @var{host}}, @w{time_value64_t *@var{current_time}}) +Get the current time as seen by @var{host}. On success, the time passed +since the epoch is returned in @var{current_time}. +@end deftypefun + +@deftypefun kern_return_t host_set_time64 (@w{host_priv_t @var{host_priv}}, @w{time_value64_t @var{new_time}}) +Set the time of @var{host_priv} to @var{new_time}. +@end deftypefun + +@deftypefun kern_return_t host_adjust_time64 (@w{host_priv_t @var{host_priv}}, @w{time_value64_t @var{new_adjustment}}, @w{time_value64_t *@var{old_adjustment}}) +Arrange for the current time as seen by @var{host_priv} to be gradually +changed by the adjustment value @var{new_adjustment}, and return the old +adjustment value in @var{old_adjustment}. +@end deftypefun + +For efficiency, the current time is available through a mapped-time +interface. + +@deftp {Data type} mapped_time_value_t +This structure defines the mapped-time interface. It has the following +members: + +@table @code +@item integer_t seconds +The number of seconds. + +@item integer_t microseconds +The number of microseconds. + +@item integer_t check_seconds +This is a copy of the seconds value, which must be checked to protect +against a race condition when reading out the two time values. This +should only be used when getting the 32 bit version of @code{time_value64_t}. + +@item time_value64_t time_value +The current time. + +@item int64_t check_seconds64 +This is a copy of the seconds value in @var{time_value}, which must be checked to protect +against a race condition when reading out the two time values. +@end table +@end deftp + +Here is an example how to read out the current time using the +mapped-time interface: + +@c XXX Complete the example. +@example +do + @{ + secs = mtime->time_value.seconds; + __sync_synchronize(); + nanos = mtime->time_value.nanoseconds; + __sync_synchronize(); + @} +while (secs != mtime->check_seconds64); +@end example + + +@node Host Reboot +@section Host Reboot + +@deftypefun kern_return_t host_reboot (@w{host_priv_t @var{host_priv}}, @w{int @var{options}}) +Reboot the host specified by @var{host_priv}. The argument +@var{options} specifies the flags. The available flags are defined in +@file{sys/reboot.h}: + +@table @code +@item RB_HALT +Do not reboot, but halt the machine. + +@item RB_DEBUGGER +Do not reboot, but enter kernel debugger from user space. +@end table + +If successful, the function might not return. +@end deftypefun + + +@node Processors and Processor Sets +@chapter Processors and Processor Sets + +This section describes the Mach interface to processor sets and +individual processors. The interface allows to group processors into +sets and control the processors and processor sets. + +A processor is not a central part of the interface. It is mostly of +relevance as a part of a processor set. Threads are always assigned to +processor sets, and all processors in a set are equally involved in +executing all threads assigned to that set. + +The processor set is represented by two ports, a name port +@var{processor_set_name} used to query information about the host +accessible to everyone, and a control port @var{processor_set} used to +manipulate it. + +@menu +* Processor Set Interface:: How to work with processor sets. +* Processor Interface:: How to work with individual processors. +@end menu + + +@node Processor Set Interface +@section Processor Set Interface + +@menu +* Processor Set Ports:: Ports representing a processor set. +* Processor Set Access:: How the processor sets are accessed. +* Processor Set Creation:: How new processor sets are created. +* Processor Set Destruction:: How processor sets are destroyed. +* Tasks and Threads on Sets:: Assigning tasks, threads to processor sets. +* Processor Set Priority:: Specifying the priority of a processor set. +* Processor Set Policy:: Changing the processor set policies. +* Processor Set Info:: Obtaining information about a processor set. +@end menu + + +@node Processor Set Ports +@subsection Processor Set Ports +@cindex processor set ports +@cindex ports representing a processor set + +@cindex processor set name port +@cindex port representing a processor set name +@deftp {Data type} processor_set_name_t +This is a @code{mach_port_t} and used to hold the port name of a +processor set name port that names the processor set. Any task can get +a send right to name port of a processor set. The processor set name +port allows to get information about the processor set. +@end deftp + +@cindex processor set port +@deftp {Data type} processor_set_t +This is a @code{mach_port_t} and used to hold the port name of a +privileged processor set control port that represents the processor set. +Operations on the processor set are implemented as remote procedure +calls to the processor set port. The processor set port allows to +manipulate the processor set. +@end deftp + + +@node Processor Set Access +@subsection Processor Set Access + +@deftypefun kern_return_t host_processor_sets (@w{host_t @var{host}}, @w{processor_set_name_array_t *@var{processor_sets}}, @w{mach_msg_type_number_t *@var{processor_sets_count}}) +The function @code{host_processor_sets} gets send rights to the name +port for each processor set currently assigned to @var{host}. + +@code{host_processor_set_priv} can be used to obtain the control ports +from these if desired. @var{processor_sets} is an array that is +created as a result of this call. The caller may wish to +@code{vm_deallocate} this array when the data is no longer needed. +@var{processor_sets_count} is set to the number of processor sets in the +@var{processor_sets}. + +This function returns @code{KERN_SUCCESS} if the call succeeded and +@code{KERN_INVALID_ARGUMENT} if @var{host} is not a host. +@end deftypefun + +@deftypefun kern_return_t host_processor_set_priv (@w{host_priv_t @var{host_priv}}, @w{processor_set_name_t @var{set_name}}, @w{processor_set_t *@var{set}}) +The function @code{host_processor_set_priv} allows a privileged +application to obtain the control port @var{set} for an existing +processor set from its name port @var{set_name}. The privileged host +port @var{host_priv} is required. + +This function returns @code{KERN_SUCCESS} if the call succeeded and +@code{KERN_INVALID_ARGUMENT} if @var{host_priv} is not a valid host +control port. +@end deftypefun + +@deftypefun kern_return_t processor_set_default (@w{host_t @var{host}}, @w{processor_set_name_t *@var{default_set}}) +The function @code{processor_set_default} returns the default processor +set of @var{host} in @var{default_set}. The default processor set is +used by all threads, tasks, and processors that are not explicitly +assigned to other sets. processor_set_default returns a port that can +be used to obtain information about this set (e.g. how many threads are +assigned to it). This port cannot be used to perform operations on that +set. + +This function returns @code{KERN_SUCCESS} if the call succeeded, +@code{KERN_INVALID_ARGUMENT} if @var{host} is not a host and +@code{KERN_INVALID_ADDRESS} if @var{default_set} points to +inaccessible memory. +@end deftypefun + + +@node Processor Set Creation +@subsection Processor Set Creation + +@deftypefun kern_return_t processor_set_create (@w{host_t @var{host}}, @w{processor_set_t *@var{new_set}}, @w{processor_set_name_t *@var{new_name}}) +The function @code{processor_set_create} creates a new processor set on +@var{host} and returns the two ports associated with it. The port +returned in @var{new_set} is the actual port representing the set. It +is used to perform operations such as assigning processors, tasks, or +threads. The port returned in @var{new_name} identifies the set, and is +used to obtain information about the set. + +This function returns @code{KERN_SUCCESS} if the call succeeded, +@code{KERN_INVALID_ARGUMENT} if @var{host} is not a host, +@code{KERN_INVALID_ADDRESS} if @var{new_set} or @var{new_name} points to +inaccessible memory and @code{KERN_FAILURE} is the operating system does +not support processor allocation. +@end deftypefun + + +@node Processor Set Destruction +@subsection Processor Set Destruction + +@deftypefun kern_return_t processor_set_destroy (@w{processor_set_t @var{processor_set}}) +The function @code{processor_set_destroy} destroys the specified +processor set. Any assigned processors, tasks, or threads are +reassigned to the default set. The object port for the processor set is +required (not the name port). The default processor set cannot be +destroyed. + +This function returns @code{KERN_SUCCESS} if the set was destroyed, +@code{KERN_FAILURE} if an attempt was made to destroy the default +processor set, or the operating system does not support processor +allocation, and @code{KERN_INVALID_ARGUMENT} if @var{processor_set} is +not a valid processor set control port. +@end deftypefun + + +@node Tasks and Threads on Sets +@subsection Tasks and Threads on Sets + +@deftypefun kern_return_t processor_set_tasks (@w{processor_set_t @var{processor_set}}, @w{task_array_t *@var{task_list}}, @w{mach_msg_type_number_t *@var{task_count}}) +The function @code{processor_set_tasks} gets send rights to the kernel +port for each task currently assigned to @var{processor_set}. + +@var{task_list} is an array that is created as a result of this call. +The caller may wish to @code{vm_deallocate} this array when the data is +no longer needed. @var{task_count} is set to the number of tasks in the +@var{task_list}. + +This function returns @code{KERN_SUCCESS} if the call succeeded and +@code{KERN_INVALID_ARGUMENT} if @var{processor_set} is not a processor +set. +@end deftypefun + +@deftypefun kern_return_t processor_set_threads (@w{processor_set_t @var{processor_set}}, @w{thread_array_t *@var{thread_list}}, @w{mach_msg_type_number_t *@var{thread_count}}) +The function @code{processor_set_thread} gets send rights to the kernel +port for each thread currently assigned to @var{processor_set}. + +@var{thread_list} is an array that is created as a result of this call. +The caller may wish to @code{vm_deallocate} this array when the data is +no longer needed. @var{thread_count} is set to the number of threads in +the @var{thread_list}. + +This function returns @code{KERN_SUCCESS} if the call succeeded and +@code{KERN_INVALID_ARGUMENT} if @var{processor_set} is not a processor +set. +@end deftypefun + +@deftypefun kern_return_t task_assign (@w{task_t @var{task}}, @w{processor_set_t @var{processor_set}}, @w{boolean_t @var{assign_threads}}) +The function @code{task_assign} assigns @var{task} the set +@var{processor_set}. This assignment is for the purposes of determining +the initial assignment of newly created threads in task. Any previous +assignment of the task is nullified. Existing threads within the task +are also reassigned if @var{assign_threads} is @code{TRUE}. They are +not affected if it is @code{FALSE}. + +This function returns @code{KERN_SUCCESS} if the assignment has been +performed and @code{KERN_INVALID_ARGUMENT} if @var{task} is not a task, +or @var{processor_set} is not a processor set on the same host as +@var{task}. +@end deftypefun + +@deftypefun kern_return_t task_assign_default (@w{task_t @var{task}}, @w{boolean_t @var{assign_threads}}) +The function @code{task_assign_default} is a variant of +@code{task_assign} that assigns the task to the default processor set on +that task's host. This variant exists because the control port for the +default processor set is privileged and not usually available to users. + +This function returns @code{KERN_SUCCESS} if the assignment has been +performed and @code{KERN_INVALID_ARGUMENT} if @var{task} is not a task. +@end deftypefun + +@deftypefun kern_return_t task_get_assignment (@w{task_t @var{task}}, @w{processor_set_name_t *@var{assigned_set}}) +The function @code{task_get_assignment} returns the name of the +processor set to which the thread is currently assigned in +@var{assigned_set}. This port can only be used to obtain information +about the processor set. + +This function returns @code{KERN_SUCCESS} if the assignment has been +performed, @code{KERN_INVALID_ADDRESS} if @var{processor_set} points to +inaccessible memory, and @code{KERN_INVALID_ARGUMENT} if @var{task} is +not a task. +@end deftypefun + +@deftypefun kern_return_t thread_assign (@w{thread_t @var{thread}}, @w{processor_set_t @var{processor_set}}) +The function @code{thread_assign} assigns @var{thread} the set +@var{processor_set}. After the assignment is completed, the thread only +executes on processors assigned to the designated processor set. If +there are no such processors, then the thread is unable to execute. Any +previous assignment of the thread is nullified. Unix system call +compatibility code may temporarily force threads to execute on the +master processor. + +This function returns @code{KERN_SUCCESS} if the assignment has been +performed and @code{KERN_INVALID_ARGUMENT} if @var{thread} is not a +thread, or @var{processor_set} is not a processor set on the same host +as @var{thread}. +@end deftypefun + +@deftypefun kern_return_t thread_assign_default (@w{thread_t @var{thread}}) +The function @code{thread_assign_default} is a variant of +@code{thread_assign} that assigns the thread to the default processor +set on that thread's host. This variant exists because the control port +for the default processor set is privileged and not usually available +to users. + +This function returns @code{KERN_SUCCESS} if the assignment has been +performed and @code{KERN_INVALID_ARGUMENT} if @var{thread} is not a +thread. +@end deftypefun + +@deftypefun kern_return_t thread_get_assignment (@w{thread_t @var{thread}}, @w{processor_set_name_t *@var{assigned_set}}) +The function @code{thread_get_assignment} returns the name of the +processor set to which the thread is currently assigned in +@var{assigned_set}. This port can only be used to obtain information +about the processor set. + +This function returns @code{KERN_SUCCESS} if the assignment has been +performed, @code{KERN_INVALID_ADDRESS} if @var{processor_set} points to +inaccessible memory, and @code{KERN_INVALID_ARGUMENT} if @var{thread} is +not a thread. +@end deftypefun + + +@node Processor Set Priority +@subsection Processor Set Priority + +@deftypefun kern_return_t processor_set_max_priority (@w{processor_set_t @var{processor_set}}, @w{int @var{max_priority}}, @w{boolean_t @var{change_threads}}) +The function @code{processor_set_max_priority} is used to set the +maximum priority for a processor set. The priority of a processor set +is used only for newly created threads (thread's maximum priority is set +to processor set's) and the assignment of threads to the set (thread's +maximum priority is reduced if it exceeds the set's maximum priority, +thread's priority is similarly reduced). +@code{processor_set_max_priority} changes this priority. It also sets +the maximum priority of all threads assigned to the processor set to +this new priority if @var{change_threads} is @code{TRUE}. If this +maximum priority is less than the priorities of any of these threads, +their priorities will also be set to this new value. + +This function returns @code{KERN_SUCCESS} if the call succeeded and +@code{KERN_INVALID_ARGUMENT} if @var{processor_set} is not a processor +set or @var{priority} is not a valid priority. +@end deftypefun + + +@node Processor Set Policy +@subsection Processor Set Policy + +@deftypefun kern_return_t processor_set_policy_enable (@w{processor_set_t @var{processor_set}}, @w{int @var{policy}}) +@deftypefunx kern_return_t processor_set_policy_disable (@w{processor_set_t @var{processor_set}}, @w{int @var{policy}}, @w{boolean_t @var{change_threads}}) +Processor sets may restrict the scheduling policies to be used for +threads assigned to them. These two calls provide the mechanism for +designating permitted and forbidden policies. The current set of +permitted policies can be obtained from @code{processor_set_info}. +Timesharing may not be forbidden by any processor set. This is a +compromise to reduce the complexity of the assign operation; any thread +whose policy is forbidden by the target processor set has its policy +reset to timesharing. If the @var{change_threads} argument to +@code{processor_set_policy_disable} is true, threads currently assigned +to this processor set and using the newly disabled policy will have +their policy reset to timesharing. + +@file{mach/policy.h} contains the allowed policies; it is included by +@file{mach.h}. Not all policies (e.g. fixed priority) are supported by +all systems. + +This function returns @code{KERN_SUCCESS} if the operation was completed +successfully and @code{KERN_INVALID_ARGUMENT} if @var{processor_set} is +not a processor set or @var{policy} is not a valid policy, or an attempt +was made to disable timesharing. +@end deftypefun + + +@node Processor Set Info +@subsection Processor Set Info + +@deftypefun kern_return_t processor_set_info (@w{processor_set_name_t @var{set_name}}, @w{int @var{flavor}}, @w{host_t *@var{host}}, @w{processor_set_info_t @var{processor_set_info}}, @w{mach_msg_type_number_t *@var{processor_set_info_count}}) +The function @code{processor_set_info} returns the selected information array +for a processor set, as specified by @var{flavor}. + +@var{host} is set to the host on which the processor set resides. This +is the non-privileged host port. + +@var{processor_set_info} is an array of integers that is supplied by the +caller and returned filled with specified information. +@var{processor_set_info_count} is supplied as the maximum number of +integers in @var{processor_set_info}. On return, it contains the actual +number of integers in @var{processor_set_info}. The maximum number of +integers returned by any flavor is @code{PROCESSOR_SET_INFO_MAX}. + +The type of information returned is defined by @var{flavor}, which can +be one of the following: + +@table @code +@item PROCESSOR_SET_BASIC_INFO +The function returns basic information about the processor set, as +defined by @code{processor_set_basic_info_t}. This includes the number +of tasks and threads assigned to the processor set. The number of +integers returned is @code{PROCESSOR_SET_BASIC_INFO_COUNT}. + +@item PROCESSOR_SET_SCHED_INFO +The function returns information about the scheduling policy for the +processor set as defined by @code{processor_set_sched_info_t}. The +number of integers returned is @code{PROCESSOR_SET_SCHED_INFO_COUNT}. +@end table + +Some machines may define additional (machine-dependent) flavors. + +The function returns @code{KERN_SUCCESS} if the call succeeded and +@code{KERN_INVALID_ARGUMENT} if @var{processor_set} is not a processor +set or @var{flavor} is not recognized. The function returns +@code{MIG_ARRAY_TOO_LARGE} if the returned info array is too large for +@var{processor_set_info}. In this case, @var{processor_set_info} is +filled as much as possible and @var{processor_set_info_count} is set to the +number of elements that would have been returned if there were enough +room. +@end deftypefun + +@deftp {Data type} {struct processor_set_basic_info} +This structure is returned in @var{processor_set_info} by the +@code{processor_set_info} function and provides basic information about +the processor set. You can cast a variable of type +@code{processor_set_info_t} to a pointer of this type if you provided it +as the @var{processor_set_info} parameter for the +@code{PROCESSOR_SET_BASIC_INFO} flavor of @code{processor_set_info}. It +has the following members: + +@table @code +@item int processor_count +number of processors + +@item int task_count +number of tasks + +@item int thread_count +number of threads + +@item int load_average +scaled load average + +@item int mach_factor +scaled mach factor +@end table +@end deftp + +@deftp {Data type} processor_set_basic_info_t +This is a pointer to a @code{struct processor_set_basic_info}. +@end deftp + +@deftp {Data type} {struct processor_set_sched_info} +This structure is returned in @var{processor_set_info} by the +@code{processor_set_info} function and provides schedule information +about the processor set. You can cast a variable of type +@code{processor_set_info_t} to a pointer of this type if you provided it +as the @var{processor_set_info} parameter for the +@code{PROCESSOR_SET_SCHED_INFO} flavor of @code{processor_set_info}. It +has the following members: + +@table @code +@item int policies +allowed policies + +@item int max_priority +max priority for new threads +@end table +@end deftp + +@deftp {Data type} processor_set_sched_info_t +This is a pointer to a @code{struct processor_set_sched_info}. +@end deftp + + +@node Processor Interface +@section Processor Interface + +@cindex processor port +@cindex port representing a processor +@deftp {Data type} processor_t +This is a @code{mach_port_t} and used to hold the port name of a +processor port that represents the processor. Operations on the +processor are implemented as remote procedure calls to the processor +port. +@end deftp + +@menu +* Hosted Processors:: Getting a list of all processors on a host. +* Processor Control:: Starting, stopping, controlling processors. +* Processors and Sets:: Combining processors into processor sets. +* Processor Info:: Obtaining information on processors. +@end menu + + +@node Hosted Processors +@subsection Hosted Processors + +@deftypefun kern_return_t host_processors (@w{host_priv_t @var{host_priv}}, @w{processor_array_t *@var{processor_list}}, @w{mach_msg_type_number_t *@var{processor_count}}) +The function @code{host_processors} gets send rights to the processor +port for each processor existing on @var{host_priv}. This is the +privileged port that allows its holder to control a processor. + +@var{processor_list} is an array that is created as a result of this +call. The caller may wish to @code{vm_deallocate} this array when the +data is no longer needed. @var{processor_count} is set to the number of +processors in the @var{processor_list}. + +This function returns @code{KERN_SUCCESS} if the call succeeded, +@code{KERN_INVALID_ARGUMENT} if @var{host_priv} is not a privileged host +port, and @code{KERN_INVALID_ADDRESS} if @var{processor_count} points to +inaccessible memory. +@end deftypefun + + +@node Processor Control +@subsection Processor Control + +@deftypefun kern_return_t processor_start (@w{processor_t @var{processor}}) +@deftypefunx kern_return_t processor_exit (@w{processor_t @var{processor}}) +@deftypefunx kern_return_t processor_control (@w{processor_t @var{processor}}, @w{processor_info_t *@var{cmd}}, @w{mach_msg_type_number_t @var{count}}) +Some multiprocessors may allow privileged software to control +processors. The @code{processor_start}, @code{processor_exit}, and +@code{processor_control} operations implement this. The interpretation +of the command in @var{cmd} is machine dependent. A newly started +processor is assigned to the default processor set. An exited processor +is removed from the processor set to which it was assigned and ceases to +be active. + +@var{count} contains the length of the command @var{cmd} as a number of +ints. + +Availability limited. All of these operations are machine-dependent. +They may do nothing. The ability to restart an exited processor is also +machine-dependent. + +This function returns @code{KERN_SUCCESS} if the operation was +performed, @code{KERN_FAILURE} if the operation was not performed (a +likely reason is that it is not supported on this processor), +@code{KERN_INVALID_ARGUMENT} if @var{processor} is not a processor, and +@code{KERN_INVALID_ADDRESS} if @var{cmd} points to inaccessible memory. +@end deftypefun + +@node Processors and Sets +@subsection Processors and Sets + +@deftypefun kern_return_t processor_assign (@w{processor_t @var{processor}}, @w{processor_set_t @var{processor_set}}, @w{boolean_t @var{wait}}) +The function @code{processor_assign} assigns @var{processor} to the +set @var{processor_set}. After the assignment is completed, the +processor only executes threads that are assigned to that processor set. +Any previous assignment of the processor is nullified. The master +processor cannot be reassigned. All processors take clock interrupts at +all times. The @var{wait} argument indicates whether the caller should +wait for the assignment to be completed or should return immediately. +Dedicated kernel threads are used to perform processor assignment, so +setting wait to @code{FALSE} allows assignment requests to be queued and +performed faster, especially if the kernel has more than one dedicated +internal thread for processor assignment. Redirection of other device +interrupts away from processors assigned to other than the default +processor set is machine-dependent. Intermediaries that interpose on +ports must be sure to interpose on both ports involved in this call if +they interpose on either. + +This function returns @code{KERN_SUCCESS} if the assignment has been +performed, @code{KERN_INVALID_ARGUMENT} if @var{processor} is not a +processor, or @var{processor_set} is not a processor set on the same +host as @var{processor}. +@end deftypefun + +@deftypefun kern_return_t processor_get_assignment (@w{processor_t @var{processor}}, @w{processor_set_name_t *@var{assigned_set}}) +The function @code{processor_get_assignment} obtains the current +assignment of a processor. The name port of the processor set is +returned in @var{assigned_set}. +@end deftypefun + +@node Processor Info +@subsection Processor Info + +@deftypefun kern_return_t processor_info (@w{processor_t @var{processor}}, @w{int @var{flavor}}, @w{host_t *@var{host}}, @w{processor_info_t @var{processor_info}}, @w{mach_msg_type_number_t *@var{processor_info_count}}) +The function @code{processor_info} returns the selected information array +for a processor, as specified by @var{flavor}. + +@var{host} is set to the host on which the processor set resides. This +is the non-privileged host port. + +@var{processor_info} is an array of integers that is supplied by the +caller and returned filled with specified information. +@var{processor_info_count} is supplied as the maximum number of integers in +@var{processor_info}. On return, it contains the actual number of +integers in @var{processor_info}. The maximum number of integers +returned by any flavor is @code{PROCESSOR_INFO_MAX}. + +The type of information returned is defined by @var{flavor}, which can +be one of the following: + +@table @code +@item PROCESSOR_BASIC_INFO +The function returns basic information about the processor, as defined +by @code{processor_basic_info_t}. This includes the slot number of the +processor. The number of integers returned is +@code{PROCESSOR_BASIC_INFO_COUNT}. +@end table + +Machines which require more configuration information beyond the slot +number are expected to define additional (machine-dependent) flavors. + +The function returns @code{KERN_SUCCESS} if the call succeeded and +@code{KERN_INVALID_ARGUMENT} if @var{processor} is not a processor or +@var{flavor} is not recognized. The function returns +@code{MIG_ARRAY_TOO_LARGE} if the returned info array is too large for +@var{processor_info}. In this case, @var{processor_info} is filled as +much as possible and @var{processor_infoCnt} is set to the number of +elements that would have been returned if there were enough room. +@end deftypefun + +@deftp {Data type} {struct processor_basic_info} +This structure is returned in @var{processor_info} by the +@code{processor_info} function and provides basic information about the +processor. You can cast a variable of type @code{processor_info_t} to a +pointer of this type if you provided it as the @var{processor_info} +parameter for the @code{PROCESSOR_BASIC_INFO} flavor of +@code{processor_info}. It has the following members: + +@table @code +@item cpu_type_t cpu_type +cpu type + +@item cpu_subtype_t cpu_subtype +cpu subtype + +@item boolean_t running +is processor running? + +@item int slot_num +slot number + +@item boolean_t is_master +is this the master processor +@end table +@end deftp + +@deftp {Data type} processor_basic_info_t +This is a pointer to a @code{struct processor_basic_info}. +@end deftp + + +@node Device Interface +@chapter Device Interface + +The GNU Mach microkernel provides a simple device interface that allows +the user space programs to access the underlying hardware devices. Each +device has a unique name, which is a string up to 127 characters long. +To open a device, the device master port has to be supplied. The device +master port is only available through the bootstrap port. Anyone who +has control over the device master port can use all hardware devices. +@c XXX FIXME bootstrap port, bootstrap + +@cindex device port +@cindex port representing a device +@deftp {Data type} device_t +This is a @code{mach_port_t} and used to hold the port name of a +device port that represents the device. Operations on the device are +implemented as remote procedure calls to the device port. Each device +provides a sequence of records. The length of a record is specific to +the device. Data can be transferred ``out-of-line'' or ``in-line'' +(@pxref{Memory}). +@end deftp + +All constants and functions in this chapter are defined in +@file{device/device.h}. + +@menu +* Device Reply Server:: Handling device reply messages. +* Device Open:: Opening hardware devices. +* Device Close:: Closing hardware devices. +* Device Read:: Reading data from the device. +* Device Write:: Writing data to the device. +* Device Map:: Mapping devices into virtual memory. +* Device Status:: Querying and manipulating a device. +* Device Filter:: Filtering packets arriving on a device. +* Device Interrupt:: Getting hardware interrupt notifications. +@end menu + + +@node Device Reply Server +@section Device Reply Server + +Beside the usual synchronous interface, an asynchronous interface is +provided. For this, the caller has to receive and handle the reply +messages separately from the function call. + +@deftypefun boolean_t device_reply_server (@w{msg_header_t *@var{in_msg}}, @w{msg_header_t *@var{out_msg}}) +The function @code{device_reply_server} is produced by the +remote procedure call generator to handle a received message. This +function does all necessary argument handling, and actually calls one of +the following functions: @code{ds_device_open_reply}, +@code{ds_device_read_reply}, @code{ds_device_read_reply_inband}, +@code{ds_device_write_reply} and @code{ds_device_write_reply_inband}. + +The @var{in_msg} argument is the message that has been received from the +kernel. The @var{out_msg} is a reply message, but this is not used for +this server. + +The function returns @code{TRUE} to indicate that the message in +question was applicable to this interface, and that the appropriate +routine was called to interpret the message. It returns @code{FALSE} to +indicate that the message did not apply to this interface, and that no +other action was taken. +@end deftypefun + + +@node Device Open +@section Device Open + +@deftypefun kern_return_t device_open (@w{mach_port_t @var{master_port}}, @w{dev_mode_t @var{mode}}, @w{dev_name_t @var{name}}, @w{device_t *@var{device}}) +The function @code{device_open} opens the device @var{name} and returns +a port to it in @var{device}. The open count for the device is +incremented by one. If the open count was 0, the open handler for the +device is invoked. + +@var{master_port} must hold the master device port. @var{name} +specifies the device to open, and is a string up to 128 characters long. +@var{mode} is the open mode. It is a bitwise-or of the following +constants: + +@table @code +@item D_READ +Request read access for the device. + +@item D_WRITE +Request write access for the device. + +@item D_NODELAY +Do not delay an open. +@c XXX Is this really used at all? Maybe for tape drives? What does it mean? +@end table + +The function returns @code{D_SUCCESS} if the device was successfully +opened, @code{D_INVALID_OPERATION} if @var{master_port} is not the +master device port, @code{D_WOULD_BLOCK} is the device is busy and +@code{D_NOWAIT} was specified in mode, @code{D_ALREADY_OPEN} if the +device is already open in an incompatible mode and +@code{D_NO_SUCH_DEVICE} if @var{name} does not denote a know device. +@end deftypefun + +@deftypefun kern_return_t device_open_request (@w{mach_port_t @var{master_port}}, @w{mach_port_t @var{reply_port}}, @w{dev_mode_t @var{mode}}, @w{dev_name_t @var{name}}) +@deftypefunx kern_return_t ds_device_open_reply (@w{mach_port_t @var{reply_port}}, @w{kern_return_t @var{return}}, @w{device_t *@var{device}}) +This is the asynchronous form of the @code{device_open} function. +@code{device_open_request} performs the open request. The meaning for +the parameters is as in @code{device_open}. Additionally, the caller +has to supply a reply port to which the @code{ds_device_open_reply} +message is sent by the kernel when the open has been performed. The +return value of the open operation is stored in @var{return_code}. + +As neither function receives a reply message, only message transmission +errors apply. If no error occurs, @code{KERN_SUCCESS} is returned. +@end deftypefun + + +@node Device Close +@section Device Close + +@deftypefun kern_return_t device_close (@w{device_t @var{device}}) +The function @code{device_close} decrements the open count of the device +by one. If the open count drops to zero, the close handler for the +device is called. The device to close is specified by its port +@var{device}. + +The function returns @code{D_SUCCESS} if the device was successfully +closed and @code{D_NO_SUCH_DEVICE} if @var{device} does not denote a +device port. +@end deftypefun + + +@node Device Read +@section Device Read + +@deftypefun kern_return_t device_read (@w{device_t @var{device}}, @w{dev_mode_t @var{mode}}, @w{recnum_t @var{recnum}}, @w{int @var{bytes_wanted}}, @w{io_buf_ptr_t *@var{data}}, @w{mach_msg_type_number_t *@var{data_count}}) +The function @code{device_read} reads @var{bytes_wanted} bytes from +@var{device}, and stores them in a buffer allocated with +@code{vm_allocate}, which address is returned in @var{data}. The caller +must deallocated it if it is no longer needed. The number of bytes +actually returned is stored in @var{data_count}. + +If @var{mode} is @code{D_NOWAIT}, the operation does not block. +Otherwise @var{mode} should be 0. @var{recnum} is the record number to +be read, its meaning is device specific. + +The function returns @code{D_SUCCESS} if some data was successfully +read, @code{D_WOULD_BLOCK} if no data is currently available and +@code{D_NOWAIT} is specified, and @code{D_NO_SUCH_DEVICE} if +@var{device} does not denote a device port. +@end deftypefun + +@deftypefun kern_return_t device_read_inband (@w{device_t @var{device}}, @w{dev_mode_t @var{mode}}, @w{recnum_t @var{recnum}}, @w{int @var{bytes_wanted}}, @w{io_buf_ptr_inband_t *@var{data}}, @w{mach_msg_type_number_t *@var{data_count}}) +The @code{device_read_inband} function works as the @code{device_read} +function, except that the data is returned ``in-line'' in the reply IPC +message (@pxref{Memory}). +@end deftypefun + +@deftypefun kern_return_t device_read_request (@w{device_t @var{device}}, @w{mach_port_t @var{reply_port}}, @w{dev_mode_t @var{mode}}, @w{recnum_t @var{recnum}}, @w{int @var{bytes_wanted}}) +@deftypefunx kern_return_t ds_device_read_reply (@w{mach_port_t @var{reply_port}}, @w{kern_return_t @var{return_code}}, @w{io_buf_ptr_t @var{data}}, @w{mach_msg_type_number_t @var{data_count}}) +This is the asynchronous form of the @code{device_read} function. +@code{device_read_request} performs the read request. The meaning for +the parameters is as in @code{device_read}. Additionally, the caller +has to supply a reply port to which the @code{ds_device_read_reply} +message is sent by the kernel when the read has been performed. The +return value of the read operation is stored in @var{return_code}. + +As neither function receives a reply message, only message transmission +errors apply. If no error occurs, @code{KERN_SUCCESS} is returned. +@end deftypefun + +@deftypefun kern_return_t device_read_request_inband (@w{device_t @var{device}}, @w{mach_port_t @var{reply_port}}, @w{dev_mode_t @var{mode}}, @w{recnum_t @var{recnum}}, @w{int @var{bytes_wanted}}) +@deftypefunx kern_return_t ds_device_read_reply_inband (@w{mach_port_t @var{reply_port}}, @w{kern_return_t @var{return_code}}, @w{io_buf_ptr_t @var{data}}, @w{mach_msg_type_number_t @var{data_count}}) +The @code{device_read_request_inband} and +@code{ds_device_read_reply_inband} functions work as the +@code{device_read_request} and @code{ds_device_read_reply} functions, +except that the data is returned ``in-line'' in the reply IPC message +(@pxref{Memory}). +@end deftypefun + + +@node Device Write +@section Device Write + +@deftypefun kern_return_t device_write (@w{device_t @var{device}}, @w{dev_mode_t @var{mode}}, @w{recnum_t @var{recnum}}, @w{io_buf_ptr_t @var{data}}, @w{mach_msg_type_number_t @var{data_count}}, @w{int *@var{bytes_written}}) +The function @code{device_write} writes @var{data_count} bytes from the +buffer @var{data} to @var{device}. The number of bytes actually written +is returned in @var{bytes_written}. + +If @var{mode} is @code{D_NOWAIT}, the function returns without waiting +for I/O completion. Otherwise @var{mode} should be 0. @var{recnum} is +the record number to be written, its meaning is device specific. + +The function returns @code{D_SUCCESS} if some data was successfully +written and @code{D_NO_SUCH_DEVICE} if @var{device} does not denote a +device port or the device is dead or not completely open. +@end deftypefun + +@deftypefun kern_return_t device_write_inband (@w{device_t @var{device}}, @w{dev_mode_t @var{mode}}, @w{recnum_t @var{recnum}}, @w{int @var{bytes_wanted}}, @w{io_buf_ptr_inband_t *@var{data}}, @w{mach_msg_type_number_t *@var{data_count}}) +The @code{device_write_inband} function works as the @code{device_write} +function, except that the data is sent ``in-line'' in the request IPC +message (@pxref{Memory}). +@end deftypefun + +@deftypefun kern_return_t device_write_request (@w{device_t @var{device}}, @w{mach_port_t @var{reply_port}}, @w{dev_mode_t @var{mode}}, @w{recnum_t @var{recnum}}, @w{io_buf_ptr_t @var{data}}, @w{mach_msg_type_number_t @var{data_count}}) +@deftypefunx kern_return_t ds_device_write_reply (@w{mach_port_t @var{reply_port}}, @w{kern_return_t @var{return_code}}, @w{int @var{bytes_written}}) +This is the asynchronous form of the @code{device_write} function. +@code{device_write_request} performs the write request. The meaning for +the parameters is as in @code{device_write}. Additionally, the caller +has to supply a reply port to which the @code{ds_device_write_reply} +message is sent by the kernel when the write has been performed. The +return value of the write operation is stored in @var{return_code}. + +As neither function receives a reply message, only message transmission +errors apply. If no error occurs, @code{KERN_SUCCESS} is returned. +@end deftypefun + +@deftypefun kern_return_t device_write_request_inband (@w{device_t @var{device}}, @w{mach_port_t @var{reply_port}}, @w{dev_mode_t @var{mode}}, @w{recnum_t @var{recnum}}, @w{io_buf_ptr_t @var{data}}, @w{mach_msg_type_number_t @var{data_count}}) +@deftypefunx kern_return_t ds_device_write_reply_inband (@w{mach_port_t @var{reply_port}}, @w{kern_return_t @var{return_code}}, @w{int @var{bytes_written}}) +The @code{device_write_request_inband} and +@code{ds_device_write_reply_inband} functions work as the +@code{device_write_request} and @code{ds_device_write_reply} functions, +except that the data is sent ``in-line'' in the request IPC message +(@pxref{Memory}). +@end deftypefun + + +@node Device Map +@section Device Map + +@deftypefun kern_return_t device_map (@w{device_t @var{device}}, @w{vm_prot_t @var{prot}}, @w{vm_offset_t @var{offset}}, @w{vm_size_t @var{size}}, @w{mach_port_t *@var{pager}}, @w{int @var{unmap}}) +The function @code{device_map} creates a new memory manager for +@var{device} and returns a port to it in @var{pager}. The memory +manager is usable as a memory object in a @code{vm_map} call. The call +is device dependent. + +The protection for the memory object is specified by @var{prot}. The +memory object starts at @var{offset} within the device and extends +@var{size} bytes. @var{unmap} is currently unused. +@c XXX I suppose the caller should set it to 0. + +The function returns @code{D_SUCCESS} if some data was successfully +written and @code{D_NO_SUCH_DEVICE} if @var{device} does not denote a +device port or the device is dead or not completely open. +@end deftypefun + + +@node Device Status +@section Device Status + +@deftypefun kern_return_t device_set_status (@w{device_t @var{device}}, @w{dev_flavor_t @var{flavor}}, @w{dev_status_t @var{status}}, @w{mach_msg_type_number_t @var{status_count}}) +The function @code{device_set_status} sets the status of a device. The +possible values for @var{flavor} and their interpretation is device +specific. + +The function returns @code{D_SUCCESS} if some data was successfully +written and @code{D_NO_SUCH_DEVICE} if @var{device} does not denote a +device port or the device is dead or not completely open. +@end deftypefun + +@deftypefun kern_return_t device_get_status (@w{device_t @var{device}}, @w{dev_flavor_t @var{flavor}}, @w{dev_status_t @var{status}}, @w{mach_msg_type_number_t *@var{status_count}}) +The function @code{device_get_status} gets the status of a device. The +possible values for @var{flavor} and their interpretation is device +specific. + +The function returns @code{D_SUCCESS} if some data was successfully +written and @code{D_NO_SUCH_DEVICE} if @var{device} does not denote a +device port or the device is dead or not completely open. +@end deftypefun + + +@node Device Filter +@section Device Filter + +@deftypefun kern_return_t device_set_filter (@w{device_t @var{device}}, @w{mach_port_t @var{receive_port}}, @w{mach_msg_type_name_t @var{receive_port_type}}, @w{int @var{priority}}, @w{filter_array_t @var{filter}}, @w{mach_msg_type_number_t @var{filter_count}}) +The function @code{device_set_filter} makes it possible to filter out +selected data arriving at or leaving the device and forward it to a port. +@var{filter} is a list of filter commands, which are applied to incoming +data to determine if the data should be sent to @var{receive_port}. The +IPC type of the send right is specified by @var{receive_port_right}, it +is either @code{MACH_MSG_TYPE_MAKE_SEND} or +@code{MACH_MSG_TYPE_MOVE_SEND}. The @var{priority} value is used to +order multiple filters. + +There can be up to @code{NET_MAX_FILTER} commands in @var{filter}. The +actual number of commands is passed in @var{filter_count}. For the +purpose of the filter test, an internal stack is provided. After all +commands have been processed, the value on the top of the stack +determines if the data is forwarded or the next filter is tried. + +The first command is a header which contains two fields: one for flags +and the other for the type of interpreter used to run the rest of the +commands. + +Any combination of the following flags is allowed but at least one of them +must be specified. + +@table @code +@item NETF_IN +The filter will be applied to data received by the device. + +@item NETF_OUT +The filter will be applied to data transmitted by the device. +@end table + +Unless the type is given explicitly the native NETF interpreter will be used. +To select an alternative implementation use one of the following types: + +@table @code +@item NETF_BPF +Use Berkeley Packet Filter. +@end table + +For the listener to know what kind of packet is being received, when the +filter code accepts a packet the message sent to @var{receive_port} is +tagged with either NETF_IN or NETF_OUT. + +@c XXX The following description was taken verbatim from the +@c kernel_interface.pdf document. +Each word of the command list specifies a data (push) operation (high +order NETF_NBPO bits) as well as a binary operator (low order NETF_NBPA +bits). The value to be pushed onto the stack is chosen as follows. + +@table @code +@item NETF_PUSHLIT +Use the next short word of the filter as the value. + +@item NETF_PUSHZERO +Use 0 as the value. + +@item NETF_PUSHWORD+N +Use short word N of the ``data'' portion of the message as the value. + +@item NETF_PUSHHDR+N +Use short word N of the ``header'' portion of the message as the value. + +@item NETF_PUSHIND+N +Pops the top long word from the stack and then uses short word N of the +``data'' portion of the message as the value. + +@item NETF_PUSHHDRIND+N +Pops the top long word from the stack and then uses short word N of the +``header'' portion of the message as the value. + +@item NETF_PUSHSTK+N +Use long word N of the stack (where the top of stack is long word 0) as +the value. + +@item NETF_NOPUSH +Don't push a value. +@end table + +The unsigned value so chosen is promoted to a long word before being +pushed. Once a value is pushed (except for the case of +@code{NETF_NOPUSH}), the top two long words of the stack are popped and +a binary operator applied to them (with the old top of stack as the +second operand). The result of the operator is pushed on the stack. +These operators are: + +@table @code +@item NETF_NOP +Don't pop off any values and do no operation. + +@item NETF_EQ +Perform an equal comparison. + +@item NETF_LT +Perform a less than comparison. + +@item NETF_LE +Perform a less than or equal comparison. + +@item NETF_GT +Perform a greater than comparison. + +@item NETF_GE +Perform a greater than or equal comparison. + +@item NETF_AND +Perform a bitise boolean AND operation. + +@item NETF_OR +Perform a bitise boolean inclusive OR operation. + +@item NETF_XOR +Perform a bitise boolean exclusive OR operation. + +@item NETF_NEQ +Perform a not equal comparison. + +@item NETF_LSH +Perform a left shift operation. + +@item NETF_RSH +Perform a right shift operation. + +@item NETF_ADD +Perform an addition. + +@item NETF_SUB +Perform a subtraction. + +@item NETF_COR +Perform an equal comparison. If the comparison is @code{TRUE}, terminate +the filter list. Otherwise, pop the result of the comparison off the +stack. + +@item NETF_CAND +Perform an equal comparison. If the comparison is @code{FALSE}, +terminate the filter list. Otherwise, pop the result of the comparison +off the stack. + +@item NETF_CNOR +Perform a not equal comparison. If the comparison is @code{FALSE}, +terminate the filter list. Otherwise, pop the result of the comparison +off the stack. + +@item NETF_CNAND +Perform a not equal comparison. If the comparison is @code{TRUE}, +terminate the filter list. Otherwise, pop the result of the comparison +off the stack. The scan of the filter list terminates when the filter +list is emptied, or a @code{NETF_C...} operation terminates the list. At +this time, if the final value of the top of the stack is @code{TRUE}, +then the message is accepted for the filter. +@end table + +The function returns @code{D_SUCCESS} if some data was successfully +written, @code{D_INVALID_OPERATION} if @var{receive_port} is not a valid +send right, and @code{D_NO_SUCH_DEVICE} if @var{device} does not denote +a device port or the device is dead or not completely open. +@end deftypefun + + +@node Device Interrupt +@section Device Interrupt + +@deftypefun kern_return_t device_intr_register (@w{device_t @var{device}}, @w{int @var{id}}, @w{int @var{flags}}, @w{mach_port_t @var{receive_port}}) +The function @code{device_intr_register} registers for receiving hardware +interrupt events through @var{device_intr_notify} notifications. The hardware +interrupt identifier is specified by @var{id}. @var{flags} must be set to 0. The +notifications will be sent on the @var{receive_port} send right. +@code{device_intr_register} is only available on the dedicated @code{irq} device. +@end deftypefun + +@deftypefun kern_return_t device_intr_ack (@w{device_t @var{device}}, @w{mach_port_t @var{receive_port}}) +On a hardware interrupt, the kernel disables the interrupt line before sending +notifications. To prevent from interrupt losses, the interrupt is kept disabled +until @code{device_intr_ack} is called to acknowledge the interrupt. +@var{receive_port} is the send right on which the interrupt notification was +received. +@end deftypefun + + +@node Kernel Debugger +@chapter Kernel Debugger + +The GNU Mach kernel debugger @code{ddb} is a powerful built-in debugger +with a gdb like syntax. It is enabled at compile time using the +@option{--enable-kdb} option. Whenever you want to enter the debugger +while running the kernel, you can press the key combination +@key{Ctrl-Alt-D}. + +@menu +* Operation:: Basic architecture of the kernel debugger. +* Commands:: Available commands in the kernel debugger. +* Variables:: Access of variables from the kernel debugger. +* Expressions:: Usage of expressions in the kernel debugger. +@end menu + + +@node Operation +@section Operation + +The current location is called @dfn{dot}. The dot is displayed with a +hexadecimal format at a prompt. Examine and write commands update dot +to the address of the last line examined or the last location modified, +and set @dfn{next} to the address of the next location to be examined or +changed. Other commands don't change dot, and set next to be the same +as dot. + +The general command syntax is: + +@example +@var{command}[/@var{modifier}] @var{address} [,@var{count}] +@end example + +@kbd{!!} repeats the previous command, and a blank line repeats from the +address next with count 1 and no modifiers. Specifying @var{address} sets +dot to the address. Omitting @var{address} uses dot. A missing @var{count} +is taken to be 1 for printing commands or infinity for stack traces. + +Current @code{ddb} is enhanced to support multi-thread debugging. A +break point can be set only for a specific thread, and the address space +or registers of non current thread can be examined or modified if +supported by machine dependent routines. For example, + +@example +break/t mach_msg_trap $task11.0 +@end example + +sets a break point at @code{mach_msg_trap} for the first thread of task +11 listed by a @code{show all threads} command. + +In the above example, @code{$task11.0} is translated to the +corresponding thread structure's address by variable translation +mechanism described later. If a default target thread is set in a +variable @code{$thread}, the @code{$task11.0} can be omitted. In +general, if @code{t} is specified in a modifier of a command line, a +specified thread or a default target thread is used as a target thread +instead of the current one. The @code{t} modifier in a command line is +not valid in evaluating expressions in a command line. If you want to +get a value indirectly from a specific thread's address space or access +to its registers within an expression, you have to specify a default +target thread in advance, and to use @code{:t} modifier immediately +after the indirect access or the register reference like as follows: + +@example +set $thread $task11.0 +print $eax:t *(0x100):tuh +@end example + +No sign extension and indirection @code{size(long, half word, byte)} can +be specified with @code{u}, @code{l}, @code{h} and @code{b} respectively +for the indirect access. + +Note: Support of non current space/register access and user space break +point depend on the machines. If not supported, attempts of such +operation may provide incorrect information or may cause strange +behavior. Even if supported, the user space access is limited to the +pages resident in the main memory at that time. If a target page is not +in the main memory, an error will be reported. + +@code{ddb} has a feature like a command @code{more} for the output. If +an output line exceeds the number set in the @code{$lines} variable, it +displays @samp{--db_more--} and waits for a response. The valid +responses for it are: + +@table @kbd +@item @key{SPC} +one more page + +@item @key{RET} +one more line + +@item q +abort the current command, and return to the command input mode +@end table + + +@node Commands +@section Commands + +@table @code +@item examine(x) [/@var{modifier}] @var{addr}[,@var{count}] [ @var{thread} ] +Display the addressed locations according to the formats in the +modifier. Multiple modifier formats display multiple locations. If no +format is specified, the last formats specified for this command is +used. Address space other than that of the current thread can be +specified with @code{t} option in the modifier and @var{thread} +parameter. The format characters are + +@table @code +@item b +look at by bytes(8 bits) + +@item h +look at by half words(16 bits) + +@item l +look at by long words(32 bits) + +@item q +look at by quad words(64 bits) + +@item a +print the location being displayed + +@item , +skip one unit producing no output + +@item A +print the location with a line number if possible + +@item x +display in unsigned hex + +@item z +display in signed hex + +@item o +display in unsigned octal + +@item d +display in signed decimal + +@item u +display in unsigned decimal + +@item r +display in current radix, signed + +@item c +display low 8 bits as a character. Non-printing characters are +displayed as an octal escape code (e.g. '\000'). + +@item s +display the null-terminated string at the location. Non-printing +characters are displayed as octal escapes. + +@item m +display in unsigned hex with character dump at the end of each line. +The location is also displayed in hex at the beginning of each line. + +@item i +display as an instruction + +@item I +display as an instruction with possible alternate formats depending on +the machine: + +@table @code +@item vax +don't assume that each external label is a procedure entry mask + +@item i386 +don't round to the next long word boundary + +@item mips +print register contents +@end table +@end table + +@item xf +Examine forward. It executes an examine command with the last specified +parameters to it except that the next address displayed by it is used as +the start address. + +@item xb +Examine backward. It executes an examine command with the last +specified parameters to it except that the last start address subtracted +by the size displayed by it is used as the start address. + +@item whatis @var{addr} +Try to find what this address is. This looks up in the various tasks, threads, +maps, caches etc. to give an idea what is behind this address. + +@item print[/axzodurc] @var{addr1} [ @var{addr2} @dots{} ] +Print @var{addr}'s according to the modifier character. Valid formats +are: @code{a} @code{x} @code{z} @code{o} @code{d} @code{u} @code{r} +@code{c}. If no modifier is specified, the last one specified to it is +used. @var{addr} can be a string, and it is printed as it is. For +example, + +@example +print/x "eax = " $eax "\necx = " $ecx "\n" +@end example + +will print like + +@example +eax = xxxxxx +ecx = yyyyyy +@end example + +@item write[/bhlt] @var{addr} [ @var{thread} ] @var{expr1} [ @var{expr2} @dots{} ] +Write the expressions at succeeding locations. The write unit size can +be specified in the modifier with a letter b (byte), h (half word) or +l(long word) respectively. If omitted, long word is assumed. Target +address space can also be specified with @code{t} option in the modifier +and @var{thread} parameter. Warning: since there is no delimiter +between expressions, strange things may happen. It's best to enclose +each expression in parentheses. + +@item set $@var{variable} [=] @var{expr} +Set the named variable or register with the value of @var{expr}. Valid +variable names are described below. + +@item break[/tuTU] @var{addr}[,@var{count}] [ @var{thread1} @dots{} ] +Set a break point at @var{addr}. If count is supplied, continues +(@var{count}-1) times before stopping at the break point. If the break +point is set, a break point number is printed with @samp{#}. This +number can be used in deleting the break point or adding conditions to +it. + +@table @code +@item t +Set a break point only for a specific thread. The thread is specified +by @var{thread} parameter, or default one is used if the parameter is +omitted. + +@item u +Set a break point in user space address. It may be combined with +@code{t} or @code{T} option to specify the non-current target user +space. Without @code{u} option, the address is considered in the kernel +space, and wrong space address is rejected with an error message. This +option can be used only if it is supported by machine dependent +routines. + +@item T +Set a break point only for threads in a specific task. It is like +@code{t} option except that the break point is valid for all threads +which belong to the same task as the specified target thread. + +@item U +Set a break point in shared user space address. It is like @code{u} +option, except that the break point is valid for all threads which share +the same address space even if @code{t} option is specified. @code{t} +option is used only to specify the target shared space. Without +@code{t} option, @code{u} and @code{U} have the same meanings. @code{U} +is useful for setting a user space break point in non-current address +space with @code{t} option such as in an emulation library space. This +option can be used only if it is supported by machine dependent +routines. +@end table + +Warning: if a user text is shadowed by a normal user space debugger, +user space break points may not work correctly. Setting a break point +at the low-level code paths may also cause strange behavior. + +@item delete[/tuTU] @var{addr}|#@var{number} [ @var{thread1} @dots{} ] +Delete the break point. The target break point can be specified by a +break point number with @code{#}, or by @var{addr} like specified in +@code{break} command. + +@item cond #@var{number} [ @var{condition} @var{commands} ] +Set or delete a condition for the break point specified by the +@var{number}. If the @var{condition} and @var{commands} are null, the +condition is deleted. Otherwise the condition is set for it. When the +break point is hit, the @var{condition} is evaluated. The +@var{commands} will be executed if the condition is true and the break +point count set by a break point command becomes zero. @var{commands} +is a list of commands separated by semicolons. Each command in the list +is executed in that order, but if a @code{continue} command is executed, +the command execution stops there, and the stopped thread resumes +execution. If the command execution reaches the end of the list, and it +enters into a command input mode. For example, + +@example +set $work0 0 +break/Tu xxx_start $task7.0 +cond #1 (1) set $work0 1; set $work1 0; cont +break/T vm_fault $task7.0 +cond #2 ($work0) set $work1 ($work1+1); cont +break/Tu xxx_end $task7.0 +cond #3 ($work0) print $work1 " faults\n"; set $work0 0 +cont +@end example + +will print page fault counts from @code{xxx_start} to @code{xxx_end} in +@code{task7}. + +@item step[/p] [,@var{count}] +Single step @var{count} times. If @code{p} option is specified, print +each instruction at each step. Otherwise, only print the last +instruction. + +Warning: depending on machine type, it may not be possible to +single-step through some low-level code paths or user space code. On +machines with software-emulated single-stepping (e.g., pmax), stepping +through code executed by interrupt handlers will probably do the wrong +thing. + +@item continue[/c] +Continue execution until a breakpoint or watchpoint. If @code{/c}, +count instructions while executing. Some machines (e.g., pmax) also +count loads and stores. + +Warning: when counting, the debugger is really silently single-stepping. +This means that single-stepping on low-level code may cause strange +behavior. + +@item until +Stop at the next call or return instruction. + +@item next[/p] +Stop at the matching return instruction. If @code{p} option is +specified, print the call nesting depth and the cumulative instruction +count at each call or return. Otherwise, only print when the matching +return is hit. + +@item match[/p] +A synonym for @code{next}. + +@item trace[/tu] [ @var{frame_addr}|@var{thread} ][,@var{count}] +Stack trace. @code{u} option traces user space; if omitted, only traces +kernel space. If @code{t} option is specified, it shows the stack trace +of the specified thread or a default target thread. Otherwise, it shows +the stack trace of the current thread from the frame address specified +by a parameter or from the current frame. @var{count} is the number of +frames to be traced. If the @var{count} is omitted, all frames are +printed. + +Warning: If the target thread's stack is not in the main memory at that +time, the stack trace will fail. User space stack trace is valid only +if the machine dependent code supports it. + +@item search[/bhl] @var{addr} @var{value} [@var{mask}] [,@var{count}] +Search memory for a value. This command might fail in interesting ways +if it doesn't find the searched-for value. This is because @code{ddb} +doesn't always recover from touching bad memory. The optional count +argument limits the search. + +@item macro @var{name} @var{commands} +Define a debugger macro as @var{name}. @var{commands} is a list of +commands to be associated with the macro. In the expressions of the +command list, a variable @code{$argxx} can be used to get a parameter +passed to the macro. When a macro is called, each argument is evaluated +as an expression, and the value is assigned to each parameter, +@code{$arg1}, @code{$arg2}, @dots{} respectively. 10 @code{$arg} +variables are reserved to each level of macros, and they can be used as +local variables. The nesting of macro can be allowed up to 5 levels. +For example, + +@example +macro xinit set $work0 $arg1 +macro xlist examine/m $work0,4; set $work0 *($work0) +xinit *(xxx_list) +xlist +@enddots{} +@end example + +will print the contents of a list starting from @code{xxx_list} by each +@code{xlist} command. + +@item dmacro @var{name} +Delete the macro named @var{name}. + +@item show all threads[/uls] +Display all tasks and threads information. This version of @code{ddb} +prints more information than previous one. It shows UNIX process +information like @command{ps} for each task. The UNIX process +information may not be shown if it is not supported in the machine, or +the bottom of the stack of the target task is not in the main memory at +that time. It also shows task and thread identification numbers. These +numbers can be used to specify a task or a thread symbolically in +various commands. The numbers are valid only in the same debugger +session. If the execution is resumed again, the numbers may change. +The current thread can be distinguished from others by a @code{#} after +the thread id instead of @code{:}. Without @code{l} option, it only +shows thread id, thread structure address and the status for each +thread. The status consists of 6 letters, R(run), W(wait), S(suspended), +O(swapped out), N(interruptible), and F(loating) point arithmetic used (if +supported by the platform). If the corresponding +status bit is off, @code{.} is printed instead. If @code{l} option is +specified, more detail information is printed for each thread. If the +@code{s} option is given, scheduling information is displayed. + +@item show all tasks +Displays all tasks similar to @code{show all threads}, but omits +information about the individual threads. + +@item show task [ @var{addr} ] +Display the information of a task specified by @var{addr}. If +@var{addr} is omitted, current task information is displayed. + +@code{show task $taskxx} can notably be used to show task number +@var{xx} + +@item show thread [ @var{addr} ] +Display the information of a thread specified by @var{addr}. If +@var{addr} is omitted, current thread information is displayed. + +@code{show thread $taskxx.yy} can notably be used to show thread +number @var{yy} of task number @var{xx}. + +@item show registers[/tu [ @var{thread} ]] +Display the register set. Target thread can be specified with @code{t} +option and @var{thread} parameter. If @code{u} option is specified, it +displays user registers instead of kernel or currently saved one. + +Warning: The support of @code{t} and @code{u} option depends on the +machine. If not supported, incorrect information will be displayed. + +@item show map @var{addr} +Prints the @code{vm_map} at @var{addr}. + +@code{show map $mapxx} can notably be used to show the map of task +number @var{xx}. + +@item show object @var{addr} +Prints the @code{vm_object} at @var{addr}. + +@item show page @var{addr} +Prints the @code{vm_page} structure at @var{addr}. + +@item show port @var{addr} +Prints the @code{ipc_port} structure at @var{addr}. + +@item show ipc_port[/t [ @var{thread} ]] +Prints all @code{ipc_port} structure's addresses the target thread has. +The target thread is a current thread or that specified by a parameter. + +@item show macro [ @var{name} ] +Show the definitions of macros. If @var{name} is specified, only the +definition of it is displayed. Otherwise, definitions of all macros are +displayed. + +@item show watches +Displays all watchpoints. + +@item watch[/T] @var{addr},@var{size} [ @var{task} ] +Set a watchpoint for a region. Execution stops when an attempt to +modify the region occurs. The @var{size} argument defaults to 4. +Without @code{T} option, @var{addr} is assumed to be a kernel address. +If you want to set a watch point in user space, specify @code{T} and +@var{task} parameter where the address belongs to. If the @var{task} +parameter is omitted, a task of the default target thread or a current +task is assumed. If you specify a wrong space address, the request is +rejected with an error message. + +Warning: Attempts to watch wired kernel memory may cause unrecoverable +error in some systems such as i386. Watchpoints on user addresses work +best. + +@item dwatch[/T] @var{addr} [ @var{task} ] +Clears a watchpoint previously set for a region. +Without @code{T} option, @var{addr} is assumed to be a kernel address. +If you want to clear a watch point in user space, specify @code{T} and +@var{task} parameter where the address belongs to. If the @var{task} +parameter is omitted, a task of the default target thread or a current +task is assumed. If you specify a wrong space address, the request is +rejected with an error message. + +@item debug traps /on|/off +Enables or disables debugging of all traps with @code{ddb}. + +@item debug references /on|/off +Enables or disables debugging of all port reference counting errors +with @code{ddb}. + +@end table + + +@node Variables +@section Variables + +The debugger accesses registers and variables as $@var{name}. Register +names are as in the @code{show registers} command. Some variables are +suffixed with numbers, and may have some modifier following a colon +immediately after the variable name. For example, register variables +can have @code{u} and @code{t} modifier to indicate user register and +that of a default target thread instead of that of the current thread +(e.g. @code{$eax:tu}). + +Built-in variables currently supported are: + +@table @code +@item task@var{xx}[.@var{yy}] +Task or thread structure address. @var{xx} and @var{yy} are task and +thread identification numbers printed by a @code{show all threads} +command respectively. This variable is read only. + +@item map@var{xx} +VM map structure address. @var{xx} is a task identification number +printed by a @code{show all tasks} command. This variable is read +only. + +@item thread +The default target thread. The value is used when @code{t} option is +specified without explicit thread structure address parameter in command +lines or expression evaluation. + +@item radix +Input and output radix + +@item maxoff +Addresses are printed as @var{symbol}+@var{offset} unless offset is greater than +maxoff. + +@item maxwidth +The width of the displayed line. + +@item lines +The number of lines. It is used by @code{more} feature. + +@item tabstops +Tab stop width. + +@item arg@var{xx} +Parameters passed to a macro. @var{xx} can be 1 to 10. + +@item work@var{xx} +Work variable. @var{xx} can be 0 to 31. +@end table + + +@node Expressions +@section Expressions + +Almost all expression operators in C are supported except @code{~}, +@code{^}, and unary @code{&}. Special rules in @code{ddb} are: + +@table @code +@item @var{identifier} +name of a symbol. It is translated to the address(or value) of it. +@code{.} and @code{:} can be used in the identifier. If supported by +an object format dependent routine, +[@var{file_name}:]@var{func}[:@var{line_number}] +[@var{file_name}:]@var{variable}, and +@var{file_name}[:@var{line_number}] can be accepted as a symbol. The +symbol may be prefixed with @code{@var{symbol_table_name}::} like +@code{emulator::mach_msg_trap} to specify other than kernel symbols. + +@item @var{number} +radix is determined by the first two letters: +@table @code +@item 0x +hex +@item 0o +octal +@item 0t +decimal +@end table + +otherwise, follow current radix. + +@item . +dot + +@item + +next + +@item .. +address of the start of the last line examined. Unlike dot or next, +this is only changed by @code{examine} or @code{write} command. + +@item ´ +last address explicitly specified. + +@item $@var{variable} +register name or variable. It is translated to the value of it. It may +be followed by a @code{:} and modifiers as described above. + +@item a +multiple of right hand side. + +@item *@var{expr} +indirection. It may be followed by a @code{:} and modifiers as +described above. +@end table + +@include gpl.texi + +@node Documentation License +@appendix Documentation License + +This manual is copyrighted and licensed under the GNU Free Documentation +license. + +Parts of this manual are derived from the Mach manual packages +originally provided by Carnegie Mellon University. + +@menu +* GNU Free Documentation License:: The GNU Free Documentation License. +* CMU License:: The CMU license applies to the original Mach + kernel and its documentation. +@end menu + +@include fdl.texi + +@node CMU License +@appendixsec CMU License + +@quotation +@display +Mach Operating System +Copyright @copyright{} 1991,1990,1989 Carnegie Mellon University +All Rights Reserved. +@end display + +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. + +@sc{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 + +@display + Software Distribution Coordinator + School of Computer Science + Carnegie Mellon University + Pittsburgh PA 15213-3890 +@end display + +@noindent +or @email{Software.Distribution@@CS.CMU.EDU} any improvements or +extensions that they make and grant Carnegie Mellon the rights to +redistribute these changes. +@end quotation + +@node Concept Index +@unnumbered Concept Index + +@printindex cp + + +@node Function and Data Index +@unnumbered Function and Data Index + +@printindex fn + + +@summarycontents +@contents +@bye diff --git a/gensym.awk b/gensym.awk new file mode 100644 index 0000000..609d927 --- /dev/null +++ b/gensym.awk @@ -0,0 +1,78 @@ +# +# Copyright (c) 1994 The University of Utah and +# the Computer Systems Laboratory (CSL). 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. +# +# THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS +# IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF +# ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. +# +# CSL requests users of this software to return to csl-dist@cs.utah.edu any +# improvements that they make and grant CSL redistribution rights. +# +# Author: Bryan Ford, University of Utah CSL +# + +BEGIN { + bogus_printed = "no" +} + +# Start the bogus function just before the first sym directive, +# so that any #includes higher in the file don't get stuffed inside it. +/^[a-z]/ { + if (bogus_printed == "no") + { + print "void bogus(void);" + print "void bogus(void) {"; + bogus_printed = "yes"; + } +} + +# Take an arbitrarily complex C symbol or expression and constantize it. +/^expr/ { + print "__asm (\"\\n\\"; + if ($3 == "") + printf "* %s mAgIc%%0\" : : \"i\" (%s));\n", $2, $2; + else + printf "* %s mAgIc%%0\" : : \"i\" (%s));\n", $3, $2; +} + +# Output a symbol defining the size of a C structure. +/^size/ { + print "__asm (\"\\n\\"; + if ($4 == "") + printf "* %s_SIZE mAgIc%%0\" : : \"i\" (sizeof(struct %s)));\n", + toupper($3), $2; + else + printf "* %s mAgIc%%0\" : : \"i\" (sizeof(struct %s)));\n", + $4, $2; +} + +# Output a symbol defining the byte offset of an element of a C structure. +/^offset/ { + print "__asm (\"\\n\\"; + if ($5 == "") + { + printf "* %s_%s mAgIc%%0\" : : \"i\" (&((struct %s*)0)->%s));\n", + toupper($3), toupper($4), $2, $4; + } + else + { + printf "* %s mAgIc%%0\" : : \"i\" (&((struct %s*)0)->%s));\n", + toupper($5), $2, $4; + } +} + +# Copy through all preprocessor directives. +/^#/ { + print +} + +END { + print "}" +} diff --git a/gitlog-to-changelog b/gitlog-to-changelog new file mode 100755 index 0000000..e02d34c --- /dev/null +++ b/gitlog-to-changelog @@ -0,0 +1,432 @@ +eval '(exit $?0)' && eval 'exec perl -wS "$0" ${1+"$@"}' + & eval 'exec perl -wS "$0" $argv:q' + if 0; +# Convert git log output to ChangeLog format. + +my $VERSION = '2012-07-29 06:11'; # UTC +# The definition above must lie within the first 8 lines in order +# for the Emacs time-stamp write hook (at end) to update it. +# If you change this file with Emacs, please let the write hook +# do its job. Otherwise, update this string manually. + +# Copyright (C) 2008-2013 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Written by Jim Meyering + +use strict; +use warnings; +use Getopt::Long; +use POSIX qw(strftime); + +(my $ME = $0) =~ s|.*/||; + +# use File::Coda; # http://meyering.net/code/Coda/ +END { + defined fileno STDOUT or return; + close STDOUT and return; + warn "$ME: failed to close standard output: $!\n"; + $? ||= 1; +} + +sub usage ($) +{ + my ($exit_code) = @_; + my $STREAM = ($exit_code == 0 ? *STDOUT : *STDERR); + if ($exit_code != 0) + { + print $STREAM "Try '$ME --help' for more information.\n"; + } + else + { + print $STREAM < ChangeLog + $ME -- -n 5 foo > last-5-commits-to-branch-foo + +SPECIAL SYNTAX: + +The following types of strings are interpreted specially when they appear +at the beginning of a log message line. They are not copied to the output. + + Copyright-paperwork-exempt: Yes + Append the "(tiny change)" notation to the usual "date name email" + ChangeLog header to mark a change that does not require a copyright + assignment. + Co-authored-by: Joe User + List the specified name and email address on a second + ChangeLog header, denoting a co-author. + Signed-off-by: Joe User + These lines are simply elided. + +In a FILE specified via --amend, comment lines (starting with "#") are ignored. +FILE must consist of pairs where SHA is a 40-byte SHA1 (alone on +a line) referring to a commit in the current project, and CODE refers to one +or more consecutive lines of Perl code. Pairs must be separated by one or +more blank line. + +Here is sample input for use with --amend=FILE, from coreutils: + +3a169f4c5d9159283548178668d2fae6fced3030 +# fix typo in title: +s/all tile types/all file types/ + +1379ed974f1fa39b12e2ffab18b3f7a607082202 +# Due to a bug in vc-dwim, I mis-attributed a patch by Paul to myself. +# Change the author to be Paul. Note the escaped "@": +s,Jim .*>,Paul Eggert , + +EOF + } + exit $exit_code; +} + +# If the string $S is a well-behaved file name, simply return it. +# If it contains white space, quotes, etc., quote it, and return the new string. +sub shell_quote($) +{ + my ($s) = @_; + if ($s =~ m![^\w+/.,-]!) + { + # Convert each single quote to '\'' + $s =~ s/\'/\'\\\'\'/g; + # Then single quote the string. + $s = "'$s'"; + } + return $s; +} + +sub quoted_cmd(@) +{ + return join (' ', map {shell_quote $_} @_); +} + +# Parse file F. +# Comment lines (starting with "#") are ignored. +# F must consist of pairs where SHA is a 40-byte SHA1 +# (alone on a line) referring to a commit in the current project, and +# CODE refers to one or more consecutive lines of Perl code. +# Pairs must be separated by one or more blank line. +sub parse_amend_file($) +{ + my ($f) = @_; + + open F, '<', $f + or die "$ME: $f: failed to open for reading: $!\n"; + + my $fail; + my $h = {}; + my $in_code = 0; + my $sha; + while (defined (my $line = )) + { + $line =~ /^\#/ + and next; + chomp $line; + $line eq '' + and $in_code = 0, next; + + if (!$in_code) + { + $line =~ /^([0-9a-fA-F]{40})$/ + or (warn "$ME: $f:$.: invalid line; expected an SHA1\n"), + $fail = 1, next; + $sha = lc $1; + $in_code = 1; + exists $h->{$sha} + and (warn "$ME: $f:$.: duplicate SHA1\n"), + $fail = 1, next; + } + else + { + $h->{$sha} ||= ''; + $h->{$sha} .= "$line\n"; + } + } + close F; + + $fail + and exit 1; + + return $h; +} + +# git_dir_option $SRCDIR +# +# From $SRCDIR, the --git-dir option to pass to git (none if $SRCDIR +# is undef). Return as a list (0 or 1 element). +sub git_dir_option($) +{ + my ($srcdir) = @_; + my @res = (); + if (defined $srcdir) + { + my $qdir = shell_quote $srcdir; + my $cmd = "cd $qdir && git rev-parse --show-toplevel"; + my $qcmd = shell_quote $cmd; + my $git_dir = qx($cmd); + defined $git_dir + or die "$ME: cannot run $qcmd: $!\n"; + $? == 0 + or die "$ME: $qcmd had unexpected exit code or signal ($?)\n"; + chomp $git_dir; + push @res, "--git-dir=$git_dir/.git"; + } + @res; +} + +{ + my $since_date; + my $format_string = '%s%n%b%n'; + my $amend_file; + my $append_dot = 0; + my $cluster = 1; + my $strip_tab = 0; + my $strip_cherry_pick = 0; + my $srcdir; + GetOptions + ( + help => sub { usage 0 }, + version => sub { print "$ME version $VERSION\n"; exit }, + 'since=s' => \$since_date, + 'format=s' => \$format_string, + 'amend=s' => \$amend_file, + 'append-dot' => \$append_dot, + 'cluster!' => \$cluster, + 'strip-tab' => \$strip_tab, + 'strip-cherry-pick' => \$strip_cherry_pick, + 'srcdir=s' => \$srcdir, + ) or usage 1; + + defined $since_date + and unshift @ARGV, "--since=$since_date"; + + # This is a hash that maps an SHA1 to perl code (i.e., s/old/new/) + # that makes a correction in the log or attribution of that commit. + my $amend_code = defined $amend_file ? parse_amend_file $amend_file : {}; + + my @cmd = ('git', + git_dir_option $srcdir, + qw(log --log-size), + '--pretty=format:%H:%ct %an <%ae>%n%n'.$format_string, @ARGV); + open PIPE, '-|', @cmd + or die ("$ME: failed to run '". quoted_cmd (@cmd) ."': $!\n" + . "(Is your Git too old? Version 1.5.1 or later is required.)\n"); + + my $prev_multi_paragraph; + my $prev_date_line = ''; + my @prev_coauthors = (); + while (1) + { + defined (my $in = ) + or last; + $in =~ /^log size (\d+)$/ + or die "$ME:$.: Invalid line (expected log size):\n$in"; + my $log_nbytes = $1; + + my $log; + my $n_read = read PIPE, $log, $log_nbytes; + $n_read == $log_nbytes + or die "$ME:$.: unexpected EOF\n"; + + # Extract leading hash. + my ($sha, $rest) = split ':', $log, 2; + defined $sha + or die "$ME:$.: malformed log entry\n"; + $sha =~ /^[0-9a-fA-F]{40}$/ + or die "$ME:$.: invalid SHA1: $sha\n"; + + # If this commit's log requires any transformation, do it now. + my $code = $amend_code->{$sha}; + if (defined $code) + { + eval 'use Safe'; + my $s = new Safe; + # Put the unpreprocessed entry into "$_". + $_ = $rest; + + # Let $code operate on it, safely. + my $r = $s->reval("$code") + or die "$ME:$.:$sha: failed to eval \"$code\":\n$@\n"; + + # Note that we've used this entry. + delete $amend_code->{$sha}; + + # Update $rest upon success. + $rest = $_; + } + + # Remove lines inserted by "git cherry-pick". + if ($strip_cherry_pick) + { + $rest =~ s/^\s*Conflicts:\n.*//sm; + $rest =~ s/^\s*\(cherry picked from commit [\da-f]+\)\n//m; + } + + my @line = split "\n", $rest; + my $author_line = shift @line; + defined $author_line + or die "$ME:$.: unexpected EOF\n"; + $author_line =~ /^(\d+) (.*>)$/ + or die "$ME:$.: Invalid line " + . "(expected date/author/email):\n$author_line\n"; + + # Format 'Copyright-paperwork-exempt: Yes' as a standard ChangeLog + # `(tiny change)' annotation. + my $tiny = (grep (/^Copyright-paperwork-exempt:\s+[Yy]es$/, @line) + ? ' (tiny change)' : ''); + + my $date_line = sprintf "%s %s$tiny\n", + strftime ("%F", localtime ($1)), $2; + + my @coauthors = grep /^Co-authored-by:.*$/, @line; + # Omit meta-data lines we've already interpreted. + @line = grep !/^(?:Signed-off-by:[ ].*>$ + |Co-authored-by:[ ] + |Copyright-paperwork-exempt:[ ] + )/x, @line; + + # Remove leading and trailing blank lines. + if (@line) + { + while ($line[0] =~ /^\s*$/) { shift @line; } + while ($line[$#line] =~ /^\s*$/) { pop @line; } + } + + # Record whether there are two or more paragraphs. + my $multi_paragraph = grep /^\s*$/, @line; + + # Format 'Co-authored-by: A U Thor ' lines in + # standard multi-author ChangeLog format. + for (@coauthors) + { + s/^Co-authored-by:\s*/\t /; + s/\s*/ + or warn "$ME: warning: missing email address for " + . substr ($_, 5) . "\n"; + } + + # If clustering of commit messages has been disabled, if this header + # would be different from the previous date/name/email/coauthors header, + # or if this or the previous entry consists of two or more paragraphs, + # then print the header. + if ( ! $cluster + || $date_line ne $prev_date_line + || "@coauthors" ne "@prev_coauthors" + || $multi_paragraph + || $prev_multi_paragraph) + { + $prev_date_line eq '' + or print "\n"; + print $date_line; + @coauthors + and print join ("\n", @coauthors), "\n"; + } + $prev_date_line = $date_line; + @prev_coauthors = @coauthors; + $prev_multi_paragraph = $multi_paragraph; + + # If there were any lines + if (@line == 0) + { + warn "$ME: warning: empty commit message:\n $date_line\n"; + } + else + { + if ($append_dot) + { + # If the first line of the message has enough room, then + if (length $line[0] < 72) + { + # append a dot if there is no other punctuation or blank + # at the end. + $line[0] =~ /[[:punct:]\s]$/ + or $line[0] .= '.'; + } + } + + # Remove one additional leading TAB from each line. + $strip_tab + and map { s/^\t// } @line; + + # Prefix each non-empty line with a TAB. + @line = map { length $_ ? "\t$_" : '' } @line; + + print "\n", join ("\n", @line), "\n"; + } + + defined ($in = ) + or last; + $in ne "\n" + and die "$ME:$.: unexpected line:\n$in"; + } + + close PIPE + or die "$ME: error closing pipe from " . quoted_cmd (@cmd) . "\n"; + # FIXME-someday: include $PROCESS_STATUS in the diagnostic + + # Complain about any unused entry in the --amend=F specified file. + my $fail = 0; + foreach my $sha (keys %$amend_code) + { + warn "$ME:$amend_file: unused entry: $sha\n"; + $fail = 1; + } + + exit $fail; +} + +# Local Variables: +# mode: perl +# indent-tabs-mode: nil +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "my $VERSION = '" +# time-stamp-format: "%:y-%02m-%02d %02H:%02M" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "'; # UTC" +# End: diff --git a/i386/Makefrag.am b/i386/Makefrag.am new file mode 100644 index 0000000..58ee327 --- /dev/null +++ b/i386/Makefrag.am @@ -0,0 +1,215 @@ +# Makefile fragment for i386. + +# Copyright (C) 1997, 1999, 2006, 2007 Free Software Foundation, Inc. + +# 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. +# +# THE FREE SOFTWARE FOUNDATION ALLOWS FREE USE OF THIS SOFTWARE IN ITS +# "AS IS" CONDITION. THE FREE SOFTWARE FOUNDATION DISCLAIMS ANY +# LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE +# USE OF THIS SOFTWARE. + +# +# Building a distribution. +# +EXTRA_DIST += \ + i386/i386/mach_i386.srv \ + i386/i386/i386asm.sym \ + i386/ldscript \ + i386/README-Drivers \ + i386/include + +if HOST_ix86 + +# +# Source files for any i386 kernel. +# + +libkernel_a_SOURCES += \ + i386/i386at/acpi_parse_apic.h \ + i386/i386at/acpi_parse_apic.c \ + i386/i386at/autoconf.c \ + i386/i386at/autoconf.h \ + i386/i386at/biosmem.c \ + i386/i386at/biosmem.h \ + i386/i386at/conf.c \ + i386/i386at/cons_conf.c \ + i386/i386at/elf.h \ + i386/i386at/idt.h \ + i386/i386at/model_dep.c \ + i386/i386at/model_dep.h \ + i386/include/mach/sa/stdarg.h + +if PLATFORM_at +libkernel_a_SOURCES += \ + i386/i386at/acpi_parse_apic.h \ + i386/i386at/acpi_parse_apic.c \ + i386/i386at/boothdr.S \ + i386/i386at/com.c \ + i386/i386at/com.h \ + i386/i386at/comreg.h \ + i386/i386at/cram.h \ + i386/i386at/disk.h \ + i386/i386at/i8250.h \ + i386/i386at/immc.c \ + i386/i386at/int_init.c \ + i386/i386at/int_init.h \ + i386/i386at/interrupt.S \ + i386/i386at/kd.c \ + i386/i386at/kd.h \ + i386/i386at/kd_event.c \ + i386/i386at/kd_event.h \ + i386/i386at/kd_queue.c \ + i386/i386at/kd_queue.h \ + i386/i386at/kd_mouse.c \ + i386/i386at/kd_mouse.h \ + i386/i386at/kdasm.S \ + i386/i386at/kdsoft.h \ + i386/i386at/mem.c \ + i386/i386at/mem.h \ + i386/i386at/rtc.c \ + i386/i386at/rtc.h +endif + +# +# `lpr' device support. +# + +if enable_lpr +libkernel_a_SOURCES += \ + i386/i386at/lpr.c \ + i386/i386at/lpr.h +endif + + +# +# Further source files for any i386 kernel. +# + +libkernel_a_SOURCES += \ + i386/i386/copy_user.h \ + i386/i386/cswitch.S \ + i386/i386/debug_trace.S \ + i386/i386/idt_inittab.S \ + i386/i386/locore.S \ + i386/i386/percpu.c \ + i386/i386/percpu.h \ + i386/i386/spl.S \ + i386/i386/cpuboot.S + +if PLATFORM_at +libkernel_a_SOURCES += \ + i386/i386/apic.h \ + i386/i386/apic.c \ + i386/i386/hardclock.c \ + i386/i386/hardclock.h \ + i386/i386/irq.c \ + i386/i386/irq.h \ + i386/i386/msr.h \ + i386/i386/pit.c \ + i386/i386/pit.h + +if enable_apic +libkernel_a_SOURCES += \ + i386/i386at/ioapic.c +else +libkernel_a_SOURCES += \ + i386/i386/pic.c \ + i386/i386/pic.h \ + i386/i386at/pic_isa.c +endif +endif + +# +# KDB support. +# + +if enable_kdb +libkernel_a_SOURCES += \ + i386/i386/_setjmp.S +endif + + +# +# Files from the generic sources that we want. +# + +libkernel_a_SOURCES += \ + chips/busses.c \ + chips/busses.h \ + device/cirbuf.c + +# +# Automatically generated source files. +# +# See Makerules.mig.am. +# + +nodist_lib_dep_tr_for_defs_a_SOURCES += \ + i386/i386/mach_i386.server.defs.c +nodist_libkernel_a_SOURCES += \ + i386/i386/mach_i386.server.h \ + i386/i386/mach_i386.server.c \ + i386/i386/mach_i386.server.msgids +# i386/i386/mach_i386.server.defs + +nodist_libkernel_a_SOURCES += \ + i386/i386/i386asm.h + +# +# Architecture specialities. +# + +if PLATFORM_at +gnumach_LINKFLAGS += \ + --defsym _START_MAP=$(_START_MAP) \ + --defsym _START=_START_MAP+0xC0000000 \ + -T '$(srcdir)'/i386/ldscript +endif + +AM_CFLAGS += \ + -mno-3dnow \ + -mno-mmx \ + -mno-sse \ + -mno-sse2 + +# +# Installation. +# + +include_mach_i386dir = $(includedir)/mach/i386 +include_mach_i386_HEADERS = \ + i386/include/mach/i386/asm.h \ + i386/include/mach/i386/boolean.h \ + i386/include/mach/i386/eflags.h \ + i386/include/mach/i386/exception.h \ + i386/include/mach/i386/fp_reg.h \ + i386/include/mach/i386/ioccom.h \ + i386/include/mach/i386/kern_return.h \ + i386/include/mach/i386/mach_i386.defs \ + i386/include/mach/i386/mach_i386_types.h \ + i386/include/mach/i386/machine_types.defs \ + i386/include/mach/i386/multiboot.h \ + i386/include/mach/i386/syscall_sw.h \ + i386/include/mach/i386/thread_status.h \ + i386/include/mach/i386/trap.h \ + i386/include/mach/i386/vm_param.h \ + i386/include/mach/i386/vm_types.h + +# +# Platform specific parts. +# + +if PLATFORM_xen +include i386/xen/Makefrag.am + +libkernel_a_SOURCES += \ + i386/i386/xen.h + +endif + +endif # HOST_i386 diff --git a/i386/Makefrag_x86.am b/i386/Makefrag_x86.am new file mode 100644 index 0000000..272de02 --- /dev/null +++ b/i386/Makefrag_x86.am @@ -0,0 +1,84 @@ +# Copyright (C) 2023 Free Software Foundation, Inc. + +# 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. +# +# THE FREE SOFTWARE FOUNDATION ALLOWS FREE USE OF THIS SOFTWARE IN ITS +# "AS IS" CONDITION. THE FREE SOFTWARE FOUNDATION DISCLAIMS ANY +# LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE +# USE OF THIS SOFTWARE. + +# Shared files for all x86. + +libkernel_a_SOURCES += \ + i386/i386/ast.h \ + i386/i386/ast_check.c \ + i386/i386/ast_types.h \ + i386/i386/cpu.h \ + i386/i386/cpu_number.h \ + i386/i386/db_disasm.c \ + i386/i386/db_interface.c \ + i386/i386/db_interface.h \ + i386/i386/db_machdep.h \ + i386/i386/db_trace.c \ + i386/i386/db_trace.h \ + i386/i386/debug.h \ + i386/i386/debug_i386.c \ + i386/i386/eflags.h \ + i386/i386/fpu.c \ + i386/i386/fpu.h \ + i386/i386/gdt.c \ + i386/i386/gdt.h \ + i386/i386/idt-gen.h \ + i386/i386/idt.c \ + i386/i386/io_perm.c \ + i386/i386/io_perm.h \ + i386/i386/ipl.h \ + i386/i386/ktss.c \ + i386/i386/ktss.h \ + i386/i386/kttd_interface.c \ + i386/i386/kttd_machdep.h \ + i386/i386/ldt.c \ + i386/i386/ldt.h \ + i386/i386/lock.h \ + i386/i386/locore.h \ + i386/i386/loose_ends.c \ + i386/i386/loose_ends.h \ + i386/i386/mach_param.h \ + i386/i386/machine_routines.h \ + i386/i386/machine_task.c \ + i386/i386/machspl.h \ + i386/i386/model_dep.h \ + i386/i386/mp_desc.c \ + i386/i386/mp_desc.h \ + i386/i386/pcb.c \ + i386/i386/pcb.h \ + i386/i386/phys.c \ + i386/i386/pio.h \ + i386/i386/pmap.h \ + i386/i386/proc_reg.h \ + i386/i386/sched_param.h \ + i386/i386/seg.h \ + i386/i386/setjmp.h \ + i386/i386/smp.c \ + i386/i386/smp.h \ + i386/i386/spl.h \ + i386/i386/strings.c \ + i386/i386/task.h \ + i386/i386/thread.h \ + i386/i386/time_stamp.h \ + i386/i386/trap.c \ + i386/i386/trap.h \ + i386/i386/tss.h \ + i386/i386/user_ldt.c \ + i386/i386/user_ldt.h \ + i386/i386/vm_param.h \ + i386/i386/xpr.h \ + i386/intel/pmap.c \ + i386/intel/pmap.h \ + i386/intel/read_fault.c \ + i386/intel/read_fault.h + diff --git a/i386/README-Drivers b/i386/README-Drivers new file mode 100644 index 0000000..3d1066c --- /dev/null +++ b/i386/README-Drivers @@ -0,0 +1,122 @@ +-*- text -*- + +Here some i386 specific details of the device drivers are explained. + +Each driver is followed by one or more triplets of three numbers. These +triplets specify combinations of I/O address, spl, and, pic that are believed +to work. + +Then comes the name of the device to users. `%d' is a unit number. + + +** Table + +*** Serial devices and similar equivalents + +PC com ports (always enabled) + 0x3f8,2f8,3e8 + com%d + +Parallel port + lpr%d + +System Console (always enabled) + (indirect name for kd or first com line) + console + +PC keyboard/display (always enabled) + kd + + +*** Special devices + +Mappable time device (always enabled) + time + +Mouse interface to PC (always enabled) + (Piggy backs horribly on COM devices) + mouse%d + +X Window System interface to keyboard (always enabled) + kbd%d + +Interface to setting up IO port access for users (always enabled) + iopl%d + + +*** Disk controllers (except for SCSI) + +PC floppy + 0x3f0, 370 + fd%d + + +*** Ethernet controllers +These all show up as `eth%d' except the atp device. + +NE2000/NE1000 ISA (ne, ne1000, ne2000) + 0x300,280,320,340,360 + +3Com 503 (3c503) / Etherlink II + 0x300,310,330,350,250,280,2a0,2e0 + +WD80x3 + 0x300,280,380,240 + +3COM 501 (3c501) / Etherlink I + 0x280,300 + +SMC Ultra + 0x200,220,240,280,300,340,380 + +HP PCLAN+ (27247B and 27252A) + 0x200,240,280,2c0,300,320,340 + +HP PCLAN (27245 and other 27xxx series) + 0x300,320,340,280,2c0,200,240 + +Seeq8005 + 0x300,320,340,360 + +Cabletron E21xx + 0x300,280,380,220 + +AT1700 (Fujitsu 86965) + 0x260,280,2a0,240,340,320,380,300 + +ICL EtherTeam 16i/32 (eth16i, eth32) + 0x260,280,2a0,240,340,320,380,300 (16i) + +EtherExpress 16 + 0x300,270,320,340 + +EtherExpressPro + 0x200,240,280,2c0,300,320,340,360 + +AT&T WaveLAN & DEC RoamAbout DS + 0x390 + +3Com 507 (3c507, el16) + 0x300,320,340,280 + +3Com 505 (3c505, elplus) + 0x300,280,310 + +D-Link DE-600 + 0x378 + +D-Link DE-620 + 0x378 + +Schneider & Koch G16 + 0x100,180,208,220,288,320,328,390 + +NI5210 + 0x300,280,360,320,340 + +NI6510 + 0x300/320/340/360 + +AT-LAN-TEC/RealTek pocket adaptor + 0x378,278,3bc + atp%d diff --git a/i386/configfrag.ac b/i386/configfrag.ac new file mode 100644 index 0000000..f07a98c --- /dev/null +++ b/i386/configfrag.ac @@ -0,0 +1,124 @@ +dnl Configure fragment for i386. + +dnl Copyright (C) 1999, 2004, 2006, 2007, 2008 Free Software Foundation, Inc. + +dnl Permission to use, copy, modify and distribute this software and its +dnl documentation is hereby granted, provided that both the copyright +dnl notice and this permission notice appear in all copies of the +dnl software, derivative works or modified versions, and any portions +dnl thereof, and that both notices appear in supporting documentation. +dnl +dnl THE FREE SOFTWARE FOUNDATION ALLOWS FREE USE OF THIS SOFTWARE IN ITS +dnl "AS IS" CONDITION. THE FREE SOFTWARE FOUNDATION DISCLAIMS ANY +dnl LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE +dnl USE OF THIS SOFTWARE. + +# +# Definitions. +# + +[case $host_cpu in + i?86)] + AM_CONDITIONAL([HOST_ix86], [true]) + + # Some of the i386-specific code checks for these. + AC_DEFINE([__ELF__], [1], [__ELF__]) + + # Determines the size of the CPU cache line. + AC_DEFINE([CPU_L1_SHIFT], [6], [CPU_L1_SHIFT]) + + [# Does the architecture provide machine-specific interfaces? + mach_machine_routines=1;; + *)] + AM_CONDITIONAL([HOST_ix86], [false])[;; +esac + +case $host_platform in + at)] + AM_CONDITIONAL([PLATFORM_at], [true])[;; + *)] + AM_CONDITIONAL([PLATFORM_at], [false])[;; +esac + +# +# Formerly in `i386/bogus/'. +# + +case $host_platform:$host_cpu in + at:i?86) + # should be 4, but we do not support shared IRQ for these + ncom=2 + nlpr=1 + + # i386/bogus/platforms.h] + AC_DEFINE([AT386], [1], [AT386])[;; + xen:i?86) + # TODO. That should probably not be needed. + ncom=1 + # TODO. That should probably not be needed. + # i386/bogus/platforms.h] + AC_DEFINE([AT386], [1], [AT386])[;; + *) + :;; +esac] + +# +# Options. +# + +# The immediate console, useful for debugging early system +# initialization. Disabled by default. +AC_DEFINE([ENABLE_IMMEDIATE_CONSOLE], [0], [ENABLE_IMMEDIATE_CONSOLE]) + +AC_ARG_ENABLE([lpr], + AS_HELP_STRING([--enable-lpr], [lpr device; on ix86-at enabled by default])) +[case $host_platform:$host_cpu in + at:i?86) + case $enable_device_drivers in + default) + enable_lpr=${enable_lpr-yes};; + *) + enable_lpr=${enable_lpr-no};; + esac;; + *) + if [ x"$enable_lpr" = xyes ]; then] + AC_MSG_ERROR([cannot enable `lpr' in this configuration.]) + [fi;; +esac +if [ x"$enable_lpr" = xyes ]; then] + AC_DEFINE([MACH_LPR], [], [lpr device]) + AM_CONDITIONAL([enable_lpr], [true]) +[else] + AM_CONDITIONAL([enable_lpr], [false]) +[fi] + +AC_ARG_ENABLE([apic], + AS_HELP_STRING([--enable-apic], [LAPIC/IOAPIC support])) +[if [ x"$enable_apic" = xyes ]; then] + AC_DEFINE([APIC], [1], [APIC support]) + AM_CONDITIONAL([enable_apic], [true]) +[else] + AM_CONDITIONAL([enable_apic], [false]) +[fi] + +[case $host_platform:$host_cpu in + xen:i?86) + enable_pae=${enable_pae-yes};; + *:i?86) + :;; + *:x86_64) + enable_pae=${enable_pae-yes};; + *) + if [ x"$enable_pae" = xyes ]; then] + AC_MSG_ERROR([can only enable the `PAE' feature on ix86.]) + [fi;; +esac] + +AC_ARG_WITH([_START_MAP], + AS_HELP_STRING([--with-_START_MAP=0x1000000], [specify kernel mapping start address]), + [_START_MAP="$withval"], [_START_MAP=0x1000000]) +AC_SUBST(_START_MAP) + +dnl Local Variables: +dnl mode: autoconf +dnl End: diff --git a/i386/i386/.gitignore b/i386/i386/.gitignore new file mode 100644 index 0000000..4520a2a --- /dev/null +++ b/i386/i386/.gitignore @@ -0,0 +1 @@ +/i386asm.h diff --git a/i386/i386/_setjmp.S b/i386/i386/_setjmp.S new file mode 100644 index 0000000..efabeb6 --- /dev/null +++ b/i386/i386/_setjmp.S @@ -0,0 +1,63 @@ +/* + * 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. + */ +/* + * C library -- _setjmp, _longjmp + * + * _longjmp(a,v) + * will generate a "return(v)" from + * the last call to + * _setjmp(a) + * by restoring registers from the stack, + * The previous signal state is NOT restored. + * + */ + +#include + +ENTRY(_setjmp) + movl 4(%esp),%ecx /* fetch buffer */ + movl %ebx,0(%ecx) + movl %esi,4(%ecx) + movl %edi,8(%ecx) + movl %ebp,12(%ecx) /* save frame pointer of caller */ + popl %edx + movl %esp,16(%ecx) /* save stack pointer of caller */ + movl %edx,20(%ecx) /* save pc of caller */ + xorl %eax,%eax + jmp *%edx + +ENTRY(_longjmp) + movl 8(%esp),%eax /* return(v) */ + movl 4(%esp),%ecx /* fetch buffer */ + movl 0(%ecx),%ebx + movl 4(%ecx),%esi + movl 8(%ecx),%edi + movl 12(%ecx),%ebp + movl 16(%ecx),%esp + orl %eax,%eax + jnz 0f + incl %eax +0: jmp *20(%ecx) /* done, return.... */ diff --git a/i386/i386/apic.c b/i386/i386/apic.c new file mode 100644 index 0000000..0b5be50 --- /dev/null +++ b/i386/i386/apic.c @@ -0,0 +1,453 @@ +/* apic.c - APIC controller management for Mach. + Copyright (C) 2020 Free Software Foundation, Inc. + Written by Almudena Garcia Jurado-Centurion + + This file is part of GNU Mach. + + GNU Mach is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Mach is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * Period of HPET timer in nanoseconds + */ +uint32_t hpet_period_nsec; + +/* + * This dummy structure is needed so that CPU_NUMBER can be called + * before the lapic pointer is initialized to point to the real Local Apic. + * It causes the apic_id to be faked as 0, which is the master processor. + */ +static ApicLocalUnit dummy_lapic = {0}; +volatile ApicLocalUnit* lapic = &dummy_lapic; + +/* This lookup table of [apic_id] -> kernel_id is initially populated with zeroes + * so every lookup results in master processor until real kernel ids are populated. + */ +int cpu_id_lut[UINT8_MAX + 1] = {0}; + +ApicInfo apic_data; + +/* + * apic_data_init: initialize the apic_data structures to preliminary values. + * Reserve memory to the lapic list dynamic vector. + * Returns 0 if success, -1 if error. + */ +int +apic_data_init(void) +{ + apic_data.cpu_lapic_list = NULL; + apic_data.ncpus = 0; + apic_data.nioapics = 0; + apic_data.nirqoverride = 0; + + /* Reserve the vector memory for the maximum number of processors. */ + apic_data.cpu_lapic_list = (uint16_t*) kalloc(NCPUS*sizeof(uint16_t)); + + /* If the memory reserve fails, return -1 to advice about the error. */ + if (apic_data.cpu_lapic_list == NULL) + return -1; + + return 0; +} + +/* + * apic_lapic_init: initialize lapic pointer to the memory common address. + * Receives as input a pointer to the virtual memory address, previously mapped in a page. + */ +void +apic_lapic_init(ApicLocalUnit* lapic_ptr) +{ + lapic = lapic_ptr; +} + +/* + * apic_add_cpu: add a new lapic/cpu entry to the cpu_lapic list. + * Receives as input the lapic's APIC ID. + */ +void +apic_add_cpu(uint16_t apic_id) +{ + apic_data.cpu_lapic_list[apic_data.ncpus] = apic_id; + apic_data.ncpus++; +} + +/* + * apic_add_ioapic: add a new ioapic entry to the ioapic list. + * Receives as input an ioapic_data structure, filled with the IOAPIC entry's data. + */ +void +apic_add_ioapic(IoApicData ioapic) +{ + apic_data.ioapic_list[apic_data.nioapics] = ioapic; + apic_data.nioapics++; +} + +/* + * apic_add_irq_override: add a new IRQ to the irq_override list. + * Receives as input an irq_override_data structure, filled with the IRQ entry's data. + */ +void +apic_add_irq_override(IrqOverrideData irq_over) +{ + apic_data.irq_override_list[apic_data.nirqoverride] = irq_over; + apic_data.nirqoverride++; +} + +IrqOverrideData * +acpi_get_irq_override(uint8_t pin) +{ + int i; + + for (i = 0; i < apic_data.nirqoverride; i++) { + if (apic_data.irq_override_list[i].irq == pin) { + return &apic_data.irq_override_list[i]; + } + } + return NULL; +} + +/* + * apic_get_cpu_apic_id: returns the apic_id of a cpu. + * Receives as input the kernel ID of a CPU. + */ +int +apic_get_cpu_apic_id(int kernel_id) +{ + if (kernel_id >= NCPUS) + return -1; + + return apic_data.cpu_lapic_list[kernel_id]; +} + + +/* + * apic_get_cpu_kernel_id: returns the kernel_id of a cpu. + * Receives as input the APIC ID of a CPU. + */ +int +apic_get_cpu_kernel_id(uint16_t apic_id) +{ + return cpu_id_lut[apic_id]; +} + +/* apic_get_lapic: returns a reference to the common memory address for Local APIC. */ +volatile ApicLocalUnit* +apic_get_lapic(void) +{ + return lapic; +} + +/* + * apic_get_ioapic: returns the IOAPIC identified by its kernel ID. + * Receives as input the IOAPIC's Kernel ID. + * Returns a ioapic_data structure pointer with the IOAPIC's data. + */ +struct IoApicData * +apic_get_ioapic(int kernel_id) +{ + if (kernel_id < MAX_IOAPICS) + return &apic_data.ioapic_list[kernel_id]; + return NULL; +} + +/* apic_get_numcpus: returns the current number of cpus. */ +uint8_t +apic_get_numcpus(void) +{ + return apic_data.ncpus; +} + +/* apic_get_num_ioapics: returns the current number of ioapics. */ +uint8_t +apic_get_num_ioapics(void) +{ + return apic_data.nioapics; +} + +/* apic_get_total_gsis: returns the total number of GSIs in the system. */ +int +apic_get_total_gsis(void) +{ + int id; + int gsis = 0; + + for (id = 0; id < apic_get_num_ioapics(); id++) + gsis += apic_get_ioapic(id)->ngsis; + + return gsis; +} + +/* + * apic_get_current_cpu: returns the apic_id of current cpu. + */ +int +apic_get_current_cpu(void) +{ + unsigned int eax, ebx, ecx, edx; + eax = 1; + ecx = 0; + cpuid(eax, ebx, ecx, edx); + return (ebx >> 24); +} + + +/* + * apic_refit_cpulist: adjust the size of cpu_lapic array to fit the real number of cpus + * instead the maximum number. + * + * Returns 0 if success, -1 if error. + */ +int apic_refit_cpulist(void) +{ + uint16_t* old_list = apic_data.cpu_lapic_list; + uint16_t* new_list = NULL; + + if (old_list == NULL) + return -1; + + new_list = (uint16_t*) kalloc(apic_data.ncpus*sizeof(uint16_t)); + + if (new_list == NULL) + return -1; + + for (int i = 0; i < apic_data.ncpus; i++) + new_list[i] = old_list[i]; + + apic_data.cpu_lapic_list = new_list; + kfree((vm_offset_t) old_list, NCPUS*sizeof(uint16_t)); + + return 0; +} + +/* + * apic_generate_cpu_id_lut: Generate lookup table of cpu kernel ids from apic ids + */ +void apic_generate_cpu_id_lut(void) +{ + int i, apic_id; + + for (i = 0; i < apic_data.ncpus; i++) { + apic_id = apic_get_cpu_apic_id(i); + if (apic_id >= 0) + cpu_id_lut[apic_id] = i; + else + printf("apic_get_cpu_apic_id(%d) failed...\n", i); + } +} + +/* + * apic_print_info: shows the list of Local APIC and IOAPIC. + * Shows each CPU and IOAPIC, with Its Kernel ID and APIC ID. + */ +void apic_print_info(void) +{ + int i; + int ncpus, nioapics; + + ncpus = apic_get_numcpus(); + nioapics = apic_get_num_ioapics(); + + uint16_t lapic_id; + uint16_t ioapic_id; + + IoApicData *ioapic; + + printf("CPUS:\n"); + for (i = 0; i < ncpus; i++) { + lapic_id = apic_get_cpu_apic_id(i); + printf(" CPU %d - APIC ID %x - addr=0x%p\n", i, lapic_id, apic_get_lapic()); + } + + printf("IOAPICS:\n"); + for (i = 0; i < nioapics; i++) { + ioapic = apic_get_ioapic(i); + if (!ioapic) { + printf("ERROR: invalid IOAPIC ID %x\n", i); + } else { + ioapic_id = ioapic->apic_id; + printf(" IOAPIC %d - APIC ID %x - addr=0x%p\n", i, ioapic_id, ioapic->ioapic); + } + } +} + +void apic_send_ipi(unsigned dest_shorthand, unsigned deliv_mode, unsigned dest_mode, unsigned level, unsigned trig_mode, unsigned vector, unsigned dest_id) +{ + IcrLReg icrl_values; + IcrHReg icrh_values; + + /* Keep previous values and only overwrite known fields */ + icrl_values.r = lapic->icr_low.r; + icrh_values.r = lapic->icr_high.r; + + icrl_values.destination_shorthand = dest_shorthand; + icrl_values.delivery_mode = deliv_mode; + icrl_values.destination_mode = dest_mode; + icrl_values.level = level; + icrl_values.trigger_mode = trig_mode; + icrl_values.vector = vector; + icrh_values.destination_field = dest_id; + + lapic->icr_high.r = icrh_values.r; + lapic->icr_low.r = icrl_values.r; +} + +void +lapic_enable(void) +{ + lapic->spurious_vector.r |= LAPIC_ENABLE; +} + +void +lapic_disable(void) +{ + lapic->spurious_vector.r &= ~LAPIC_ENABLE; +} + +void +lapic_setup(void) +{ + unsigned long flags; + int apic_id; + volatile uint32_t dummy; + + cpu_intr_save(&flags); + + apic_id = apic_get_current_cpu(); + + dummy = lapic->dest_format.r; + lapic->dest_format.r = 0xffffffff; /* flat model */ + dummy = lapic->logical_dest.r; + lapic->logical_dest.r = lapic->apic_id.r; /* target self */ + dummy = lapic->lvt_lint0.r; + lapic->lvt_lint0.r = dummy | LAPIC_DISABLE; + dummy = lapic->lvt_lint1.r; + lapic->lvt_lint1.r = dummy | LAPIC_DISABLE; + dummy = lapic->lvt_performance_monitor.r; + lapic->lvt_performance_monitor.r = dummy | LAPIC_DISABLE; + if (apic_id != 0) + { + dummy = lapic->lvt_timer.r; + lapic->lvt_timer.r = dummy | LAPIC_DISABLE; + } + dummy = lapic->task_pri.r; + lapic->task_pri.r = 0; + + /* Enable LAPIC to send or recieve IPI/SIPIs */ + dummy = lapic->spurious_vector.r; + lapic->spurious_vector.r = IOAPIC_SPURIOUS_BASE + | LAPIC_ENABLE_DIRECTED_EOI; + + lapic->error_status.r = 0; + + cpu_intr_restore(flags); +} + +void +lapic_eoi(void) +{ + lapic->eoi.r = 0; +} + +#define HPET32(x) *((volatile uint32_t *)((uint8_t *)hpet_addr + x)) +#define HPET_CAP_PERIOD 0x04 +#define HPET_CFG 0x10 +# define HPET_CFG_ENABLE (1 << 0) +# define HPET_LEGACY_ROUTE (1 << 1) +#define HPET_COUNTER 0xf0 +#define HPET_T0_CFG 0x100 +# define HPET_T0_32BIT_MODE (1 << 8) +# define HPET_T0_VAL_SET (1 << 6) +# define HPET_T0_TYPE_PERIODIC (1 << 3) +# define HPET_T0_INT_ENABLE (1 << 2) +#define HPET_T0_COMPARATOR 0x108 + +#define FSEC_PER_NSEC 1000000 +#define NSEC_PER_USEC 1000 + +/* This function sets up the HPET timer to be in + * 32 bit periodic mode and not generating any interrupts. + * The timer counts upwards and when it reaches 0xffffffff it + * wraps to zero. The timer ticks at a constant rate in nanoseconds which + * is stored in hpet_period_nsec variable. + */ +void +hpet_init(void) +{ + uint32_t period; + uint32_t val; + + assert(hpet_addr != 0); + + /* Find out how often the HPET ticks in nanoseconds */ + period = HPET32(HPET_CAP_PERIOD); + hpet_period_nsec = period / FSEC_PER_NSEC; + printf("HPET ticks every %d nanoseconds\n", hpet_period_nsec); + + /* Disable HPET and legacy interrupt routing mode */ + val = HPET32(HPET_CFG); + val = val & ~(HPET_LEGACY_ROUTE | HPET_CFG_ENABLE); + HPET32(HPET_CFG) = val; + + /* Clear the counter */ + HPET32(HPET_COUNTER) = 0; + + /* Set up 32 bit periodic timer with no interrupts */ + val = HPET32(HPET_T0_CFG); + val = (val & ~HPET_T0_INT_ENABLE) | HPET_T0_32BIT_MODE | HPET_T0_TYPE_PERIODIC | HPET_T0_VAL_SET; + HPET32(HPET_T0_CFG) = val; + + /* Set comparator to max */ + HPET32(HPET_T0_COMPARATOR) = 0xffffffff; + + /* Enable the HPET */ + HPET32(HPET_CFG) |= HPET_CFG_ENABLE; + + printf("HPET enabled\n"); +} + +void +hpet_udelay(uint32_t us) +{ + uint32_t start, now; + uint32_t max_delay_us = 0xffffffff / NSEC_PER_USEC; + + if (us > max_delay_us) { + printf("HPET ERROR: Delay too long, %d usec, truncating to %d usec\n", + us, max_delay_us); + us = max_delay_us; + } + + /* Convert us to HPET ticks */ + us = (us * NSEC_PER_USEC) / hpet_period_nsec; + + start = HPET32(HPET_COUNTER); + do { + now = HPET32(HPET_COUNTER); + } while (now - start < us); +} + +void +hpet_mdelay(uint32_t ms) +{ + hpet_udelay(ms * 1000); +} + diff --git a/i386/i386/apic.h b/i386/i386/apic.h new file mode 100644 index 0000000..9eef0d8 --- /dev/null +++ b/i386/i386/apic.h @@ -0,0 +1,337 @@ +/* + * Copyright (c) 1994 The University of Utah and + * the Computer Systems Laboratory at the University of Utah (CSL). + * All rights reserved. + * + * Permission to use, copy, modify and distribute this software is hereby + * granted provided that (1) source code retains these copyright, permission, + * and disclaimer notices, and (2) redistributions including binaries + * reproduce the notices in supporting documentation, and (3) all advertising + * materials mentioning features or use of this software display the following + * acknowledgement: ``This product includes software developed by the + * Computer Systems Laboratory at the University of Utah.'' + * + * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS + * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF + * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * CSL requests users of this software to return to csl-dist@cs.utah.edu any + * improvements that they make and grant CSL redistribution rights. + * + * Author: Bryan Ford, University of Utah CSL + */ +#ifndef _IMPS_APIC_ +#define _IMPS_APIC_ + +#ifndef __ASSEMBLER__ + +#include + +typedef struct ApicReg { + uint32_t r; /* the actual register */ + uint32_t p[3]; /* pad to the next 128-bit boundary */ +} ApicReg; + +typedef struct ApicIoUnit { + ApicReg select; + ApicReg window; + ApicReg unused[2]; + ApicReg eoi; /* write the vector you wish to EOI to this reg */ +} ApicIoUnit; + +struct ioapic_route_entry { + uint32_t vector : 8, + delvmode : 3, /* 000=fixed 001=lowest 111=ExtInt */ + destmode : 1, /* 0=physical 1=logical */ + delvstatus : 1, + polarity : 1, /* 0=activehigh 1=activelow */ + irr : 1, + trigger : 1, /* 0=edge 1=level */ + mask : 1, /* 0=enabled 1=disabled */ + reserved1 : 15; + uint32_t reserved2 : 24, + dest : 8; +} __attribute__ ((packed)); + +union ioapic_route_entry_union { + struct { + uint32_t lo; + uint32_t hi; + }; + struct ioapic_route_entry both; +}; + + +/* Grateful to trasterlabs for this snippet */ + +typedef union u_icr_low +{ + uint32_t value[4]; + struct + { + uint32_t r; // FEE0 0300H - 4 bytes + unsigned :32; // FEE0 0304H + unsigned :32; // FEE0 0308H + unsigned :32; // FEE0 030CH + }; + struct + { + unsigned vector: 8; /* Vector of interrupt. Lowest 8 bits of routine address */ + unsigned delivery_mode : 3; + unsigned destination_mode: 1; + unsigned delivery_status: 1; + unsigned :1; + unsigned level: 1; + unsigned trigger_mode: 1; + unsigned remote_read_status: 2; /* Read-only field */ + unsigned destination_shorthand: 2; + unsigned :12; + }; +} IcrLReg; + +typedef union u_icr_high +{ + uint32_t value[4]; + struct + { + uint32_t r; // FEE0 0310H - 4 bytes + unsigned :32; // FEE0 0314H + unsigned :32; // FEE0 0318H + unsigned :32; // FEE0 031CH + }; + struct + { + unsigned :24; // FEE0 0310H - 4 bytes + unsigned destination_field :8; /* APIC ID (in physical mode) or MDA (in logical) of destination processor */ + }; +} IcrHReg; + + +typedef enum e_icr_dest_shorthand +{ + NO_SHORTHAND = 0, + SELF = 1, + ALL_INCLUDING_SELF = 2, + ALL_EXCLUDING_SELF = 3 +} icr_dest_shorthand; + +typedef enum e_icr_deliv_mode +{ + FIXED = 0, + LOWEST_PRIORITY = 1, + SMI = 2, + NMI = 4, + INIT = 5, + STARTUP = 6, +} icr_deliv_mode; + +typedef enum e_icr_dest_mode +{ + PHYSICAL = 0, + LOGICAL = 1 +} icr_dest_mode; + +typedef enum e_icr_deliv_status +{ + IDLE = 0, + SEND_PENDING = 1 +} icr_deliv_status; + +typedef enum e_icr_level +{ + DE_ASSERT = 0, + ASSERT = 1 +} icr_level; + +typedef enum e_irc_trigger_mode +{ + EDGE = 0, + LEVEL = 1 +} irc_trigger_mode; + + +typedef struct ApicLocalUnit { + ApicReg reserved0; /* 0x000 */ + ApicReg reserved1; /* 0x010 */ + ApicReg apic_id; /* 0x020. Hardware ID of current processor */ + ApicReg version; /* 0x030 */ + ApicReg reserved4; /* 0x040 */ + ApicReg reserved5; /* 0x050 */ + ApicReg reserved6; /* 0x060 */ + ApicReg reserved7; /* 0x070 */ + ApicReg task_pri; /* 0x080 */ + ApicReg arbitration_pri; /* 0x090 */ + ApicReg processor_pri; /* 0x0a0 */ + ApicReg eoi; /* 0x0b0 */ + ApicReg remote; /* 0x0c0 */ + ApicReg logical_dest; /* 0x0d0 */ + ApicReg dest_format; /* 0x0e0 */ + ApicReg spurious_vector; /* 0x0f0 */ + ApicReg isr[8]; /* 0x100 */ + ApicReg tmr[8]; /* 0x180 */ + ApicReg irr[8]; /* 0x200 */ + ApicReg error_status; /* 0x280 */ + ApicReg reserved28[6]; /* 0x290 */ + ApicReg lvt_cmci; /* 0x2f0 */ + IcrLReg icr_low; /* 0x300. Store the information to send an IPI (Inter-processor Interrupt) */ + IcrHReg icr_high; /* 0x310. Store the IPI destination */ + ApicReg lvt_timer; /* 0x320 */ + ApicReg lvt_thermal; /* 0x330 */ + ApicReg lvt_performance_monitor; /* 0x340 */ + ApicReg lvt_lint0; /* 0x350 */ + ApicReg lvt_lint1; /* 0x360 */ + ApicReg lvt_error; /* 0x370 */ + ApicReg init_count; /* 0x380 */ + ApicReg cur_count; /* 0x390 */ + ApicReg reserved3a; /* 0x3a0 */ + ApicReg reserved3b; /* 0x3b0 */ + ApicReg reserved3c; /* 0x3c0 */ + ApicReg reserved3d; /* 0x3d0 */ + ApicReg divider_config; /* 0x3e0 */ + ApicReg reserved3f; /* 0x3f0 */ +} ApicLocalUnit; + +typedef struct IoApicData { + uint8_t apic_id; + uint8_t ngsis; + uint32_t addr; + uint32_t gsi_base; + ApicIoUnit *ioapic; +} IoApicData; + +#define APIC_IRQ_OVERRIDE_POLARITY_MASK 1 +#define APIC_IRQ_OVERRIDE_ACTIVE_LOW 2 +#define APIC_IRQ_OVERRIDE_TRIGGER_MASK 4 +#define APIC_IRQ_OVERRIDE_LEVEL_TRIGGERED 8 + +typedef struct IrqOverrideData { + uint8_t bus; + uint8_t irq; + uint32_t gsi; + uint16_t flags; +} IrqOverrideData; + +#define MAX_IOAPICS 16 +#define MAX_IRQ_OVERRIDE 24 + +typedef struct ApicInfo { + uint8_t ncpus; + uint8_t nioapics; + int nirqoverride; + uint16_t* cpu_lapic_list; + struct IoApicData ioapic_list[MAX_IOAPICS]; + struct IrqOverrideData irq_override_list[MAX_IRQ_OVERRIDE]; +} ApicInfo; + +int apic_data_init(void); +void apic_add_cpu(uint16_t apic_id); +void apic_lapic_init(ApicLocalUnit* lapic_ptr); +void apic_add_ioapic(struct IoApicData); +void apic_add_irq_override(struct IrqOverrideData irq_over); +void apic_send_ipi(unsigned dest_shorthand, unsigned deliv_mode, unsigned dest_mode, unsigned level, unsigned trig_mode, unsigned vector, unsigned dest_id); +IrqOverrideData *acpi_get_irq_override(uint8_t gsi); +int apic_get_cpu_apic_id(int kernel_id); +int apic_get_cpu_kernel_id(uint16_t apic_id); +volatile ApicLocalUnit* apic_get_lapic(void); +struct IoApicData *apic_get_ioapic(int kernel_id); +uint8_t apic_get_numcpus(void); +uint8_t apic_get_num_ioapics(void); +int apic_get_current_cpu(void); +void apic_print_info(void); +int apic_refit_cpulist(void); +void apic_generate_cpu_id_lut(void); +int apic_get_total_gsis(void); +void picdisable(void); +void lapic_eoi(void); +void ioapic_irq_eoi(int pin); +void lapic_setup(void); +void lapic_disable(void); +void lapic_enable(void); +void lapic_enable_timer(void); +void calibrate_lapic_timer(void); +void ioapic_toggle(int pin, int mask); +void ioapic_configure(void); + +void hpet_init(void); +void hpet_udelay(uint32_t us); +void hpet_mdelay(uint32_t ms); + +extern int timer_pin; +extern void intnull(int unit); +extern volatile ApicLocalUnit* lapic; +extern int cpu_id_lut[]; +extern uint32_t *hpet_addr; + +#endif + +#define APIC_IO_UNIT_ID 0x00 +#define APIC_IO_VERSION 0x01 +# define APIC_IO_VERSION_SHIFT 0 +# define APIC_IO_ENTRIES_SHIFT 16 +#define APIC_IO_REDIR_LOW(int_pin) (0x10+(int_pin)*2) +#define APIC_IO_REDIR_HIGH(int_pin) (0x11+(int_pin)*2) + +#define IMCR_SELECT 0x22 +#define IMCR_DATA 0x23 +#define MODE_IMCR 0x70 +# define IMCR_USE_PIC 0 +# define IMCR_USE_APIC 1 + +#define LAPIC_LOW_PRIO 0x100 +#define LAPIC_NMI 0x400 +#define LAPIC_EXTINT 0x700 +#define LAPIC_LEVEL_TRIGGERED 0x8000 + +#define LAPIC_ENABLE 0x100 +#define LAPIC_FOCUS 0x200 +#define LAPIC_ENABLE_DIRECTED_EOI 0x1000 +#define LAPIC_DISABLE 0x10000 +#define LAPIC_TIMER_PERIODIC 0x20000 +#define LAPIC_TIMER_DIVIDE_2 0 +#define LAPIC_TIMER_DIVIDE_4 1 +#define LAPIC_TIMER_DIVIDE_8 2 +#define LAPIC_TIMER_DIVIDE_16 3 +#define LAPIC_TIMER_BASEDIV 0x100000 +#define LAPIC_HAS_DIRECTED_EOI 0x1000000 + +#define NINTR 64 /* Max 32 GSIs on each of two IOAPICs */ +#define IOAPIC_FIXED 0 +#define IOAPIC_PHYSICAL 0 +#define IOAPIC_LOGICAL 1 +#define IOAPIC_NMI 4 +#define IOAPIC_EXTINT 7 +#define IOAPIC_ACTIVE_HIGH 0 +#define IOAPIC_ACTIVE_LOW 1 +#define IOAPIC_EDGE_TRIGGERED 0 +#define IOAPIC_LEVEL_TRIGGERED 1 +#define IOAPIC_MASK_ENABLED 0 +#define IOAPIC_MASK_DISABLED 1 + +#define APIC_MSR 0x1b +#define APIC_MSR_BSP 0x100 /* Processor is a BSP */ +#define APIC_MSR_X2APIC 0x400 /* LAPIC is in x2APIC mode */ +#define APIC_MSR_ENABLE 0x800 /* LAPIC is enabled */ + +/* Set or clear a bit in a 255-bit APIC mask register. + These registers are spread through eight 32-bit registers. */ +#define APIC_SET_MASK_BIT(reg, bit) \ + ((reg)[(bit) >> 5].r |= 1 << ((bit) & 0x1f)) +#define APIC_CLEAR_MASK_BIT(reg, bit) \ + ((reg)[(bit) >> 5].r &= ~(1 << ((bit) & 0x1f))) + +#ifndef __ASSEMBLER__ + +#ifdef APIC +static inline void mask_irq (unsigned int irq_nr) { + ioapic_toggle(irq_nr, IOAPIC_MASK_DISABLED); +} + +static inline void unmask_irq (unsigned int irq_nr) { + ioapic_toggle(irq_nr, IOAPIC_MASK_ENABLED); +} +#endif + +#endif /* !__ASSEMBLER__ */ + +#endif /*_IMPS_APIC_*/ + diff --git a/i386/i386/ast.h b/i386/i386/ast.h new file mode 100644 index 0000000..7afaa41 --- /dev/null +++ b/i386/i386/ast.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 "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. + */ + +#ifndef _I386_AST_H_ +#define _I386_AST_H_ + +/* + * Machine-dependent AST file for machines with no hardware AST support. + * + * For the I386, we define AST_I386_FP to handle delayed + * floating-point exceptions. The FPU may interrupt on errors + * while the user is not running (in kernel or other thread running). + */ + +#define AST_I386_FP 0x80000000 + +#define MACHINE_AST_PER_THREAD AST_I386_FP + + +/* Chain to the machine-independent header. */ +/* #include_next "ast.h" */ + + +#endif /* _I386_AST_H_ */ diff --git a/i386/i386/ast_check.c b/i386/i386/ast_check.c new file mode 100644 index 0000000..61cd5e8 --- /dev/null +++ b/i386/i386/ast_check.c @@ -0,0 +1,56 @@ +/* + * 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. + */ + +#if NCPUS > 1 + +/* + * Handle signalling ASTs on other processors. + * + * Initial i386 implementation does nothing. + */ + +#include +#include +#include +#include +#include + +/* + * Initialize for remote invocation of ast_check. + */ +void init_ast_check(const processor_t processor) +{ +} + +/* + * Cause remote invocation of ast_check. Caller is at splsched(). + */ +void cause_ast_check(const processor_t processor) +{ + smp_remote_ast(apic_get_cpu_apic_id(processor->slot_num)); +} + +#endif /* NCPUS > 1 */ diff --git a/i386/i386/ast_types.h b/i386/i386/ast_types.h new file mode 100644 index 0000000..89e3182 --- /dev/null +++ b/i386/i386/ast_types.h @@ -0,0 +1,36 @@ +/* + * 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. + */ + +#ifndef _I386_AST_TYPES_H_ +#define _I386_AST_TYPES_H_ + +/* + * Data type for remote ast_check() invocation support. Currently + * not implemented. Do this first to avoid include problems. + */ +typedef int ast_check_t; + +#endif /* _I386_AST_TYPES_H_ */ diff --git a/i386/i386/copy_user.h b/i386/i386/copy_user.h new file mode 100644 index 0000000..3d1c727 --- /dev/null +++ b/i386/i386/copy_user.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2023 Free Software Foundation + * + * This program is free software ; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation ; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY ; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the program ; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef COPY_USER_H +#define COPY_USER_H + +#include +#include + +#include +#include + +/* + * The copyin_32to64() and copyout_64to32() routines are meant for data types + * that have different size in kernel and user space. They should be independent + * of endianness and hopefully can be reused in the future on other archs. + * These types are e.g.: + * - port names vs port pointers, on a 64-bit kernel + * - memory addresses, on a 64-bit kernel and 32-bit user + */ + +static inline int copyin_32to64(const uint32_t *uaddr, uint64_t *kaddr) +{ + uint32_t rkaddr; + int ret; + ret = copyin(uaddr, &rkaddr, sizeof(uint32_t)); + if (ret) + return ret; + *kaddr = rkaddr; + return 0; +} + +static inline int copyout_64to32(const uint64_t *kaddr, uint32_t *uaddr) +{ + uint32_t rkaddr=*kaddr; + return copyout(&rkaddr, uaddr, sizeof(uint32_t)); +} + +static inline int copyin_address(const rpc_vm_offset_t *uaddr, vm_offset_t *kaddr) +{ +#ifdef USER32 + return copyin_32to64(uaddr, kaddr); +#else /* USER32 */ + return copyin(uaddr, kaddr, sizeof(*uaddr)); +#endif /* USER32 */ +} + +static inline int copyout_address(const vm_offset_t *kaddr, rpc_vm_offset_t *uaddr) +{ +#ifdef USER32 + return copyout_64to32(kaddr, uaddr); +#else /* USER32 */ + return copyout(kaddr, uaddr, sizeof(*kaddr)); +#endif /* USER32 */ +} + +static inline int copyin_port(const mach_port_name_t *uaddr, mach_port_t *kaddr) +{ +#ifdef __x86_64__ + return copyin_32to64(uaddr, kaddr); +#else /* __x86_64__ */ + return copyin(uaddr, kaddr, sizeof(*uaddr)); +#endif /* __x86_64__ */ +} + +static inline int copyout_port(const mach_port_t *kaddr, mach_port_name_t *uaddr) +{ +#ifdef __x86_64__ + return copyout_64to32(kaddr, uaddr); +#else /* __x86_64__ */ + return copyout(kaddr, uaddr, sizeof(*kaddr)); +#endif /* __x86_64__ */ +} + +#if defined(__x86_64__) && defined(USER32) +/* For 32 bit userland, kernel and user land messages are not the same size. */ +size_t msg_usize(const mach_msg_header_t *kmsg); +#else +static inline size_t msg_usize(const mach_msg_header_t *kmsg) +{ + return kmsg->msgh_size; +} +#endif /* __x86_64__ && USER32 */ + +#endif /* COPY_USER_H */ diff --git a/i386/i386/cpu.h b/i386/i386/cpu.h new file mode 100644 index 0000000..1bf40dc --- /dev/null +++ b/i386/i386/cpu.h @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2010-2014 Richard Braun. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef _X86_CPU_H +#define _X86_CPU_H + +#include + +/* + * EFLAGS register flags. + */ +#define CPU_EFL_ONE 0x00000002 +#define CPU_EFL_IF 0x00000200 + +/* + * Return the content of the EFLAGS register. + * + * Implies a compiler barrier. + */ +static __always_inline unsigned long +cpu_get_eflags(void) +{ + unsigned long eflags; + + asm volatile("pushf\n" + "pop %0\n" + : "=r" (eflags) + : : "memory"); + + return eflags; +} + +/* + * Enable local interrupts. + * + * Implies a compiler barrier. + */ +static __always_inline void +cpu_intr_enable(void) +{ + asm volatile("sti" : : : "memory"); +} + +/* + * Disable local interrupts. + * + * Implies a compiler barrier. + */ +static __always_inline void +cpu_intr_disable(void) +{ + asm volatile("cli" : : : "memory"); +} + +/* + * Restore the content of the EFLAGS register, possibly enabling interrupts. + * + * Implies a compiler barrier. + */ +static __always_inline void +cpu_intr_restore(unsigned long flags) +{ + asm volatile("push %0\n" + "popf\n" + : : "r" (flags) + : "memory"); +} + +/* + * Disable local interrupts, returning the previous content of the EFLAGS + * register. + * + * Implies a compiler barrier. + */ +static __always_inline void +cpu_intr_save(unsigned long *flags) +{ + *flags = cpu_get_eflags(); + cpu_intr_disable(); +} + +/* + * Return true if interrupts are enabled. + * + * Implies a compiler barrier. + */ +static __always_inline int +cpu_intr_enabled(void) +{ + unsigned long eflags; + + eflags = cpu_get_eflags(); + return (eflags & CPU_EFL_IF) ? 1 : 0; +} + +#endif /* _X86_CPU_H */ diff --git a/i386/i386/cpu_number.h b/i386/i386/cpu_number.h new file mode 100644 index 0000000..67c19e9 --- /dev/null +++ b/i386/i386/cpu_number.h @@ -0,0 +1,119 @@ +/* + * 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. + */ +/* + * Machine-dependent definitions for cpu identification. + * + */ +#ifndef _I386_CPU_NUMBER_H_ +#define _I386_CPU_NUMBER_H_ + +#if NCPUS > 1 + +#define MY(stm) %gs:PERCPU_##stm + +#ifdef __i386__ +#define CX(addr, reg) addr(,reg,4) +#endif +#ifdef __x86_64__ +#define CX(addr, reg) addr(,reg,8) +#endif + +#define CPU_NUMBER_NO_STACK(reg) \ + movl %cs:lapic, reg ;\ + movl %cs:APIC_ID(reg), reg ;\ + shrl $24, reg ;\ + movl %cs:CX(cpu_id_lut, reg), reg ;\ + +#ifdef __i386__ +/* Never call CPU_NUMBER_NO_GS(%esi) */ +#define CPU_NUMBER_NO_GS(reg) \ + pushl %esi ;\ + pushl %eax ;\ + pushl %ebx ;\ + pushl %ecx ;\ + pushl %edx ;\ + movl $1, %eax ;\ + cpuid ;\ + shrl $24, %ebx ;\ + movl %cs:CX(cpu_id_lut, %ebx), %esi ;\ + popl %edx ;\ + popl %ecx ;\ + popl %ebx ;\ + popl %eax ;\ + movl %esi, reg ;\ + popl %esi +#endif +#ifdef __x86_64__ +/* Never call CPU_NUMBER_NO_GS(%esi) */ +#define CPU_NUMBER_NO_GS(reg) \ + pushq %rsi ;\ + pushq %rax ;\ + pushq %rbx ;\ + pushq %rcx ;\ + pushq %rdx ;\ + movl $1, %eax ;\ + cpuid ;\ + shrl $24, %ebx ;\ + movl %cs:CX(cpu_id_lut, %ebx), %esi ;\ + popq %rdx ;\ + popq %rcx ;\ + popq %rbx ;\ + popq %rax ;\ + movl %esi, reg ;\ + popq %rsi +#endif + +#define CPU_NUMBER(reg) \ + movl MY(CPU_ID), reg; + +#ifndef __ASSEMBLER__ +#include +#include +#include + +static inline int cpu_number_slow(void) +{ + return cpu_id_lut[apic_get_current_cpu()]; +} + +static inline int cpu_number(void) +{ + return percpu_get(int, cpu_id); +} +#endif + +#else /* NCPUS == 1 */ + +#define MY(stm) (percpu_array + PERCPU_##stm) + +#define CPU_NUMBER_NO_STACK(reg) +#define CPU_NUMBER_NO_GS(reg) +#define CPU_NUMBER(reg) +#define CX(addr,reg) addr + +#endif /* NCPUS == 1 */ + +#endif /* _I386_CPU_NUMBER_H_ */ diff --git a/i386/i386/cpuboot.S b/i386/i386/cpuboot.S new file mode 100644 index 0000000..7e6c477 --- /dev/null +++ b/i386/i386/cpuboot.S @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2022 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#if NCPUS > 1 +#include +#include +#include +#include +#include +#include +#include + +#define M(addr) (addr - apboot) +#define CR0_CLEAR_FLAGS_CACHE_ENABLE (CR0_CD | CR0_NW) +#define CR0_SET_FLAGS (CR0_CLEAR_FLAGS_CACHE_ENABLE | CR0_PE) +#define CR0_CLEAR_FLAGS (CR0_PG | CR0_AM | CR0_WP | CR0_NE | CR0_TS | CR0_EM | CR0_MP) +#define BOOT_CS 0x8 +#define BOOT_DS 0x10 + +.data + +.align 16 +apboot_idt_ptr: + .long 0 +.align 16 + .word 0 +apboot_gdt_descr: + .word 14*8-1 + .long apboot_gdt - KERNELBASE +.align 16 +apboot_gdt: + /* NULL segment = 0x0 */ + .quad 0 + + /* KERNEL_CS = 0x8 */ + .word 0xffff /* Segment limit first 0-15 bits*/ + .word (-KERNELBASE) & 0xffff /*Base first 0-15 bits*/ + .byte ((-KERNELBASE) >> 16) & 0xff /*Base 16-23 bits */ + .byte ACC_PL_K | ACC_CODE_R | ACC_P /*Access byte */ + .byte ((SZ_32 | SZ_G) << 4) | 0xf /* High 4 bits */ + .byte ((-KERNELBASE) >> 24) & 0xff /*Base 24-31 bits */ + + /* KERNEL_DS = 0x10 */ + .word 0xffff /*Segment limit */ + .word (-KERNELBASE) & 0xffff /*Base first 0-15 bits*/ + .byte ((-KERNELBASE) >> 16) & 0xff + .byte ACC_PL_K | ACC_DATA_W | ACC_P /*Access byte*/ + .byte ((SZ_32 | SZ_G) << 4) | 0xf /* High 4 bits */ + .byte ((-KERNELBASE) >> 24) & 0xff /*Base 24-31 bits */ + + /* LDT = 0x18 */ + .quad 0 + + /* TSS = 0x20 */ + .quad 0 + + /* USER_LDT = 0x28 */ + .quad 0 + + /* USER_TSS = 0x30 */ + .quad 0 + + /* LINEAR = 0x38 */ + .quad 0 + + /* FPREGS = 0x40 */ + .quad 0 + + /* USER_GDT = 0x48 and 0x50 */ + .quad 0 + .quad 0 + + /* USER_TSS64 = 0x58 */ + .quad 0 + + /* USER_TSS64 = 0x60 */ + .quad 0 + + /* boot GS = 0x68 */ + .word 0xffff +apboot_percpu_low: + .word 0 +apboot_percpu_med: + .byte 0 + .byte ACC_PL_K | ACC_DATA_W | ACC_P + .byte ((SZ_32 | SZ_G) << 4) | 0xf +apboot_percpu_high: + .byte 0 + +.globl apboot, apbootend, gdt_descr_tmp, apboot_jmp_offset +.align 16 +.code16 + +apboot: +_apboot: + /* This is now address CS:0 in real mode */ + + /* Set data seg same as code seg */ + mov %cs, %dx + mov %dx, %ds + + cli + xorl %eax, %eax + movl %eax, %cr3 + + mov %ax, %es + mov %ax, %fs + mov %ax, %gs + mov %ax, %ss + + lgdt M(gdt_descr_tmp) + + movl %cr0, %eax + andl $~CR0_CLEAR_FLAGS, %eax + orl $CR0_SET_FLAGS, %eax + movl %eax, %cr0 + + /* ljmpl with relocation from machine_init */ + .byte 0x66 + .byte 0xea +apboot_jmp_offset: + .long M(0f) + .word BOOT_CS + +0: + .code32 + /* Protected mode! */ + movw $BOOT_DS, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %ss + + lgdtl apboot_gdt_descr - KERNELBASE + ljmpl $KERNEL_CS, $1f +1: + xorl %eax, %eax + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + movw $KERNEL_DS, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + movw %ax, %ss + + /* Get CPU number */ + movl $1, %eax + cpuid + shrl $24, %ebx + movl %cs:CX(cpu_id_lut, %ebx), %ecx + + /* Access per_cpu area */ + movl %ecx,%eax + movl $PC_SIZE,%ebx + mul %ebx + addl $percpu_array - KERNELBASE, %eax + + /* Record our cpu number */ + movl %ecx, (PERCPU_CPU_ID + KERNELBASE)(%eax) + + /* Set up temporary percpu descriptor */ + movw %ax, apboot_percpu_low + shr $16, %eax + movb %al, apboot_percpu_med + shr $8, %ax + movb %al, apboot_percpu_high + + movw $PERCPU_DS, %ax + movw %ax, %gs + + /* Load null Interrupt descriptor table */ + mov apboot_idt_ptr, %ebx + lidt (%ebx) + + /* Enable local apic in xAPIC mode */ + xorl %eax, %eax + xorl %edx, %edx + movl $APIC_MSR, %ecx + rdmsr + orl $APIC_MSR_ENABLE, %eax + andl $(~(APIC_MSR_BSP | APIC_MSR_X2APIC)), %eax + movl $APIC_MSR, %ecx + wrmsr + + /* Load int_stack_top[cpu] -> esp */ + CPU_NUMBER_NO_STACK(%edx) + movl CX(EXT(int_stack_top), %edx), %esp + + /* Ensure stack alignment */ + andl $0xfffffff0, %esp + + /* Reset EFLAGS to a known state */ + pushl $0 + popfl + + /* Finish the cpu configuration */ + call EXT(cpu_ap_main) + + /* NOT REACHED */ + hlt + +.align 16 + .word 0 +gdt_descr_tmp: + .short 3*8-1 + .long M(gdt_tmp) + +.align 16 +gdt_tmp: + /* 0 */ + .quad 0 + /* BOOT_CS */ + .word 0xffff + .word 0x0000 + .byte 0x00 + .byte ACC_PL_K | ACC_CODE_R | ACC_P + .byte ((SZ_32 | SZ_G) << 4) | 0xf + .byte 0x00 + /* BOOT_DS */ + .word 0xffff + .word 0x0000 + .byte 0x00 + .byte ACC_PL_K | ACC_DATA_W | ACC_P + .byte ((SZ_32 | SZ_G) << 4) | 0xf + .byte 0x00 + +_apbootend: +apbootend: +#endif diff --git a/i386/i386/cswitch.S b/i386/i386/cswitch.S new file mode 100644 index 0000000..2dee309 --- /dev/null +++ b/i386/i386/cswitch.S @@ -0,0 +1,139 @@ +/* + * 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. + */ + +#include + +#include +#include +#include +#include + +/* + * Context switch routines for i386. + */ + +ENTRY(Load_context) + movl S_ARG0,%ecx /* get thread */ + movl TH_KERNEL_STACK(%ecx),%ecx /* get kernel stack */ + lea KERNEL_STACK_SIZE-IKS_SIZE-IEL_SIZE(%ecx),%edx + /* point to stack top */ + CPU_NUMBER(%eax) + movl %ecx,MY(ACTIVE_STACK) /* store stack address */ + movl %edx,CX(EXT(kernel_stack),%eax) /* store stack top */ + + movl KSS_ESP(%ecx),%esp /* switch stacks */ + movl KSS_ESI(%ecx),%esi /* restore registers */ + movl KSS_EDI(%ecx),%edi + movl KSS_EBP(%ecx),%ebp + movl KSS_EBX(%ecx),%ebx + xorl %eax,%eax /* return zero (no old thread) */ + jmp *KSS_EIP(%ecx) /* resume thread */ + +/* + * This really only has to save registers + * when there is no explicit continuation. + */ + +ENTRY(Switch_context) + movl MY(ACTIVE_STACK),%ecx /* get old kernel stack */ + + movl %ebx,KSS_EBX(%ecx) /* save registers */ + movl %ebp,KSS_EBP(%ecx) + movl %edi,KSS_EDI(%ecx) + movl %esi,KSS_ESI(%ecx) + popl KSS_EIP(%ecx) /* save return PC */ + movl %esp,KSS_ESP(%ecx) /* save SP */ + + movl 0(%esp),%eax /* get old thread */ + movl %ecx,TH_KERNEL_STACK(%eax) /* save old stack */ + movl 4(%esp),%ebx /* get continuation */ + movl %ebx,TH_SWAP_FUNC(%eax) /* save continuation */ + + movl 8(%esp),%esi /* get new thread */ + + movl TH_KERNEL_STACK(%esi),%ecx /* get its kernel stack */ + lea KERNEL_STACK_SIZE-IKS_SIZE-IEL_SIZE(%ecx),%ebx + /* point to stack top */ + + CPU_NUMBER(%edx) + movl %esi,MY(ACTIVE_THREAD) /* new thread is active */ + movl %ecx,MY(ACTIVE_STACK) /* set current stack */ + movl %ebx,CX(EXT(kernel_stack),%edx) /* set stack top */ + + movl KSS_ESP(%ecx),%esp /* switch stacks */ + movl KSS_ESI(%ecx),%esi /* restore registers */ + movl KSS_EDI(%ecx),%edi + movl KSS_EBP(%ecx),%ebp + movl KSS_EBX(%ecx),%ebx + jmp *KSS_EIP(%ecx) /* return old thread */ + +ENTRY(Thread_continue) + pushl %eax /* push the thread argument */ + xorl %ebp,%ebp /* zero frame pointer */ + call *%ebx /* call real continuation */ + +#if NCPUS > 1 +/* + * void switch_to_shutdown_context(thread_t thread, + * void (*routine)(processor_t), + * processor_t processor) + * + * saves the kernel context of the thread, + * switches to the interrupt stack, + * continues the thread (with thread_continue), + * then runs routine on the interrupt stack. + * + * Assumes that the thread is a kernel thread (thus + * has no FPU state) + */ +ENTRY(switch_to_shutdown_context) + movl MY(ACTIVE_STACK),%ecx /* get old kernel stack */ + movl %ebx,KSS_EBX(%ecx) /* save registers */ + movl %ebp,KSS_EBP(%ecx) + movl %edi,KSS_EDI(%ecx) + movl %esi,KSS_ESI(%ecx) + popl KSS_EIP(%ecx) /* save return PC */ + movl %esp,KSS_ESP(%ecx) /* save SP */ + + movl 0(%esp),%eax /* get old thread */ + movl %ecx,TH_KERNEL_STACK(%eax) /* save old stack */ + movl $0,TH_SWAP_FUNC(%eax) /* clear continuation */ + movl 4(%esp),%ebx /* get routine to run next */ + movl 8(%esp),%esi /* get its argument */ + + CPU_NUMBER(%edx) + movl CX(EXT(int_stack_base),%edx),%ecx /* point to its interrupt stack */ + lea -4+INTSTACK_SIZE(%ecx),%esp /* switch to it (top) */ + + pushl %eax /* push thread */ + call EXT(thread_dispatch) /* reschedule thread */ + addl $4,%esp /* clean stack */ + + pushl %esi /* push argument */ + call *%ebx /* call routine to run */ + hlt /* (should never return) */ + +#endif /* NCPUS > 1 */ diff --git a/i386/i386/db_disasm.c b/i386/i386/db_disasm.c new file mode 100644 index 0000000..303b462 --- /dev/null +++ b/i386/i386/db_disasm.c @@ -0,0 +1,1437 @@ +/* + * Mach Operating System + * Copyright (c) 1994,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. + */ + +#if MACH_KDB + +/* + * Instruction disassembler. + */ +#include +#include + +#include +#include +#include +#include + +#include + +/* + * Switch to disassemble 16-bit code. + */ +boolean_t db_disasm_16 = FALSE; + +/* + * 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 El 3 /* address, long size */ +#define Ew 4 /* address, word size */ +#define Eb 5 /* address, byte size */ +#define R 6 /* register, in 'reg' field */ +#define Rw 7 /* word register, in 'reg' field */ +#define Ri 8 /* register in instruction */ +#define S 9 /* segment reg, in 'reg' field */ +#define Si 10 /* segment reg, in instruction */ +#define A 11 /* accumulator */ +#define BX 12 /* (bx) */ +#define CL 13 /* cl, for shifts */ +#define DX 14 /* dx, for IO */ +#define SI 15 /* si */ +#define DI 16 /* di */ +#define CR 17 /* control register */ +#define DR 18 /* debug register */ +#define TR 19 /* test register */ +#define I 20 /* immediate, unsigned */ +#define Is 21 /* immediate, signed */ +#define Ib 22 /* byte immediate, unsigned */ +#define Ibs 23 /* byte immediate, signed */ +#define Iw 24 /* word immediate, unsigned */ +#define Il 25 /* long immediate */ +#define O 26 /* direct address */ +#define Db 27 /* byte displacement from EIP */ +#define Dl 28 /* long displacement from EIP */ +#define o1 29 /* constant 1 */ +#define o3 30 /* constant 3 */ +#define OS 31 /* immediate offset/segment */ +#define ST 32 /* FP stack top */ +#define STI 33 /* FP stack */ +#define X 34 /* extended FP op */ +#define XA 35 /* for 'fstcw %ax' */ +#define Iba 36 /* byte immediate, don't print if 0xa */ + +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*/ { "ud2", 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,El), 0 }, /* use El for reg */ +/*21*/ { "mov", TRUE, LONG, op2(DR,El), 0 }, /* since mod == 11 */ +/*22*/ { "mov", TRUE, LONG, op2(El,CR), 0 }, +/*23*/ { "mov", TRUE, LONG, op2(El,DR), 0 }, +/*24*/ { "mov", TRUE, LONG, op2(TR,El), 0 }, +/*25*/ { "", FALSE, NONE, 0, 0 }, +/*26*/ { "mov", TRUE, LONG, op2(El,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(R,E), 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(R,E), 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*/ { "btr", 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(Eb,R), 0 }, +/*b7*/ { "movzw", TRUE, LONG, op2(Ew,R), 0 }, + +/*b8*/ { "", FALSE, NONE, 0, 0 }, +/*b9*/ { "", FALSE, NONE, 0, 0 }, +/*ba*/ { "", TRUE, LONG, op2(Ibs,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(Eb,R), 0 }, +/*bf*/ { "movsw", TRUE, LONG, op2(Ew,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(DI), 0 }, +/*af*/ { "scas", FALSE, LONG, op1(DI), 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", FALSE, NONE, op1(Iba), 0 }, +/*d5*/ { "aad", FALSE, NONE, op1(Iba), 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, task) \ + result = db_get_task_value((loc), (size), (is_signed), (task)); \ + (loc) += (size); + +/* + * Read address at location and return updated location. + */ +static db_addr_t +db_read_address( + db_addr_t loc, + int short_addr, + int regmodrm, + struct i_addr *addrp, /* out */ + task_t task) +{ + int mod, rm, sib, index, 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, task); + 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, task); + addrp->disp = disp; + addrp->base = db_index_reg_16[rm]; + break; + case 2: + get_value_inc(disp, loc, 2, TRUE, task); + addrp->disp = disp; + addrp->base = db_index_reg_16[rm]; + break; + } + } + else { + if (mod != 3 && rm == 4) { + get_value_inc(sib, loc, 1, FALSE, task); + 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, task); + addrp->base = 0; + } + else { + addrp->disp = 0; + addrp->base = db_reg[LONG][rm]; + } + break; + + case 1: + get_value_inc(disp, loc, 1, TRUE, task); + addrp->disp = disp; + addrp->base = db_reg[LONG][rm]; + break; + + case 2: + get_value_inc(disp, loc, 4, FALSE, task); + addrp->disp = disp; + addrp->base = db_reg[LONG][rm]; + break; + } + } + return loc; +} + +static void +db_print_address( + const char * seg, + int size, + const struct i_addr *addrp, + task_t task) +{ + if (addrp->is_reg) { + db_printf("%s", db_reg[size][addrp->disp]); + return; + } + + if (seg) { + db_printf("%s:", seg); + } + + if (addrp->base != 0 || addrp->index != 0) { + db_printf("%#n", addrp->disp); + db_printf("("); + if (addrp->base) + db_printf("%s", addrp->base); + if (addrp->index) + db_printf(",%s,%d", addrp->index, 1<ss); + db_printf(")"); + } else + db_task_printsym((db_addr_t)addrp->disp, DB_STGY_ANY, task); +} + +/* + * Disassemble floating-point ("escape") instruction + * and return updated location. + */ +static db_addr_t +db_disasm_esc( + db_addr_t loc, + int inst, + int short_addr, + int size, + const char * seg, + task_t task) +{ + int regmodrm; + struct finst *fp; + int mod; + struct i_addr address; + char * name; + + get_value_inc(regmodrm, loc, 1, FALSE, task); + 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, task); + 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, task); + } + 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(""); + 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( + db_addr_t loc, + boolean_t altfmt, + task_t task) +{ + int inst; + int size; + int short_addr; + char * seg; + struct inst * ip; + char * i_name; + int i_size; + int i_mode; + int regmodrm; + boolean_t first; + int displ; + int prefix; + int imm; + int imm2; + int len; + struct i_addr address; + +#ifdef __x86_64__ + /* The instruction set decoding needs an update, avoid showing bogus output. */ + db_printf("TODO\n"); + return loc+1; +#endif + + get_value_inc(inst, loc, 1, FALSE, task); + if (db_disasm_16) { + short_addr = TRUE; + size = WORD; + } + else { + short_addr = FALSE; + size = LONG; + } + seg = 0; + regmodrm = 0; + + /* + * Get prefixes + */ + prefix = TRUE; + do { + switch (inst) { + case 0x66: /* data16 */ + if (size == LONG) + size = WORD; + else + size = LONG; + break; + case 0x67: + short_addr = !short_addr; + 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, task); + } + } while (prefix); + + if (inst >= 0xd8 && inst <= 0xdf) { + loc = db_disasm_esc(loc, inst, short_addr, size, seg, task); + db_printf("\n"); + return loc; + } + + if (inst == 0x0f) { + get_value_inc(inst, loc, 1, FALSE, task); + 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, task); + loc = db_read_address(loc, short_addr, regmodrm, &address, task); + } + + 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, task); + break; + + case Eind: + db_printf("*"); + db_print_address(seg, size, &address, task); + break; + + case El: + db_print_address(seg, LONG, &address, task); + break; + + case Ew: + db_print_address(seg, WORD, &address, task); + break; + + case Eb: + db_print_address(seg, BYTE, &address, task); + 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, task);/* unsigned */ + db_printf("$%#n", imm); + break; + + case Is: + len = db_lengths[size]; + get_value_inc(imm, loc, len, TRUE, task); /* signed */ + db_printf("$%#r", imm); + break; + + case Ib: + get_value_inc(imm, loc, 1, FALSE, task); /* unsigned */ + db_printf("$%#n", imm); + break; + + case Iba: + get_value_inc(imm, loc, 1, FALSE, task); + if (imm != 0x0a) + db_printf("$%#r", imm); + break; + + case Ibs: + get_value_inc(imm, loc, 1, TRUE, task); /* signed */ + db_printf("$%#r", imm); + break; + + case Iw: + get_value_inc(imm, loc, 2, FALSE, task); /* unsigned */ + db_printf("$%#n", imm); + break; + + case Il: + get_value_inc(imm, loc, 4, FALSE, task); + db_printf("$%#n", imm); + break; + + case O: + if (short_addr) { + get_value_inc(displ, loc, 2, TRUE, task); + } + else { + get_value_inc(displ, loc, 4, TRUE, task); + } + if (seg) + db_printf("%s:%#r",seg, displ); + else + db_task_printsym((db_addr_t)displ, DB_STGY_ANY, task); + break; + + case Db: + get_value_inc(displ, loc, 1, TRUE, task); + if (short_addr) { + /* offset only affects low 16 bits */ + displ = (loc & 0xffff0000) + | ((loc + displ) & 0xffff); + } + else + displ = displ + loc; + db_task_printsym((db_addr_t)displ,DB_STGY_XTRN,task); + break; + + case Dl: + if (short_addr) { + get_value_inc(displ, loc, 2, TRUE, task); + /* offset only affects low 16 bits */ + displ = (loc & 0xffff0000) + | ((loc + displ) & 0xffff); + } + else { + get_value_inc(displ, loc, 4, TRUE, task); + displ = displ + loc; + } + db_task_printsym((db_addr_t)displ, DB_STGY_XTRN, task); + break; + + case o1: + db_printf("$1"); + break; + + case o3: + db_printf("$3"); + break; + + case OS: + if (short_addr) { + get_value_inc(imm, loc, 2, FALSE, task); /* offset */ + } + else { + get_value_inc(imm, loc, 4, FALSE, task); /* offset */ + } + get_value_inc(imm2, loc, 2, FALSE, task); /* segment */ + db_printf("$%#n,%#n", imm2, imm); + break; + } + } + + if (altfmt == 0 && !db_disasm_16) { + if (inst == 0xe9 || inst == 0xeb) { + /* + * GAS pads to longword boundary after unconditional jumps. + */ + loc = (loc + (4-1)) & ~(4-1); + } + } + db_printf("\n"); + return loc; +} + +#endif /* MACH_KDB */ diff --git a/i386/i386/db_interface.c b/i386/i386/db_interface.c new file mode 100644 index 0000000..483991d --- /dev/null +++ b/i386/i386/db_interface.c @@ -0,0 +1,865 @@ +/* + * Mach Operating System + * Copyright (c) 1993,1992,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. + */ +/* + * Interface to new debugger. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "gdt.h" +#include "trap.h" + +#include "vm_param.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if MACH_KDB +/* Whether the kernel uses any debugging register. */ +static boolean_t kernel_dr; +#endif +/* Whether the current debug registers are zero. */ +static boolean_t zero_dr; + +db_regs_t ddb_regs; + +void db_load_context(pcb_t pcb) +{ +#if MACH_KDB + int s = splhigh(); + + if (kernel_dr) { + splx(s); + return; + } +#endif + /* Else set user debug registers, if any */ + unsigned int *dr = pcb->ims.ids.dr; + boolean_t will_zero_dr = !dr[0] && !dr[1] && !dr[2] && !dr[3] && !dr[7]; + + if (!(zero_dr && will_zero_dr)) + { + set_dr0(dr[0]); + set_dr1(dr[1]); + set_dr2(dr[2]); + set_dr3(dr[3]); + set_dr7(dr[7]); + zero_dr = will_zero_dr; + } + +#if MACH_KDB + splx(s); +#endif +} + +void cpu_interrupt_to_db(int i){ +#if MACH_KDB && NCPUS > 1 + db_on(i); +#endif +} + +void db_get_debug_state( + pcb_t pcb, + struct i386_debug_state *state) +{ + *state = pcb->ims.ids; +} + +kern_return_t db_set_debug_state( + pcb_t pcb, + const struct i386_debug_state *state) +{ + int i; + + for (i = 0; i <= 3; i++) + if (state->dr[i] < VM_MIN_USER_ADDRESS + || state->dr[i] >= VM_MAX_USER_ADDRESS) + return KERN_INVALID_ARGUMENT; + + pcb->ims.ids = *state; + + if (pcb == current_thread()->pcb) + db_load_context(pcb); + + return KERN_SUCCESS; +} + +#if MACH_KDB + +struct i386_saved_state *i386_last_saved_statep; +struct i386_saved_state i386_nested_saved_state; +uintptr_t i386_last_kdb_sp; + +extern thread_t db_default_thread; + +static struct i386_debug_state ids; + +void db_dr ( + int num, + vm_offset_t linear_addr, + int type, + int len, + int persistence) +{ + int s = splhigh(); + unsigned long dr7; + + if (!kernel_dr) { + if (!linear_addr) { + splx(s); + return; + } + kernel_dr = TRUE; + /* Clear user debugging registers */ + set_dr7(0); + set_dr0(0); + set_dr1(0); + set_dr2(0); + set_dr3(0); + } + + ids.dr[num] = linear_addr; + switch (num) { + case 0: set_dr0(linear_addr); break; + case 1: set_dr1(linear_addr); break; + case 2: set_dr2(linear_addr); break; + case 3: set_dr3(linear_addr); break; + } + + /* Replace type/len/persistence for DRnum in dr7 */ + dr7 = get_dr7 (); + dr7 &= ~(0xfUL << (4*num+16)) & ~(0x3UL << (2*num)); + dr7 |= (((len << 2) | type) << (4*num+16)) | (persistence << (2*num)); + set_dr7 (dr7); + + if (kernel_dr) { + if (!ids.dr[0] && !ids.dr[1] && !ids.dr[2] && !ids.dr[3]) { + /* Not used any more, switch back to user debugging registers */ + set_dr7 (0); + kernel_dr = FALSE; + zero_dr = TRUE; + db_load_context(current_thread()->pcb); + } + } + splx(s); +} + +boolean_t +db_set_hw_watchpoint( + const db_watchpoint_t watch, + unsigned num) +{ + vm_size_t size = watch->hiaddr - watch->loaddr; + db_addr_t addr = watch->loaddr; + vm_offset_t kern_addr; + + if (num >= 4) + return FALSE; + if (size != 1 && size != 2 && size != 4) + return FALSE; + + if (addr & (size-1)) + /* Unaligned */ + return FALSE; + + if (watch->task) { + if (db_user_to_kernel_address(watch->task, addr, &kern_addr, 1) < 0) + return FALSE; + addr = kern_addr; + } + addr = kvtolin(addr); + + db_dr (num, addr, I386_DB_TYPE_W, size-1, I386_DB_LOCAL|I386_DB_GLOBAL); + + db_printf("Hardware watchpoint %d set for %x\n", num, addr); + return TRUE; +} + +boolean_t +db_clear_hw_watchpoint( + unsigned num) +{ + if (num >= 4) + return FALSE; + + db_dr (num, 0, 0, 0, 0); + return TRUE; +} + +/* + * Print trap reason. + */ +static void +kdbprinttrap( + int type, + int code) +{ + printf("kernel: %s (%d), code=%x\n", + trap_name(type), type, code); +} + +/* + * kdb_trap - field a TRACE or BPT trap + */ + +extern jmp_buf_t *db_recover; +spl_t saved_ipl[NCPUS]; /* just to know what was IPL before trap */ + +boolean_t +kdb_trap( + int type, + int code, + struct i386_saved_state *regs) +{ + spl_t s; + + s = splhigh(); + saved_ipl[cpu_number()] = s; + + switch (type) { + case T_DEBUG: /* single_step */ + { + int addr; + int status = get_dr6(); + + if (status & 0xf) { /* hmm hdw break */ + addr = status & 0x8 ? get_dr3() : + status & 0x4 ? get_dr2() : + status & 0x2 ? get_dr1() : + get_dr0(); + regs->efl |= EFL_RF; + db_single_step_cmd(addr, 0, 1, "p"); + } + } + case T_INT3: /* breakpoint */ + case T_WATCHPOINT: /* watchpoint */ + case -1: /* keyboard interrupt */ + break; + + default: + if (db_recover) { + i386_nested_saved_state = *regs; + db_printf("Caught %s (%d), code = %x, pc = %x\n", + trap_name(type), type, code, regs->eip); + db_error(""); + /*NOTREACHED*/ + } + kdbprinttrap(type, code); + } + +#if NCPUS > 1 + if (db_enter()) +#endif /* NCPUS > 1 */ + { + i386_last_saved_statep = regs; + i386_last_kdb_sp = (uintptr_t) &type; + + /* XXX Should switch to ddb`s own stack here. */ + + ddb_regs = *regs; + if ((regs->cs & 0x3) == KERNEL_RING) { + /* + * Kernel mode - esp and ss not saved + */ + ddb_regs.uesp = (uintptr_t)®s->uesp; /* kernel stack pointer */ + ddb_regs.ss = KERNEL_DS; + } + + cnpollc(TRUE); + db_task_trap(type, code, (regs->cs & 0x3) != 0); + cnpollc(FALSE); + + regs->eip = ddb_regs.eip; + regs->efl = ddb_regs.efl; + regs->eax = ddb_regs.eax; + regs->ecx = ddb_regs.ecx; + regs->edx = ddb_regs.edx; + regs->ebx = ddb_regs.ebx; + if ((regs->cs & 0x3) != KERNEL_RING) { + /* + * user mode - saved esp and ss valid + */ + regs->uesp = ddb_regs.uesp; /* user stack pointer */ + regs->ss = ddb_regs.ss & 0xffff; /* user stack segment */ + } + regs->ebp = ddb_regs.ebp; + regs->esi = ddb_regs.esi; + regs->edi = ddb_regs.edi; + regs->cs = ddb_regs.cs & 0xffff; +#if !defined(__x86_64__) || defined(USER32) + regs->es = ddb_regs.es & 0xffff; + regs->ds = ddb_regs.ds & 0xffff; + regs->fs = ddb_regs.fs & 0xffff; + regs->gs = ddb_regs.gs & 0xffff; +#endif + if ((type == T_INT3) && + (db_get_task_value(regs->eip, BKPT_SIZE, FALSE, TASK_NULL) + == BKPT_INST)) + regs->eip += BKPT_SIZE; + } +#if NCPUS > 1 + db_leave(); +#endif /* NCPUS > 1 */ + + splx(s); + return 1; +} + +/* + * Enter KDB through a keyboard trap. + * We show the registers as of the keyboard interrupt + * instead of those at its call to KDB. + */ +struct int_regs { +#ifdef __i386__ + long edi; + long esi; +#endif + long ebp; + long ebx; + struct i386_interrupt_state *is; +}; + +void +kdb_kentry( + struct int_regs *int_regs) +{ + struct i386_interrupt_state *is = int_regs->is; + spl_t s = splhigh(); + +#if NCPUS > 1 + if (db_enter()) +#endif /* NCPUS > 1 */ + { + if ((is->cs & 0x3) != KERNEL_RING) { + ddb_regs.uesp = *(uintptr_t *)(is+1); + ddb_regs.ss = *(int *)((uintptr_t *)(is+1)+1); + } + else { + ddb_regs.ss = KERNEL_DS; + ddb_regs.uesp= (uintptr_t)(is+1); + } + ddb_regs.efl = is->efl; + ddb_regs.cs = is->cs; + ddb_regs.eip = is->eip; + ddb_regs.eax = is->eax; + ddb_regs.ecx = is->ecx; + ddb_regs.edx = is->edx; + ddb_regs.ebx = int_regs->ebx; + ddb_regs.ebp = int_regs->ebp; +#ifdef __i386__ + ddb_regs.esi = int_regs->esi; + ddb_regs.edi = int_regs->edi; +#endif +#ifdef __x86_64__ + ddb_regs.esi = is->rsi; + ddb_regs.edi = is->rdi; +#endif +#if !defined(__x86_64__) || defined(USER32) + ddb_regs.ds = is->ds; + ddb_regs.es = is->es; + ddb_regs.fs = is->fs; + ddb_regs.gs = is->gs; +#endif + cnpollc(TRUE); + db_task_trap(-1, 0, (ddb_regs.cs & 0x3) != 0); + cnpollc(FALSE); + + if ((ddb_regs.cs & 0x3) != KERNEL_RING) { + ((int *)(is+1))[0] = ddb_regs.uesp; + ((int *)(is+1))[1] = ddb_regs.ss & 0xffff; + } + is->efl = ddb_regs.efl; + is->cs = ddb_regs.cs & 0xffff; + is->eip = ddb_regs.eip; + is->eax = ddb_regs.eax; + is->ecx = ddb_regs.ecx; + is->edx = ddb_regs.edx; + int_regs->ebx = ddb_regs.ebx; + int_regs->ebp = ddb_regs.ebp; +#ifdef __i386__ + int_regs->esi = ddb_regs.esi; + int_regs->edi = ddb_regs.edi; +#endif +#ifdef __x86_64__ + is->rsi = ddb_regs.esi; + is->rdi = ddb_regs.edi; +#endif +#if !defined(__x86_64__) || defined(USER32) + is->ds = ddb_regs.ds & 0xffff; + is->es = ddb_regs.es & 0xffff; + is->fs = ddb_regs.fs & 0xffff; + is->gs = ddb_regs.gs & 0xffff; +#endif + } +#if NCPUS > 1 + db_leave(); +#endif /* NCPUS > 1 */ + + (void) splx(s); +} + +boolean_t db_no_vm_fault = TRUE; + +static int +db_user_to_phys_address( + const task_t task, + vm_offset_t addr, + phys_addr_t *paddr, + int flag) +{ + pt_entry_t *ptp; + boolean_t faulted = FALSE; + + retry: + ptp = pmap_pte(task->map->pmap, addr); + if (ptp == PT_ENTRY_NULL || (*ptp & INTEL_PTE_VALID) == 0) { + if (!faulted && !db_no_vm_fault) { + kern_return_t err; + + faulted = TRUE; + err = vm_fault( task->map, + trunc_page(addr), + VM_PROT_READ, + FALSE, FALSE, 0); + if (err == KERN_SUCCESS) + goto retry; + } + if (flag) { + db_printf("\nno memory is assigned to address %08x\n", addr); + } + return(-1); + } + + *paddr = pte_to_pa(*ptp) + (addr & (INTEL_PGBYTES-1)); + return(0); +} + +int +db_user_to_kernel_address( + const task_t task, + vm_offset_t addr, + vm_offset_t *kaddr, + int flag) +{ + phys_addr_t paddr; + + if (db_user_to_phys_address(task, addr, &paddr, flag) < 0) + return(-1); + + if (paddr >= biosmem_directmap_end()) { + db_printf("\naddr %016llx is stored in highmem at physical %016llx, accessing it is not supported yet\n", (unsigned long long) addr, (unsigned long long) paddr); + return(-1); + } + + *kaddr = phystokv(paddr); + return(0); +} + +/* + * Read bytes from kernel address space for debugger. + */ + +boolean_t +db_read_bytes( + vm_offset_t addr, + int size, + char *data, + task_t task) +{ + char *src; + int n; + phys_addr_t phys_addr; + + src = (char *)addr; + if ((addr >= VM_MIN_KERNEL_ADDRESS && addr < VM_MAX_KERNEL_ADDRESS) || task == TASK_NULL) { + if (task == TASK_NULL) + task = db_current_task(); + while (--size >= 0) { + if (addr < VM_MIN_KERNEL_ADDRESS && task == TASK_NULL) { + db_printf("\nbad address %x\n", addr); + return FALSE; + } + addr++; + *data++ = *src++; + } + return TRUE; + } + while (size > 0) { + if (db_user_to_phys_address(task, addr, &phys_addr, 1) < 0) + return FALSE; + n = intel_trunc_page(addr+INTEL_PGBYTES) - addr; + if (n > size) + n = size; + size -= n; + addr += n; + copy_from_phys(phys_addr, (vm_offset_t) data, n); + data += n; + } + return TRUE; +} + +/* + * Write bytes to kernel address space for debugger. + */ +void +db_write_bytes( + vm_offset_t addr, + int size, + char *data, + task_t task) +{ + char *dst; + + pt_entry_t *ptep0 = 0; + pt_entry_t oldmap0 = 0; + vm_offset_t addr1; + pt_entry_t *ptep1 = 0; + pt_entry_t oldmap1 = 0; + extern char etext; + + if ((addr < VM_MIN_KERNEL_ADDRESS) ^ + ((addr + size) <= VM_MIN_KERNEL_ADDRESS)) { + db_error("\ncannot write data into mixed space\n"); + /* NOTREACHED */ + } + if (addr < VM_MIN_KERNEL_ADDRESS) { + if (task) { + db_write_bytes_user_space(addr, size, data, task); + return; + } else if (db_current_task() == TASK_NULL) { + db_printf("\nbad address %x\n", addr); + db_error(0); + /* NOTREACHED */ + } + } + + if (addr >= VM_MIN_KERNEL_ADDRESS && + addr <= (vm_offset_t)&etext) + { + ptep0 = pmap_pte(kernel_pmap, addr); + oldmap0 = *ptep0; + *ptep0 |= INTEL_PTE_WRITE; + + 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; + *ptep1 |= INTEL_PTE_WRITE; + } + if (CPU_HAS_FEATURE(CPU_FEATURE_PGE)) + set_cr4(get_cr4() & ~CR4_PGE); + flush_tlb(); + } + + dst = (char *)addr; + + while (--size >= 0) + *dst++ = *data++; + + if (ptep0) { + *ptep0 = oldmap0; + if (ptep1) { + *ptep1 = oldmap1; + } + flush_tlb(); + if (CPU_HAS_FEATURE(CPU_FEATURE_PGE)) + set_cr4(get_cr4() | CR4_PGE); + } +} + +void +db_write_bytes_user_space( + vm_offset_t addr, + int size, + char *data, + task_t task) +{ + int n; + phys_addr_t phys_addr; + + while (size > 0) { + if (db_user_to_phys_address(task, addr, &phys_addr, 1) < 0) + return; + n = intel_trunc_page(addr+INTEL_PGBYTES) - addr; + if (n > size) + n = size; + size -= n; + addr += n; + copy_to_phys((vm_offset_t) data, phys_addr, n); + } +} + +boolean_t +db_check_access( + vm_offset_t addr, + int size, + task_t task) +{ + int n; + phys_addr_t phys_addr; + + if (addr >= VM_MIN_KERNEL_ADDRESS) { + if (kernel_task == TASK_NULL) + return TRUE; + task = kernel_task; + } else if (task == TASK_NULL) { + if (current_thread() == THREAD_NULL) + return FALSE; + task = current_thread()->task; + } + while (size > 0) { + if (db_user_to_phys_address(task, addr, &phys_addr, 0) < 0) + return FALSE; + n = intel_trunc_page(addr+INTEL_PGBYTES) - addr; + if (n > size) + n = size; + size -= n; + addr += n; + } + return TRUE; +} + +boolean_t +db_phys_eq( + task_t task1, + vm_offset_t addr1, + const task_t task2, + vm_offset_t addr2) +{ + phys_addr_t phys_addr1, phys_addr2; + + if (addr1 >= VM_MIN_KERNEL_ADDRESS || addr2 >= VM_MIN_KERNEL_ADDRESS) + return FALSE; + if ((addr1 & (INTEL_PGBYTES-1)) != (addr2 & (INTEL_PGBYTES-1))) + return FALSE; + if (task1 == TASK_NULL) { + if (current_thread() == THREAD_NULL) + return FALSE; + task1 = current_thread()->task; + } + if (db_user_to_phys_address(task1, addr1, &phys_addr1, 0) < 0 + || db_user_to_phys_address(task2, addr2, &phys_addr2, 0) < 0) + return FALSE; + return(phys_addr1 == phys_addr2); +} + +#define DB_USER_STACK_ADDR (VM_MIN_KERNEL_ADDRESS) +#define DB_NAME_SEARCH_LIMIT (DB_USER_STACK_ADDR-(INTEL_PGBYTES*3)) + +#define GNU + +#ifndef GNU +static boolean_t +db_search_null( + const task_t task, + vm_offset_t *svaddr, + vm_offset_t evaddr, + vm_offset_t *skaddr, + int flag) +{ + unsigned vaddr; + unsigned *kaddr; + + kaddr = (unsigned *)*skaddr; + for (vaddr = *svaddr; vaddr > evaddr; ) { + if (vaddr % INTEL_PGBYTES == 0) { + vaddr -= sizeof(unsigned); + if (db_user_to_kernel_address(task, vaddr, skaddr, 0) < 0) + return FALSE; + kaddr = (vm_offset_t *)*skaddr; + } else { + vaddr -= sizeof(unsigned); + kaddr--; + } + if ((*kaddr == 0) ^ (flag == 0)) { + *svaddr = vaddr; + *skaddr = (unsigned)kaddr; + return TRUE; + } + } + return FALSE; +} +#endif /* GNU */ + +#ifdef GNU +static boolean_t +looks_like_command( + const task_t task, + char* kaddr) +{ + char *c; + + assert(!((vm_offset_t) kaddr & (INTEL_PGBYTES-1))); + + /* + * Must be the environment. + */ + if (!memcmp(kaddr, "PATH=", 5) || !memcmp(kaddr, "TERM=", 5) || !memcmp(kaddr, "SHELL=", 6) || !memcmp(kaddr, "LOCAL_PART=", 11) || !memcmp(kaddr, "LC_ALL=", 7)) + return FALSE; + + /* + * This is purely heuristical but works quite nicely. + * We know that it should look like words separated by \0, and + * eventually only \0s. + */ + c = kaddr; + while (c < kaddr + INTEL_PGBYTES) { + if (!*c) { + if (c == kaddr) + /* Starts by \0. */ + return FALSE; + break; + } + while (c < kaddr + INTEL_PGBYTES && *c) + c++; + if (c < kaddr + INTEL_PGBYTES) + c++; /* Skip \0 */ + } + /* + * Check that the remainder is just \0s. + */ + while (c < kaddr + INTEL_PGBYTES) + if (*c++) + return FALSE; + + return TRUE; +} +#endif /* GNU */ + +void +db_task_name( + const task_t task) +{ + char *p; + int n; + vm_offset_t vaddr, kaddr; + unsigned sp; + + if (task->name[0]) { + db_printf("%s", task->name); + return; + } + +#ifdef GNU + /* + * GNU Hurd-specific heuristics. + */ + + /* Heuristical address first. */ + vaddr = 0x1026000; + if (db_user_to_kernel_address(task, vaddr, &kaddr, 0) >= 0 && + looks_like_command(task, (char*) kaddr)) + goto ok; + + /* Try to catch SP of the main thread. */ + thread_t thread; + + task_lock(task); + thread = (thread_t) queue_first(&task->thread_list); + if (!thread) { + task_unlock(task); + db_printf(DB_NULL_TASK_NAME); + return; + } + sp = thread->pcb->iss.uesp; + task_unlock(task); + + vaddr = (sp & ~(INTEL_PGBYTES - 1)) + INTEL_PGBYTES; + while (1) { + if (db_user_to_kernel_address(task, vaddr, &kaddr, 0) < 0) + return; + if (looks_like_command(task, (char*) kaddr)) + break; + vaddr += INTEL_PGBYTES; + } +#else /* GNU */ + vaddr = DB_USER_STACK_ADDR; + kaddr = 0; + + /* + * skip nulls at the end + */ + if (!db_search_null(task, &vaddr, DB_NAME_SEARCH_LIMIT, &kaddr, 0)) { + db_printf(DB_NULL_TASK_NAME); + return; + } + /* + * search start of args + */ + if (!db_search_null(task, &vaddr, DB_NAME_SEARCH_LIMIT, &kaddr, 1)) { + db_printf(DB_NULL_TASK_NAME); + return; + } +#endif /* GNU */ + +ok: + n = DB_TASK_NAME_LEN-1; +#ifdef GNU + p = (char *)kaddr; + for (; n > 0; vaddr++, p++, n--) { +#else /* GNU */ + p = (char *)kaddr + sizeof(unsigned); + for (vaddr += sizeof(int); vaddr < DB_USER_STACK_ADDR && n > 0; + vaddr++, p++, n--) { +#endif /* GNU */ + if (vaddr % INTEL_PGBYTES == 0) { + (void)db_user_to_kernel_address(task, vaddr, &kaddr, 0); + p = (char*)kaddr; + } + db_printf("%c", (*p < ' ' || *p > '~')? ' ': *p); + } + while (n-- >= 0) /* compare with >= 0 for one more space */ + db_printf(" "); +} + +#endif /* MACH_KDB */ diff --git a/i386/i386/db_interface.h b/i386/i386/db_interface.h new file mode 100644 index 0000000..69a277a --- /dev/null +++ b/i386/i386/db_interface.h @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2007 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Barry deFreese. + */ + +#ifndef _I386_DB_INTERFACE_H_ +#define _I386_DB_INTERFACE_H_ + +#include +#include +#include +#include +#include + +extern boolean_t kdb_trap ( + int type, + int code, + struct i386_saved_state *regs); + +struct int_regs; + +extern void kdb_kentry(struct int_regs *int_regs); + +extern boolean_t db_read_bytes ( + vm_offset_t addr, + int size, + char *data, + task_t task); + +extern void db_write_bytes ( + vm_offset_t addr, + int size, + char *data, + task_t task); + +extern boolean_t db_check_access ( + vm_offset_t addr, + int size, + task_t task); + +extern boolean_t db_phys_eq ( + task_t task1, + vm_offset_t addr1, + task_t task2, + vm_offset_t addr2); + +extern int db_user_to_kernel_address( + task_t task, + vm_offset_t addr, + vm_offset_t *kaddr, + int flag); + +extern void db_task_name (task_t task); + +extern void cpu_interrupt_to_db(int i); + +#define I386_DB_TYPE_X 0 +#define I386_DB_TYPE_W 1 +#define I386_DB_TYPE_RW 3 + +#define I386_DB_LEN_1 0 +#define I386_DB_LEN_2 1 +#define I386_DB_LEN_4 3 +#define I386_DB_LEN_8 2 /* For >= Pentium4 and Xen CPUID >= 15 only */ + +#define I386_DB_LOCAL 1 +#define I386_DB_GLOBAL 2 + +#if MACH_KDB +extern boolean_t db_set_hw_watchpoint( + db_watchpoint_t watch, + unsigned num); + +extern boolean_t db_clear_hw_watchpoint( + unsigned num); + +extern void db_dr ( + int num, + vm_offset_t linear_addr, + int type, + int len, + int persistence); + +extern void +db_stack_trace_cmd( + db_expr_t addr, + boolean_t have_addr, + db_expr_t count, + const char *modif); + +extern void +db_halt_cpu(void); +extern void +db_reset_cpu(void); + +void +db_i386_reg_value( + struct db_variable *vp, + db_expr_t *valuep, + int flag, + struct db_var_aux_param *ap); + +void feep(void); + +/* + * Put a debugging character on the screen. + * LOC=0 means put it in the bottom right corner, LOC=1 means put it + * one column to the left, etc. + */ +void kd_debug_put(int loc, char c); + +#endif + +extern void db_get_debug_state( + pcb_t pcb, + struct i386_debug_state *state); +extern kern_return_t db_set_debug_state( + pcb_t pcb, + const struct i386_debug_state *state); + +extern void db_load_context(pcb_t pcb); + +extern void cnpollc(boolean_t on); + +void +db_write_bytes_user_space( + vm_offset_t addr, + int size, + char *data, + task_t task); + +void db_debug_all_traps (boolean_t enable); + +#endif /* _I386_DB_INTERFACE_H_ */ diff --git a/i386/i386/db_machdep.h b/i386/i386/db_machdep.h new file mode 100644 index 0000000..04c874b --- /dev/null +++ b/i386/i386/db_machdep.h @@ -0,0 +1,105 @@ +/* + * 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. + */ + +#ifndef _I386_DB_MACHDEP_H_ +#define _I386_DB_MACHDEP_H_ + +/* + * Machine-dependent defines for new kernel debugger. + */ + +#include +#include +#include +#include /* for thread_status */ +#include + +typedef vm_offset_t db_addr_t; /* address - unsigned */ +typedef long 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 SAVE_DDB_REGS DB_SAVE(db_regs_t, ddb_regs) +#define RESTORE_DDB_REGS DB_RESTORE(ddb_regs) + +#define PC_REGS(regs) ((db_addr_t)(regs)->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.eip -= 1; + +#define db_clear_single_step(regs) ((regs)->efl &= ~EFL_TF) +#define db_set_single_step(regs) ((regs)->efl |= EFL_TF) + +#define IS_BREAKPOINT_TRAP(type, code) ((type) == T_INT3) +#define IS_WATCHPOINT_TRAP(type, code) ((type) == T_WATCHPOINT) + +#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)) + +/* 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 "? " + +/* macro for checking if a thread has used floating-point */ + +#define db_thread_fp_used(thread) ((thread)->pcb->ims.ifps != 0) + +/* only a.out symbol tables */ + +#define DB_NO_COFF 1 + +#endif /* _I386_DB_MACHDEP_H_ */ diff --git a/i386/i386/db_trace.c b/i386/i386/db_trace.c new file mode 100644 index 0000000..0ef7251 --- /dev/null +++ b/i386/i386/db_trace.c @@ -0,0 +1,586 @@ +/* + * Mach Operating System + * Copyright (c) 1993,1992,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. + */ + +#if MACH_KDB + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "trap.h" + +/* + * Machine register set. + */ +struct db_variable db_regs[] = { + { "cs", (long *)&ddb_regs.cs, db_i386_reg_value }, +#if !defined(__x86_64__) || defined(USER32) + { "ds", (long *)&ddb_regs.ds, db_i386_reg_value }, + { "es", (long *)&ddb_regs.es, db_i386_reg_value }, + { "fs", (long *)&ddb_regs.fs, db_i386_reg_value }, + { "gs", (long *)&ddb_regs.gs, db_i386_reg_value }, +#endif + { "ss", (long *)&ddb_regs.ss, db_i386_reg_value }, + { "eax",(long *)&ddb_regs.eax, db_i386_reg_value }, + { "ecx",(long *)&ddb_regs.ecx, db_i386_reg_value }, + { "edx",(long *)&ddb_regs.edx, db_i386_reg_value }, + { "ebx",(long *)&ddb_regs.ebx, db_i386_reg_value }, + { "esp",(long *)&ddb_regs.uesp,db_i386_reg_value }, + { "ebp",(long *)&ddb_regs.ebp, db_i386_reg_value }, + { "esi",(long *)&ddb_regs.esi, db_i386_reg_value }, + { "edi",(long *)&ddb_regs.edi, db_i386_reg_value }, + { "eip",(long *)&ddb_regs.eip, db_i386_reg_value }, + { "efl",(long *)&ddb_regs.efl, db_i386_reg_value }, +#ifdef __x86_64__ + { "r8", (long *)&ddb_regs.r8, db_i386_reg_value }, + { "r9", (long *)&ddb_regs.r9, db_i386_reg_value }, + { "r10",(long *)&ddb_regs.r10, db_i386_reg_value }, + { "r11",(long *)&ddb_regs.r11, db_i386_reg_value }, + { "r12",(long *)&ddb_regs.r12, db_i386_reg_value }, + { "r13",(long *)&ddb_regs.r13, db_i386_reg_value }, + { "r14",(long *)&ddb_regs.r14, db_i386_reg_value }, + { "r15",(long *)&ddb_regs.r15, db_i386_reg_value }, +#endif +}; +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; + long f_retaddr; + long f_arg0; +}; + +#define TRAP 1 +#define INTERRUPT 2 +#define SYSCALL 3 + +db_addr_t db_user_trap_symbol_value = 0; +db_addr_t db_kernel_trap_symbol_value = 0; +db_addr_t db_interrupt_symbol_value = 0; +db_addr_t db_return_to_iret_symbol_value = 0; +db_addr_t db_syscall_symbol_value = 0; +boolean_t db_trace_symbols_found = FALSE; + +struct i386_kregs { + char *name; + long offset; +} i386_kregs[] = { + { "ebx", (long)(&((struct i386_kernel_state *)0)->k_ebx) }, + { "esp", (long)(&((struct i386_kernel_state *)0)->k_esp) }, + { "ebp", (long)(&((struct i386_kernel_state *)0)->k_ebp) }, +#ifdef __i386__ + { "edi", (long)(&((struct i386_kernel_state *)0)->k_edi) }, + { "esi", (long)(&((struct i386_kernel_state *)0)->k_esi) }, +#endif +#ifdef __x86_64__ + { "r12", (long)(&((struct i386_kernel_state *)0)->k_r12) }, + { "r13", (long)(&((struct i386_kernel_state *)0)->k_r13) }, + { "r14", (long)(&((struct i386_kernel_state *)0)->k_r14) }, + { "r15", (long)(&((struct i386_kernel_state *)0)->k_r15) }, +#endif + { "eip", (long)(&((struct i386_kernel_state *)0)->k_eip) }, + { 0 }, +}; + +static long * +db_lookup_i386_kreg( + const char *name, + const long *kregp) +{ + struct i386_kregs *kp; + + for (kp = i386_kregs; kp->name; kp++) { + if (strcmp(name, kp->name) == 0) + return (long *)((long)kregp + kp->offset); + } + return 0; +} + +void +db_i386_reg_value( + struct db_variable *vp, + db_expr_t *valuep, + int flag, + db_var_aux_param_t ap) +{ + long *dp = 0; + db_expr_t null_reg = 0; + thread_t thread = ap->thread; + + if (db_option(ap->modif, 'u')) { + if (thread == THREAD_NULL) { + if ((thread = current_thread()) == THREAD_NULL) + db_error("no user registers\n"); + } + if (thread == current_thread()) { + if (ddb_regs.cs & 0x3) + dp = vp->valuep; + else if (ON_INT_STACK(ddb_regs.ebp, cpu_number())) + db_error("cannot get/set user registers in nested interrupt\n"); + } + } else { + if (thread == THREAD_NULL || thread == current_thread()) { + dp = vp->valuep; + } else if ((thread->state & TH_SWAPPED) == 0 && + thread->kernel_stack) { + dp = db_lookup_i386_kreg(vp->name, + (long *)(STACK_IKS(thread->kernel_stack))); + if (dp == 0) + dp = &null_reg; + } else if ((thread->state & TH_SWAPPED) && + thread->swap_func != thread_exception_return) { +/*.....this breaks t/t $taskN.0...*/ + /* only EIP is valid */ + if (vp->valuep == (long *) &ddb_regs.eip) { + dp = (long *)(&thread->swap_func); + } else { + dp = &null_reg; + } + } + } + if (dp == 0) { + if (thread->pcb == 0) + db_error("no pcb\n"); + dp = (long *)((long)(&thread->pcb->iss) + + ((long)vp->valuep - (long)&ddb_regs)); + } + if (flag == DB_VAR_SET) + *dp = *valuep; + else + *valuep = *dp; +} + +static void +db_find_trace_symbols(void) +{ + db_expr_t value; +#ifdef __ELF__ +#define P +#else +#define P "_" +#endif + if (db_value_of_name(P"user_trap", &value)) + db_user_trap_symbol_value = (db_addr_t) value; + if (db_value_of_name(P"kernel_trap", &value)) + db_kernel_trap_symbol_value = (db_addr_t) value; + if (db_value_of_name(P"interrupt", &value)) + db_interrupt_symbol_value = (db_addr_t) value; + if (db_value_of_name(P"return_to_iret", &value)) + db_return_to_iret_symbol_value = (db_addr_t) value; + if (db_value_of_name(P"syscall", &value)) + db_syscall_symbol_value = (db_addr_t) value; +#undef P + db_trace_symbols_found = TRUE; +} + +/* + * Figure out how many arguments were passed into the frame at "fp". + */ +const int db_numargs_default = 5; + +#ifdef __x86_64 +/* Args are in registers */ +#define db_numargs(fp, task) -1 +#else +static int +db_numargs( + struct i386_frame *fp, + task_t task) +{ + long *argp; + long inst; + long args; + extern char etext[]; + + argp = (long *)db_get_task_value((long)&fp->f_retaddr, sizeof(long), FALSE, task); + if (argp < (long *)VM_MIN_KERNEL_ADDRESS || argp > (long *)etext) + args = db_numargs_default; + else if (!DB_CHECK_ACCESS((long)argp, sizeof(long), task)) + args = db_numargs_default; + else { + inst = db_get_task_value((long)argp, sizeof(long), FALSE, task); + if ((inst & 0xff) == 0x59) /* popl %ecx */ + args = 1; + else if ((inst & 0xffff) == 0xc483) /* addl %n, %esp */ + args = ((inst >> 16) & 0xff) / 4; + else + args = db_numargs_default; + } + return args; +} +#endif + +struct interrupt_frame { + struct i386_frame *if_frame; /* point to next frame */ + long if_retaddr; /* return address to _interrupt */ + long if_unit; /* unit number */ + spl_t if_spl; /* saved spl */ + long if_iretaddr; /* _return_to_{iret,iret_i} */ + long if_edx; /* old sp(iret) or saved edx(iret_i) */ + long if_ecx; /* saved ecx(iret_i) */ + long if_eax; /* saved eax(iret_i) */ + long if_eip; /* saved eip(iret_i) */ + long if_cs; /* saved cs(iret_i) */ + long if_efl; /* saved efl(iret_i) */ +}; + +/* + * 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. + */ +static void +db_nextframe( + struct i386_frame **lfp, /* in/out */ + struct i386_frame **fp, /* in/out */ + db_addr_t *sp, /* out */ + db_addr_t *ip, /* out */ + long frame_type, /* in */ + const thread_t thread) /* in */ +{ + struct i386_saved_state *saved_regs; + struct interrupt_frame *ifp; + task_t task = (thread != THREAD_NULL)? thread->task: TASK_NULL; + + switch(frame_type) { + case TRAP: + /* + * We know that trap() has 1 argument and we know that + * it is an (struct i386_saved_state *). + */ + saved_regs = (struct i386_saved_state *) + db_get_task_value((long)&((*fp)->f_arg0),sizeof(long),FALSE,task); + db_printf(">>>>> %s (%d)", + trap_name(saved_regs->trapno), saved_regs->trapno); + if (saved_regs->trapno == T_PAGE_FAULT) + db_printf(" for %s%s%s %lx", + saved_regs->err & T_PF_PROT ? "P" : "", + saved_regs->err & T_PF_WRITE ? "W" : "", + saved_regs->err & T_PF_USER ? "U" : "", + lintokv(saved_regs->cr2)); + db_printf(" at "); + db_task_printsym(saved_regs->eip, DB_STGY_PROC, task); + db_printf(" <<<<<\n"); + *fp = (struct i386_frame *)saved_regs->ebp; + *sp = (db_addr_t)saved_regs->uesp; + *ip = (db_addr_t)saved_regs->eip; + break; + case INTERRUPT: + if (*lfp == 0) { + db_printf(">>>>> interrupt <<<<<\n"); + goto miss_frame; + } + db_printf(">>>>> interrupt at "); + ifp = (struct interrupt_frame *)(*lfp); + *fp = ifp->if_frame; + *sp = (db_addr_t) ifp->if_frame; + if (ifp->if_iretaddr == db_return_to_iret_symbol_value) + *ip = ((struct i386_interrupt_state *) ifp->if_edx)->eip; + else + *ip = (db_addr_t) ifp->if_eip; + db_task_printsym(*ip, DB_STGY_PROC, task); + db_printf(" <<<<<\n"); + break; + case SYSCALL: + if (thread != THREAD_NULL && thread->pcb) { + *ip = (db_addr_t) thread->pcb->iss.eip; + *sp = (db_addr_t) thread->pcb->iss.uesp; + *fp = (struct i386_frame *) thread->pcb->iss.ebp; + break; + } + /* falling down for unknown case */ + default: + miss_frame: + *ip = (db_addr_t) + db_get_task_value((long)&(*fp)->f_retaddr, sizeof(long), FALSE, task); + *lfp = *fp; + *fp = (struct i386_frame *) + db_get_task_value((long)&(*fp)->f_frame, sizeof(long), FALSE, task); + *sp = (db_addr_t) *fp; + break; + } +} + +#define F_USER_TRACE 1 +#define F_TRACE_THREAD 2 + +void +db_stack_trace_cmd( + db_expr_t addr, + boolean_t have_addr, + db_expr_t count, + const char *modif) +{ + boolean_t trace_thread = FALSE; + struct i386_frame *frame; + db_addr_t callpc, sp; + int flags = 0; + thread_t th; + + { + const char *cp = modif; + char c; + + while ((c = *cp++) != 0) { + if (c == 't') + trace_thread = TRUE; + if (c == 'u') + flags |= F_USER_TRACE; + } + } + + if (!have_addr && !trace_thread) { + frame = (struct i386_frame *)ddb_regs.ebp; + sp = (db_addr_t)ddb_regs.uesp; + callpc = (db_addr_t)ddb_regs.eip; + th = current_thread(); + } else if (trace_thread) { + if (have_addr) { + th = (thread_t) addr; + if (!db_check_thread_address_valid(th)) + return; + } else { + th = db_default_thread; + if (th == THREAD_NULL) + th = current_thread(); + if (th == THREAD_NULL) { + db_printf("no active thread\n"); + return; + } + } + if (th == current_thread()) { + frame = (struct i386_frame *)ddb_regs.ebp; + sp = (db_addr_t)ddb_regs.uesp; + callpc = (db_addr_t)ddb_regs.eip; + } else { + if (th->pcb == 0) { + db_printf("thread has no pcb\n"); + return; + } + if ((th->state & TH_SWAPPED) || th->kernel_stack == 0) { + struct i386_saved_state *iss = &th->pcb->iss; + + db_printf("Continuation "); + db_task_printsym((db_addr_t)th->swap_func, + DB_STGY_PROC, + th->task); + db_printf("\n"); + + frame = (struct i386_frame *) (iss->ebp); + sp = (db_addr_t) (iss->uesp); + callpc = (db_addr_t) (iss->eip); + } else { + struct i386_kernel_state *iks; + iks = STACK_IKS(th->kernel_stack); + frame = (struct i386_frame *) (iks->k_ebp); + sp = (db_addr_t) (iks->k_esp); + callpc = (db_addr_t) (iks->k_eip); + } + } + } else { + frame = (struct i386_frame *)addr; + sp = (db_addr_t)addr; + th = (db_default_thread)? db_default_thread: current_thread(); + callpc = (db_addr_t)db_get_task_value((long)&frame->f_retaddr, sizeof(long), + FALSE, + (th == THREAD_NULL) ? TASK_NULL : th->task); + } + + db_i386_stack_trace( th, frame, sp, callpc, count, flags ); +} + + +void +db_i386_stack_trace( + const thread_t th, + struct i386_frame *frame, + db_addr_t sp, + db_addr_t callpc, + db_expr_t count, + int flags) +{ + task_t task; + boolean_t kernel_only; + long *argp; + long user_frame = 0; + struct i386_frame *lastframe; + int frame_type; + char *filename; + int linenum; + extern unsigned long db_maxoff; + + if (count == -1) + count = 65535; + + kernel_only = (flags & F_USER_TRACE) == 0; + + task = (th == THREAD_NULL) ? TASK_NULL : th->task; + + if (!db_trace_symbols_found) + db_find_trace_symbols(); + + if (!INKERNEL(callpc) && !INKERNEL(frame)) { + db_printf(">>>>> user space <<<<<\n"); + user_frame++; + } + + lastframe = 0; + while (count--) { + int narg; + char * name; + db_expr_t offset; + + if (INKERNEL(callpc) && user_frame == 0) { + db_addr_t call_func = 0; + + db_sym_t sym_tmp; + db_symbol_values(0, + sym_tmp = db_search_task_symbol(callpc, + DB_STGY_XTRN, + (db_addr_t *)&offset, + TASK_NULL), + &name, (db_expr_t *)&call_func); + db_free_symbol(sym_tmp); + if ((db_user_trap_symbol_value && call_func == db_user_trap_symbol_value) || + (db_kernel_trap_symbol_value && call_func == db_kernel_trap_symbol_value)) { + frame_type = TRAP; + narg = 1; + } else if (db_interrupt_symbol_value && call_func == db_interrupt_symbol_value) { + frame_type = INTERRUPT; + goto next_frame; + } else if (db_syscall_symbol_value && call_func == db_syscall_symbol_value) { + frame_type = SYSCALL; + goto next_frame; + } else { + frame_type = 0; + if (frame) + narg = db_numargs(frame, task); + else + narg = -1; + } + } else if (!frame || INKERNEL(callpc) ^ INKERNEL(frame)) { + frame_type = 0; + narg = -1; + } else { + frame_type = 0; + narg = db_numargs(frame, task); + } + + db_find_task_sym_and_offset(callpc, &name, + (db_addr_t *)&offset, task); + if (name == 0 || offset > db_maxoff) { + db_printf("0x%x(", callpc); + offset = 0; + } else + db_printf("%s(", name); + + if (!frame) { + db_printf(")\n"); + } + + if (sp) { + unsigned char inst = db_get_task_value(callpc, sizeof(char), FALSE, task); + if (inst == 0xc3) { + /* RET, unwind this directly */ + callpc = db_get_task_value(sp, sizeof(callpc), FALSE, task); + sp += sizeof(callpc); + continue; + } + } + + if (!frame) { + break; + } + + argp = &frame->f_arg0; + while (narg > 0) { + db_printf("%x", db_get_task_value((long)argp,sizeof(long),FALSE,task)); + argp++; + if (--narg != 0) + db_printf(","); + } + if (narg < 0) + db_printf("..."); + db_printf(")"); + if (offset) { + db_printf("+0x%x", offset); + } + if (db_line_at_pc(0, &filename, &linenum, callpc)) { + db_printf(" [%s", filename); + if (linenum > 0) + db_printf(":%d", linenum); + db_printf("]"); + } + db_printf("\n"); + + next_frame: + db_nextframe(&lastframe, &frame, &sp, &callpc, frame_type, th); + + if (!INKERNEL(lastframe) || + (!INKERNEL(callpc) && !INKERNEL(frame))) + user_frame++; + if (user_frame == 1) { + db_printf(">>>>> user space <<<<<\n"); + if (kernel_only) + break; + } + if (frame && frame <= lastframe) { + if (INKERNEL(lastframe) && !INKERNEL(frame)) + continue; + db_printf("Bad frame pointer: 0x%x\n", frame); + break; + } + } +} + +#endif /* MACH_KDB */ diff --git a/i386/i386/db_trace.h b/i386/i386/db_trace.h new file mode 100644 index 0000000..4684f57 --- /dev/null +++ b/i386/i386/db_trace.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2013 Free Software Foundation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _I386_DB_TRACE_H_ +#define _I386_DB_TRACE_H_ + +struct i386_frame; + +void +db_i386_stack_trace( + thread_t th, + struct i386_frame *frame, + db_addr_t sp, + db_addr_t callpc, + db_expr_t count, + int flags); + +#endif /* _I386_DB_TRACE_H_ */ diff --git a/i386/i386/debug.h b/i386/i386/debug.h new file mode 100644 index 0000000..84397ba --- /dev/null +++ b/i386/i386/debug.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 1994 The University of Utah and + * the Computer Systems Laboratory at the University of Utah (CSL). + * All rights reserved. + * + * Permission to use, copy, modify and distribute this software is hereby + * granted provided that (1) source code retains these copyright, permission, + * and disclaimer notices, and (2) redistributions including binaries + * reproduce the notices in supporting documentation, and (3) all advertising + * materials mentioning features or use of this software display the following + * acknowledgement: ``This product includes software developed by the + * Computer Systems Laboratory at the University of Utah.'' + * + * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS + * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF + * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * CSL requests users of this software to return to csl-dist@cs.utah.edu any + * improvements that they make and grant CSL redistribution rights. + * + * Author: Bryan Ford, University of Utah CSL + */ +#ifndef _I386_DEBUG_ +#define _I386_DEBUG_ + +#ifndef __ASSEMBLER__ +/* Dump a saved state. + Probably a good idea to have this around + even when DEBUG isn't turned on. */ +void dump_ss(const struct i386_saved_state *st); +#endif /* __ASSEMBLER__ */ + +#ifdef DEBUG + + +/* Maximum number of entries in a debug trace. + If the buffer overflows, the oldest entries are forgotten. */ +#define DEBUG_TRACE_LEN 512 + +/* Add the caller's current position to the debug trace buffer. + Only the kernel stack needs to be valid; + the other data segment registers are not needed + and all registers are saved. */ +#ifndef __ASSEMBLER__ + +#define DEBUG_TRACE _debug_trace(__FILE__,__LINE__) + +/* Reset the debug trace buffer so it contains no valid entries. */ +void debug_trace_reset(void); + +/* Dump the contents of the trace buffer to the console. + Also clears the trace buffer. */ +void debug_trace_dump(void); + +#else /* __ASSEMBLER__ */ + +#define DEBUG_TRACE \ + pushl $__LINE__ ;\ + pushl $9f ;\ + call __debug_trace ;\ + addl $8,%esp ;\ + .data ;\ +9: .ascii __FILE__"\0" ;\ + .text + +#endif /* __ASSEMBLER__ */ + + +#endif /* DEBUG */ + +/* XXX #include_next "debug.h" */ + +#endif /* _I386_DEBUG_ */ diff --git a/i386/i386/debug_i386.c b/i386/i386/debug_i386.c new file mode 100644 index 0000000..41d032e --- /dev/null +++ b/i386/i386/debug_i386.c @@ -0,0 +1,178 @@ +/* + * Copyright (c) 1994 The University of Utah and + * the Computer Systems Laboratory at the University of Utah (CSL). + * All rights reserved. + * + * Permission to use, copy, modify and distribute this software is hereby + * granted provided that (1) source code retains these copyright, permission, + * and disclaimer notices, and (2) redistributions including binaries + * reproduce the notices in supporting documentation, and (3) all advertising + * materials mentioning features or use of this software display the following + * acknowledgement: ``This product includes software developed by the + * Computer Systems Laboratory at the University of Utah.'' + * + * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS + * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF + * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * CSL requests users of this software to return to csl-dist@cs.utah.edu any + * improvements that they make and grant CSL redistribution rights. + * + * Author: Bryan Ford, University of Utah CSL + */ + +#include + +#include "thread.h" +#include "trap.h" +#include "debug.h" +#include "spl.h" + +void dump_ss(const struct i386_saved_state *st) +{ + printf("Dump of i386_saved_state %p:\n", st); +#if defined(__x86_64__) && ! defined(USER32) + printf("RAX %016lx RBX %016lx RCX %016lx RDX %016lx\n", + st->eax, st->ebx, st->ecx, st->edx); + printf("RSI %016lx RDI %016lx RBP %016lx RSP %016lx\n", + st->esi, st->edi, st->ebp, st->uesp); + printf("R8 %016lx R9 %016lx R10 %016lx R11 %016lx\n", + st->r8, st->r9, st->r10, st->r11); + printf("R12 %016lx R13 %016lx R14 %016lx R15 %016lx\n", + st->r12, st->r13, st->r14, st->r15); + printf("RIP %016lx EFLAGS %08lx\n", st->eip, st->efl); +#else + printf("EAX %08lx EBX %08lx ECX %08lx EDX %08lx\n", + st->eax, st->ebx, st->ecx, st->edx); + printf("ESI %08lx EDI %08lx EBP %08lx ESP %08lx\n", + st->esi, st->edi, st->ebp, st->uesp); + printf("CS %04lx SS %04lx " + "DS %04lx ES %04lx " + "FS %04lx GS %04lx\n", + st->cs & 0xffff, st->ss & 0xffff, + st->ds & 0xffff, st->es & 0xffff, + st->fs & 0xffff, st->gs & 0xffff); + printf("v86: DS %04lx ES %04lx FS %04lx GS %04lx\n", + st->v86_segs.v86_ds & 0xffff, st->v86_segs.v86_es & 0xffff, + st->v86_segs.v86_gs & 0xffff, st->v86_segs.v86_gs & 0xffff); + printf("EIP %08lx EFLAGS %08lx\n", st->eip, st->efl); +#endif + printf("trapno %ld: %s, error %08lx\n", + st->trapno, trap_name(st->trapno), + st->err); +} + +#ifdef DEBUG + +struct debug_trace_entry +{ + char *filename; + int linenum; +}; +struct debug_trace_entry debug_trace_buf[DEBUG_TRACE_LEN]; +int debug_trace_pos; + +void +debug_trace_reset(void) +{ + int s = splhigh(); + debug_trace_pos = 0; + debug_trace_buf[DEBUG_TRACE_LEN-1].filename = 0; + splx(s); +} + +static void +print_entry(int i, int *col) +{ + char *fn, *p; + + /* Strip off the path from the filename. */ + fn = debug_trace_buf[i].filename; + for (p = fn; *p; p++) + if (*p == '/') + fn = p+1; + + printf(" %9s:%-4d", fn, debug_trace_buf[i].linenum); + if (++*col == 5) + { + printf("\n"); + *col = 0; + } +} + +void +debug_trace_dump(void) +{ + int s = splhigh(); + int i; + int col = 0; + + printf("Debug trace dump "); + + /* If the last entry is nonzero, + the trace probably wrapped around. + Print out all the entries after the current position + before all the entries before it, + so we get a total of DEBUG_TRACE_LEN entries + in correct time order. */ + if (debug_trace_buf[DEBUG_TRACE_LEN-1].filename != 0) + { + printf("(full):\n"); + + for (i = debug_trace_pos; i < DEBUG_TRACE_LEN; i++) + { + print_entry(i, &col); + } + } + else + printf("(%d entries):\n", debug_trace_pos); + + /* Print the entries before the current position. */ + for (i = 0; i < debug_trace_pos; i++) + { + print_entry(i, &col); + } + + if (col != 0) + printf("\n"); + + debug_trace_reset(); + + splx(s); +} + +#include + +int syscall_trace = 0; +task_t syscall_trace_task; + +int +syscall_trace_print(int syscallvec, ...) +{ + int syscallnum = syscallvec >> 4; + int i; + const mach_trap_t *trap = &mach_trap_table[syscallnum]; + + if (syscall_trace_task && syscall_trace_task != current_task()) + goto out; + + printf("0x%08x:0x%08x:%s(", + current_task(), current_thread(), trap->mach_trap_name); + for (i = 0; i < trap->mach_trap_arg_count; i++) { + unsigned long value = (&syscallvec)[1+i]; + /* Use a crude heuristic to format pointers. */ + if (value > 1024) + printf("0x%08x", value); + else + printf("%d", value); + + if (i + 1 < trap->mach_trap_arg_count) + printf(", "); + } + printf(")\n"); + + out: + return syscallvec; +} + +#endif /* DEBUG */ diff --git a/i386/i386/debug_trace.S b/i386/i386/debug_trace.S new file mode 100644 index 0000000..f275e1b --- /dev/null +++ b/i386/i386/debug_trace.S @@ -0,0 +1,56 @@ +/* + * Copyright (c) 1994 The University of Utah and + * the Computer Systems Laboratory at the University of Utah (CSL). + * All rights reserved. + * + * Permission to use, copy, modify and distribute this software is hereby + * granted provided that (1) source code retains these copyright, permission, + * and disclaimer notices, and (2) redistributions including binaries + * reproduce the notices in supporting documentation, and (3) all advertising + * materials mentioning features or use of this software display the following + * acknowledgement: ``This product includes software developed by the + * Computer Systems Laboratory at the University of Utah.'' + * + * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS + * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF + * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * CSL requests users of this software to return to csl-dist@cs.utah.edu any + * improvements that they make and grant CSL redistribution rights. + * + * Author: Bryan Ford, University of Utah CSL + */ + +#ifdef DEBUG + +#include +#include + +#include "debug.h" + + .text +ENTRY(_debug_trace) + pushf + cli + pushl %eax + pushl %ebx + .byte 0x36 /* SS: bug in gas? */ + movl %ss:EXT(debug_trace_pos),%eax + movl 16(%esp),%ebx + movl %ebx,%ss:EXT(debug_trace_buf)(,%eax,8) + movl 20(%esp),%ebx + movl %ebx,%ss:EXT(debug_trace_buf)+4(,%eax,8) + incl %eax + andl $DEBUG_TRACE_LEN-1,%eax + .byte 0x36 /* SS: bug in gas? */ + movl %eax,%ss:EXT(debug_trace_pos) + popl %ebx + popl %eax + popf + ret + +#endif /* DEBUG */ + +/* XXX gas bug? need at least one symbol... */ +foo: + diff --git a/i386/i386/eflags.h b/i386/i386/eflags.h new file mode 100644 index 0000000..58ad968 --- /dev/null +++ b/i386/i386/eflags.h @@ -0,0 +1,35 @@ +/* + * 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. + */ +#ifndef _KERNEL_I386_EFLAGS_H_ +#define _KERNEL_I386_EFLAGS_H_ + +#include + +/* Eflags bit combinations used by the Mach kernel. */ +#define EFL_USER_SET (EFL_IF) +#define EFL_USER_CLEAR (EFL_IOPL|EFL_NT|EFL_RF) + +#endif /* _KERNEL_I386_EFLAGS_H_ */ diff --git a/i386/i386/fpu.c b/i386/i386/fpu.c new file mode 100644 index 0000000..4cd31dd --- /dev/null +++ b/i386/i386/fpu.c @@ -0,0 +1,948 @@ +/* + * Mach Operating System + * Copyright (c) 1992-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. + */ + +/* + * Copyright (C) 1994 Linus Torvalds + * + * Pentium III FXSR, SSE support + * General FPU state handling cleanups + * Gareth Hughes , May 2000 + */ + +/* + * Support for 80387 floating point or FP emulator. + */ + +#include + +#include +#include +#include + +#include +#include /* spls */ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include "cpu_number.h" + +#if 0 +#include +#define ASSERT_IPL(L) \ +{ \ + if (curr_ipl[cpu_number()] != L) { \ + printf("IPL is %d, expected %d\n", curr_ipl[cpu_number()], L); \ + panic("fpu: wrong ipl"); \ + } \ +} +#else +#define ASSERT_IPL(L) +#endif + +_Static_assert(sizeof(struct i386_xfp_xstate_header) == 8*8, + "struct i386_xfp_xstate_header size"); +_Static_assert(sizeof(struct i386_xfp_save) == 512 + 8*8, + "struct i386_xfp_save size"); + +int fp_kind = FP_387; /* 80387 present */ +enum fp_save_kind fp_save_kind = FP_FNSAVE; /* Which instruction we use to save/restore FPU state */ +uint64_t fp_xsave_support; /* Bitmap of supported XSAVE save areas */ +unsigned fp_xsave_size = sizeof(struct i386_fpsave_state); +struct i386_fpsave_state *fp_default_state; +struct kmem_cache ifps_cache; /* cache for FPU save area */ +static unsigned long mxcsr_feature_mask = 0xffffffff; /* Always AND user-provided mxcsr with this security mask */ + +#if NCPUS == 1 +volatile thread_t fp_thread = THREAD_NULL; + /* thread whose state is in FPU */ + /* always THREAD_NULL if emulating + FPU */ +volatile thread_t fp_intr_thread = THREAD_NULL; + + +#define clear_fpu() \ + { \ + set_ts(); \ + fp_thread = THREAD_NULL; \ + } + +#else /* NCPUS > 1 */ +#define clear_fpu() \ + { \ + set_ts(); \ + } + +#endif + + +/* + * Look for FPU and initialize it. + * Called on each CPU. + */ +void +init_fpu(void) +{ + unsigned short status, control; + +#ifdef MACH_RING1 + clear_ts(); +#else /* MACH_RING1 */ + unsigned int native = 0; + + if (machine_slot[cpu_number()].cpu_type >= CPU_TYPE_I486) + native = CR0_NE; + + /* + * Check for FPU by initializing it, + * then trying to read the correct bit patterns from + * the control and status registers. + */ + set_cr0((get_cr0() & ~(CR0_EM|CR0_TS)) | native); /* allow use of FPU */ +#endif /* MACH_RING1 */ + + fninit(); + status = fnstsw(); + fnstcw(&control); + + if ((status & 0xff) == 0 && + (control & 0x103f) == 0x3f) + { + /* + * We have a FPU of some sort. + * Compare -infinity against +infinity + * to check whether we have a 287 or a 387. + */ + volatile double fp_infinity, fp_one, fp_zero; + fp_one = 1.0; + fp_zero = 0.0; + fp_infinity = fp_one / fp_zero; + if (fp_infinity == -fp_infinity) { + /* + * We have an 80287. + */ + fp_kind = FP_287; + fp_save_kind = FP_FNSAVE; + asm volatile(".byte 0xdb; .byte 0xe4"); /* fnsetpm */ + } + else { + /* + * We have a 387. + */ + fp_kind = FP_387; + fp_save_kind = FP_FNSAVE; + + if (CPU_HAS_FEATURE(CPU_FEATURE_XSAVE)) { + unsigned eax, ebx, ecx, edx; + unsigned xsave_cpu_features; + + eax = 0xd; + ecx = 0x0; + cpuid(eax, ebx, ecx, edx); + fp_xsave_support = eax + (((uint64_t) edx) << 32); + +#ifndef MACH_RING1 + set_cr4(get_cr4() | CR4_OSFXSR | CR4_OSXSAVE); + set_xcr0(fp_xsave_support); +#endif /* MACH_RING1 */ + + eax = 0xd; + ecx = 0x1; + cpuid(eax, ebx, ecx, edx); + xsave_cpu_features = eax; + + if (xsave_cpu_features & CPU_FEATURE_XSAVES) { + // all states enabled by XCR0|IA32_XSS + fp_xsave_size = offsetof(struct i386_fpsave_state, xfp_save_state) + ebx; + if (fp_xsave_size < sizeof(struct i386_fpsave_state)) + panic("CPU-provided xstate size %d " + "is smaller than our minimum %d!\n", + fp_xsave_size, + (int) sizeof(struct i386_fpsave_state)); + + fp_save_kind = FP_XSAVES; + } else { + eax = 0xd; + ecx = 0x0; + cpuid(eax, ebx, ecx, edx); + // all states enabled by XCR0 + fp_xsave_size = offsetof(struct i386_fpsave_state, xfp_save_state) + ebx; + if(fp_xsave_size < sizeof(struct i386_fpsave_state)) + panic("CPU-provided xstate size %d " + "is smaller than our minimum %d!\n", + fp_xsave_size, + (int) sizeof(struct i386_fpsave_state)); + + if (xsave_cpu_features & CPU_FEATURE_XSAVEOPT) + fp_save_kind = FP_XSAVEOPT; + else if (xsave_cpu_features & CPU_FEATURE_XSAVEC) + fp_save_kind = FP_XSAVEC; + else + fp_save_kind = FP_XSAVE; + } + + fp_kind = FP_387X; + } + + else if (CPU_HAS_FEATURE(CPU_FEATURE_FXSR)) { +#ifndef MACH_RING1 + set_cr4(get_cr4() | CR4_OSFXSR); +#endif /* MACH_RING1 */ + fp_kind = FP_387FX; + fp_save_kind = FP_FXSAVE; + } + + if (fp_save_kind != FP_FNSAVE) { + /* Compute mxcsr_feature_mask. */ + static /* because we _need_ alignment */ + struct i386_xfp_save save; + unsigned long mask; + fxsave(&save); + mask = save.fp_mxcsr_mask; + if (!mask) + mask = 0x0000ffbf; + mxcsr_feature_mask &= mask; + } + } +#ifdef MACH_RING1 + set_ts(); +#else /* MACH_RING1 */ + /* + * Trap wait instructions. Turn off FPU for now. + */ + set_cr0(get_cr0() | CR0_TS | CR0_MP); +#endif /* MACH_RING1 */ + } + else { + /* + * NO FPU. + */ + panic("No FPU!"); + } +} + +/* + * Initialize FP handling. + */ +void +fpu_module_init(void) +{ + kmem_cache_init(&ifps_cache, "i386_fpsave_state", + fp_xsave_size, + alignof(struct i386_fpsave_state), + NULL, 0); + + fp_default_state = (struct i386_fpsave_state *) kmem_cache_alloc(&ifps_cache); + memset(fp_default_state, 0, fp_xsave_size); + + /* Get default state from CPU. */ + clear_ts(); + fninit(); + switch (fp_save_kind) { + case FP_XSAVEC: + case FP_XSAVES: + /* XRSTORS requires compact format, a bit faster anyway */ + fp_default_state->xfp_save_state.header.xcomp_bv = XSAVE_XCOMP_BV_COMPACT; + /* Fallthrough */ + case FP_XSAVE: + case FP_XSAVEOPT: + case FP_FXSAVE: + fxsave(&fp_default_state->xfp_save_state); + break; + case FP_FNSAVE: + fnsave(&fp_default_state->fp_save_state); + break; + } + set_ts(); + + fp_default_state->fp_valid = TRUE; +} + +/* + * Free a FPU save area. + * Called only when thread terminating - no locking necessary. + */ +void +fp_free(struct i386_fpsave_state *fps) +{ +ASSERT_IPL(SPL0); +#if NCPUS == 1 + if ((fp_thread != THREAD_NULL) && (fp_thread->pcb->ims.ifps == fps)) { + /* + * Make sure we don't get FPU interrupts later for + * this thread + */ + clear_ts(); + fwait(); + + /* Mark it free and disable access */ + clear_fpu(); + } +#endif /* NCPUS == 1 */ + kmem_cache_free(&ifps_cache, (vm_offset_t) fps); +} + +/* The two following functions were stolen from Linux's i387.c */ +static inline unsigned short +twd_i387_to_fxsr (unsigned short twd) +{ + unsigned int tmp; /* to avoid 16 bit prefixes in the code */ + + /* Transform each pair of bits into 01 (valid) or 00 (empty) */ + tmp = ~twd; + tmp = (tmp | (tmp>>1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */ + /* and move the valid bits to the lower byte. */ + tmp = (tmp | (tmp >> 1)) & 0x3333; /* 00VV00VV00VV00VV */ + tmp = (tmp | (tmp >> 2)) & 0x0f0f; /* 0000VVVV0000VVVV */ + tmp = (tmp | (tmp >> 4)) & 0x00ff; /* 00000000VVVVVVVV */ + return tmp; +} + +static inline unsigned long +twd_fxsr_to_i387 (struct i386_xfp_save *fxsave) +{ + struct { + unsigned short significand[4]; + unsigned short exponent; + unsigned short padding[3]; + } *st = NULL; + unsigned long tos = (fxsave->fp_status >> 11) & 7; + unsigned long twd = (unsigned long) fxsave->fp_tag; + unsigned long tag; + unsigned long ret = 0xffff0000u; + int i; + +#define FPREG_ADDR(f, n) ((void *)&(f)->fp_reg_word + (n) * 16); + + for (i = 0 ; i < 8 ; i++) { + if (twd & 0x1) { + st = FPREG_ADDR (fxsave, (i - tos) & 7); + + switch (st->exponent & 0x7fff) { + case 0x7fff: + tag = 2; /* Special */ + break; + case 0x0000: + if (!st->significand[0] && + !st->significand[1] && + !st->significand[2] && + !st->significand[3] ) { + tag = 1; /* Zero */ + } else { + tag = 2; /* Special */ + } + break; + default: + if (st->significand[3] & 0x8000) { + tag = 0; /* Valid */ + } else { + tag = 2; /* Special */ + } + break; + } + } else { + tag = 3; /* Empty */ + } + ret |= (tag << (2 * i)); + twd = twd >> 1; + } + return ret; +} + +/* + * Set the floating-point state for a thread. + * If the thread is not the current thread, it is + * not running (held). Locking needed against + * concurrent fpu_set_state or fpu_get_state. + */ +kern_return_t +fpu_set_state(const thread_t thread, + struct i386_float_state *state) +{ + pcb_t pcb = thread->pcb; + struct i386_fpsave_state *ifps; + struct i386_fpsave_state *new_ifps; + +ASSERT_IPL(SPL0); + if (fp_kind == FP_NO) + return KERN_FAILURE; + +#if NCPUS == 1 + + /* + * If this thread`s state is in the FPU, + * discard it; we are replacing the entire + * FPU state. + */ + if (fp_thread == thread) { + clear_ts(); + fwait(); /* wait for possible interrupt */ + clear_fpu(); /* no state in FPU */ + } +#endif + + if (state->initialized == 0) { + /* + * new FPU state is 'invalid'. + * Deallocate the fp state if it exists. + */ + simple_lock(&pcb->lock); + ifps = pcb->ims.ifps; + pcb->ims.ifps = 0; + simple_unlock(&pcb->lock); + + if (ifps != 0) { + kmem_cache_free(&ifps_cache, (vm_offset_t) ifps); + } + } + else { + /* + * Valid state. Allocate the fp state if there is none. + */ + struct i386_fp_save *user_fp_state; + struct i386_fp_regs *user_fp_regs; + + user_fp_state = (struct i386_fp_save *) &state->hw_state[0]; + user_fp_regs = (struct i386_fp_regs *) + &state->hw_state[sizeof(struct i386_fp_save)]; + + new_ifps = 0; + Retry: + simple_lock(&pcb->lock); + ifps = pcb->ims.ifps; + if (ifps == 0) { + if (new_ifps == 0) { + simple_unlock(&pcb->lock); + new_ifps = (struct i386_fpsave_state *) kmem_cache_alloc(&ifps_cache); + goto Retry; + } + ifps = new_ifps; + new_ifps = 0; + pcb->ims.ifps = ifps; + } + + /* + * Ensure that reserved parts of the environment are 0. + */ + memset(ifps, 0, fp_xsave_size); + + if (fp_save_kind != FP_FNSAVE) { + int i; + + ifps->xfp_save_state.fp_control = user_fp_state->fp_control; + ifps->xfp_save_state.fp_status = user_fp_state->fp_status; + ifps->xfp_save_state.fp_tag = twd_i387_to_fxsr(user_fp_state->fp_tag); + ifps->xfp_save_state.fp_eip = user_fp_state->fp_eip; + ifps->xfp_save_state.fp_cs = user_fp_state->fp_cs; + ifps->xfp_save_state.fp_opcode = user_fp_state->fp_opcode; + ifps->xfp_save_state.fp_dp = user_fp_state->fp_dp; + ifps->xfp_save_state.fp_ds = user_fp_state->fp_ds; + for (i=0; i<8; i++) + memcpy(&ifps->xfp_save_state.fp_reg_word[i], &user_fp_regs->fp_reg_word[i], sizeof(user_fp_regs->fp_reg_word[i])); + } else { + ifps->fp_save_state.fp_control = user_fp_state->fp_control; + ifps->fp_save_state.fp_status = user_fp_state->fp_status; + ifps->fp_save_state.fp_tag = user_fp_state->fp_tag; + ifps->fp_save_state.fp_eip = user_fp_state->fp_eip; + ifps->fp_save_state.fp_cs = user_fp_state->fp_cs; + ifps->fp_save_state.fp_opcode = user_fp_state->fp_opcode; + ifps->fp_save_state.fp_dp = user_fp_state->fp_dp; + ifps->fp_save_state.fp_ds = user_fp_state->fp_ds; + ifps->fp_regs = *user_fp_regs; + } + + simple_unlock(&pcb->lock); + if (new_ifps != 0) + kmem_cache_free(&ifps_cache, (vm_offset_t) new_ifps); + } + + return KERN_SUCCESS; +} + +/* + * Get the floating-point state for a thread. + * If the thread is not the current thread, it is + * not running (held). Locking needed against + * concurrent fpu_set_state or fpu_get_state. + */ +kern_return_t +fpu_get_state(const thread_t thread, + struct i386_float_state *state) +{ + pcb_t pcb = thread->pcb; + struct i386_fpsave_state *ifps; + +ASSERT_IPL(SPL0); + if (fp_kind == FP_NO) + return KERN_FAILURE; + + simple_lock(&pcb->lock); + ifps = pcb->ims.ifps; + if (ifps == 0) { + /* + * No valid floating-point state. + */ + simple_unlock(&pcb->lock); + memset(state, 0, sizeof(struct i386_float_state)); + return KERN_SUCCESS; + } + + /* Make sure we`ve got the latest fp state info */ + /* If the live fpu state belongs to our target */ +#if NCPUS == 1 + if (thread == fp_thread) +#else + if (thread == current_thread()) +#endif + { + clear_ts(); + fp_save(thread); + clear_fpu(); + } + + state->fpkind = fp_kind; + state->exc_status = 0; + + { + struct i386_fp_save *user_fp_state; + struct i386_fp_regs *user_fp_regs; + + state->initialized = ifps->fp_valid; + + user_fp_state = (struct i386_fp_save *) &state->hw_state[0]; + user_fp_regs = (struct i386_fp_regs *) + &state->hw_state[sizeof(struct i386_fp_save)]; + + /* + * Ensure that reserved parts of the environment are 0. + */ + memset(user_fp_state, 0, sizeof(struct i386_fp_save)); + + if (fp_save_kind != FP_FNSAVE) { + int i; + + user_fp_state->fp_control = ifps->xfp_save_state.fp_control; + user_fp_state->fp_status = ifps->xfp_save_state.fp_status; + user_fp_state->fp_tag = twd_fxsr_to_i387(&ifps->xfp_save_state); + user_fp_state->fp_eip = ifps->xfp_save_state.fp_eip; + user_fp_state->fp_cs = ifps->xfp_save_state.fp_cs; + user_fp_state->fp_opcode = ifps->xfp_save_state.fp_opcode; + user_fp_state->fp_dp = ifps->xfp_save_state.fp_dp; + user_fp_state->fp_ds = ifps->xfp_save_state.fp_ds; + for (i=0; i<8; i++) + memcpy(&user_fp_regs->fp_reg_word[i], &ifps->xfp_save_state.fp_reg_word[i], sizeof(user_fp_regs->fp_reg_word[i])); + } else { + user_fp_state->fp_control = ifps->fp_save_state.fp_control; + user_fp_state->fp_status = ifps->fp_save_state.fp_status; + user_fp_state->fp_tag = ifps->fp_save_state.fp_tag; + user_fp_state->fp_eip = ifps->fp_save_state.fp_eip; + user_fp_state->fp_cs = ifps->fp_save_state.fp_cs; + user_fp_state->fp_opcode = ifps->fp_save_state.fp_opcode; + user_fp_state->fp_dp = ifps->fp_save_state.fp_dp; + user_fp_state->fp_ds = ifps->fp_save_state.fp_ds; + *user_fp_regs = ifps->fp_regs; + } + } + simple_unlock(&pcb->lock); + + return KERN_SUCCESS; +} + +/* + * Initialize FPU for an already-running thread. + */ +static void fpinit(thread_t thread) +{ + unsigned short control; + +ASSERT_IPL(SPL0); + clear_ts(); + fpu_rstor(fp_default_state); + + control = thread->pcb->init_control; + if (control) + fldcw(control); +} + +/* + * Inherit FPU state from a parent to a child, if any + */ +void fpinherit(thread_t parent_thread, thread_t thread) +{ + pcb_t pcb = parent_thread->pcb; + struct i386_fpsave_state *ifps; + + ifps = pcb->ims.ifps; + if (ifps) { + /* Parent does have a state, inherit it */ + if (ifps->fp_valid == TRUE) + thread->pcb->init_control = ifps->fp_save_state.fp_control; + else + /* State is in the FPU, fetch from there */ + fnstcw(&thread->pcb->init_control); + } +} + +/* + * Coprocessor not present. + */ +void +fpnoextflt(void) +{ + /* + * Enable FPU use. + */ +ASSERT_IPL(SPL0); + clear_ts(); +#if NCPUS == 1 + + /* + * If this thread`s state is in the FPU, we are done. + */ + if (fp_thread == current_thread()) + return; + + /* Make sure we don't do fpsave() in fp_intr while doing fpsave() + * here if the current fpu instruction generates an error. + */ + fwait(); + /* + * If another thread`s state is in the FPU, save it. + */ + if (fp_thread != THREAD_NULL) { + fp_save(fp_thread); + } + + /* + * Give this thread the FPU. + */ + fp_thread = current_thread(); + +#endif /* NCPUS == 1 */ + + /* + * Load this thread`s state into the FPU. + */ + fp_load(current_thread()); +} + +/* + * FPU overran end of segment. + * Re-initialize FPU. Floating point state is not valid. + */ +void +fpextovrflt(void) +{ + thread_t thread = current_thread(); + pcb_t pcb; + struct i386_fpsave_state *ifps; + +#if NCPUS == 1 + + /* + * Is exception for the currently running thread? + */ + if (fp_thread != thread) { + /* Uh oh... */ + panic("fpextovrflt"); + } +#endif + + /* + * This is a non-recoverable error. + * Invalidate the thread`s FPU state. + */ + pcb = thread->pcb; + simple_lock(&pcb->lock); + ifps = pcb->ims.ifps; + pcb->ims.ifps = 0; + simple_unlock(&pcb->lock); + + /* + * Re-initialize the FPU. + */ + clear_ts(); + fninit(); + + /* + * And disable access. + */ + clear_fpu(); + + if (ifps) + kmem_cache_free(&ifps_cache, (vm_offset_t) ifps); + + /* + * Raise exception. + */ + i386_exception(EXC_BAD_ACCESS, VM_PROT_READ|VM_PROT_EXECUTE, 0); + /*NOTREACHED*/ +} + +static int +fphandleerr(void) +{ + thread_t thread = current_thread(); + + /* + * Save the FPU context to the thread using it. + */ +#if NCPUS == 1 + if (fp_thread == THREAD_NULL) { + printf("fphandleerr: FPU not belonging to anyone!\n"); + clear_ts(); + fninit(); + clear_fpu(); + return 1; + } + + if (fp_thread != thread) { + /* + * FPU exception is for a different thread. + * When that thread again uses the FPU an exception will be + * raised in fp_load. Remember the condition in fp_valid (== 2). + */ + clear_ts(); + fp_save(fp_thread); + fp_thread->pcb->ims.ifps->fp_valid = 2; + fninit(); + clear_fpu(); + /* leave fp_intr_thread THREAD_NULL */ + return 1; + } +#endif /* NCPUS == 1 */ + + /* + * Save the FPU state and turn off the FPU. + */ + clear_ts(); + fp_save(thread); + fninit(); + clear_fpu(); + + return 0; +} + +/* + * FPU error. Called by exception handler. + */ +void +fpexterrflt(void) +{ + thread_t thread = current_thread(); + + if (fphandleerr()) + return; + + /* + * Raise FPU exception. + * Locking not needed on pcb->ims.ifps, + * since thread is running. + */ + i386_exception(EXC_ARITHMETIC, + EXC_I386_EXTERR, + fp_save_kind != FP_FNSAVE ? + thread->pcb->ims.ifps->xfp_save_state.fp_status : + thread->pcb->ims.ifps->fp_save_state.fp_status); + /*NOTREACHED*/ +} + +#ifndef MACH_RING1 +/* + * FPU error. Called by AST. + */ +void +fpastintr(void) +{ + thread_t thread = current_thread(); + +ASSERT_IPL(SPL0); +#if NCPUS == 1 + /* + * Since FPU errors only occur on ESC or WAIT instructions, + * the current thread should own the FPU. If it didn`t, + * we should have gotten the task-switched interrupt first. + */ + if (fp_thread != THREAD_NULL) { + panic("fpexterrflt"); + return; + } + + /* + * Check if we got a context switch between the interrupt and the AST + * This can happen if the interrupt arrived after the FPU AST was + * checked. In this case, raise the exception in fp_load when this + * thread next time uses the FPU. Remember exception condition in + * fp_valid (extended boolean 2). + */ + if (fp_intr_thread != thread) { + if (fp_intr_thread == THREAD_NULL) { + panic("fpexterrflt: fp_intr_thread == THREAD_NULL"); + return; + } + fp_intr_thread->pcb->ims.ifps->fp_valid = 2; + fp_intr_thread = THREAD_NULL; + return; + } + fp_intr_thread = THREAD_NULL; +#else /* NCPUS == 1 */ + /* + * Save the FPU state and turn off the FPU. + */ + fp_save(thread); +#endif /* NCPUS == 1 */ + + /* + * Raise FPU exception. + * Locking not needed on pcb->ims.ifps, + * since thread is running. + */ + i386_exception(EXC_ARITHMETIC, + EXC_I386_EXTERR, + fp_save_kind != FP_FNSAVE ? + thread->pcb->ims.ifps->xfp_save_state.fp_status : + thread->pcb->ims.ifps->fp_save_state.fp_status); + /*NOTREACHED*/ +} +#endif /* MACH_RING1 */ + +/* + * Save FPU state. + * + * Locking not needed: + * . if called from fpu_get_state, pcb already locked. + * . if called from fpnoextflt or fp_intr, we are single-cpu + * . otherwise, thread is running. + */ +void +fp_save(thread_t thread) +{ + pcb_t pcb = thread->pcb; + struct i386_fpsave_state *ifps = pcb->ims.ifps; + + if (ifps != 0 && !ifps->fp_valid) + /* registers are in FPU */ + fpu_save(ifps); +} + +/* + * Restore FPU state from PCB. + * + * Locking not needed; always called on the current thread. + */ +void +fp_load(thread_t thread) +{ + pcb_t pcb = thread->pcb; + struct i386_fpsave_state *ifps; + +ASSERT_IPL(SPL0); + ifps = pcb->ims.ifps; + if (ifps == 0) { + ifps = (struct i386_fpsave_state *) kmem_cache_alloc(&ifps_cache); + memcpy(ifps, fp_default_state, fp_xsave_size); + pcb->ims.ifps = ifps; + fpinit(thread); +#if 1 +/* + * I'm not sure this is needed. Does the fpu regenerate the interrupt in + * frstor or not? Without this code we may miss some exceptions, with it + * we might send too many exceptions. + */ + } else if (ifps->fp_valid == 2) { + /* delayed exception pending */ + + ifps->fp_valid = TRUE; + clear_fpu(); + /* + * Raise FPU exception. + * Locking not needed on pcb->ims.ifps, + * since thread is running. + */ + i386_exception(EXC_ARITHMETIC, + EXC_I386_EXTERR, + fp_save_kind != FP_FNSAVE ? + thread->pcb->ims.ifps->xfp_save_state.fp_status : + thread->pcb->ims.ifps->fp_save_state.fp_status); + /*NOTREACHED*/ +#endif + } else if (! ifps->fp_valid) { + printf("fp_load: invalid FPU state!\n"); + fninit (); + } else { + fpu_rstor(ifps); + } + ifps->fp_valid = FALSE; /* in FPU */ +} + +#if (defined(AT386) || defined(ATX86_64)) && !defined(MACH_XEN) +/* + * Handle a coprocessor error interrupt on the AT386. + * This comes in on line 5 of the slave PIC at SPL1. + */ +void +fpintr(int unit) +{ + spl_t s; +#if NCPUS == 1 + thread_t thread = current_thread(); +#endif /* NCPUS == 1 */ + +ASSERT_IPL(SPL1); + /* + * Turn off the extended 'busy' line. + */ + outb(0xf0, 0); + + if (fphandleerr()) + return; + +#if NCPUS == 1 + if (fp_intr_thread != THREAD_NULL && fp_intr_thread != thread) + panic("fp_intr: already caught intr"); + fp_intr_thread = thread; +#endif /* NCPUS == 1 */ + + /* + * Since we are running on the interrupt stack, we must + * signal the thread to take the exception when we return + * to user mode. Use an AST to do this. + * + * Don`t set the thread`s AST field. If the thread is + * descheduled before it takes the AST, it will notice + * the FPU error when it reloads its FPU state. + */ + s = splsched(); + ast_on(cpu_number(), AST_I386_FP); + splx(s); +} +#endif /* AT386 */ diff --git a/i386/i386/fpu.h b/i386/i386/fpu.h new file mode 100644 index 0000000..51e0f31 --- /dev/null +++ b/i386/i386/fpu.h @@ -0,0 +1,250 @@ +/* + * Mach Operating System + * Copyright (c) 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. + */ + +#ifndef _I386_FPU_H_ +#define _I386_FPU_H_ + +/* + * Macro definitions for routines to manipulate the + * floating-point processor. + */ + +#include +#include +#include + +/* + * FPU instructions. + */ +#define fninit() \ + asm volatile("fninit") + +#define fnstcw(control) \ + asm("fnstcw %0" : "=m" (*(unsigned short *)(control))) + +#define fstcw(control) \ + asm volatile("fstcw %0" : "=m" (*(unsigned short *)(control))) + +#define fldcw(control) \ + asm volatile("fldcw %0" : : "m" (*(unsigned short *) &(control)) ) + +#define fnstsw() \ + ({ \ + unsigned short _status__; \ + asm("fnstsw %0" : "=ma" (_status__)); \ + _status__; \ + }) + +#define fnclex() \ + asm volatile("fnclex") + +#define fnsave(state) \ + asm volatile("fnsave %0" : "=m" (*state)) + +#define frstor(state) \ + asm volatile("frstor %0" : : "m" (state)) + +#define fxsave(state) \ + asm volatile("fxsave %0" : "=m" (*state)) + +#define fxrstor(state) \ + asm volatile("fxrstor %0" : : "m" (state)) + +static inline uint64_t xgetbv(uint32_t n) { + uint32_t eax, edx; + asm volatile("xgetbv" : "=a" (eax), "=d" (edx) : "c" (n)); + return eax + ((uint64_t) edx << 32); +} + +static inline uint64_t get_xcr0(void) { + return xgetbv(0); +} + +static inline void xsetbv(uint32_t n, uint64_t value) { + uint32_t eax, edx; + + eax = value; + edx = value >> 32; + + asm volatile("xsetbv" : : "c" (n), "a" (eax), "d" (edx)); +} + +static inline void set_xcr0(uint64_t value) { + xsetbv(0, value); +} + +#define CPU_XCR0_X87 (1 << 0) +#define CPU_XCR0_SSE (1 << 1) +#define CPU_XCR0_AVX (1 << 2) +#define CPU_XCR0_MPX (3 << 3) +#define CPU_XCR0_AVX512 (7 << 5) + +#define CPU_FEATURE_XSAVEOPT (1 << 0) +#define CPU_FEATURE_XSAVEC (1 << 1) +#define CPU_FEATURE_XGETBV1 (1 << 2) +#define CPU_FEATURE_XSAVES (1 << 3) + +#define xsave(state) \ + asm volatile("xsave %0" \ + : "=m" (*state) \ + : "a" ((unsigned) fp_xsave_support) \ + , "d" ((unsigned) (fp_xsave_support >> 32))) + +#define xsaveopt(state) \ + asm volatile("xsaveopt %0" \ + : "=m" (*state) \ + : "a" ((unsigned) fp_xsave_support) \ + , "d" ((unsigned) (fp_xsave_support >> 32))) + +#define xsavec(state) \ + asm volatile("xsavec %0" \ + : "=m" (*state) \ + : "a" ((unsigned) fp_xsave_support) \ + , "d" ((unsigned) (fp_xsave_support >> 32))) + +#define xsaves(state) \ + asm volatile("xsaves %0" \ + : "=m" (*state) \ + : "a" ((unsigned) fp_xsave_support) \ + , "d" ((unsigned) (fp_xsave_support >> 32))) + +#define xrstor(state) \ + asm volatile("xrstor %0" : : "m" (state) \ + , "a" ((unsigned) fp_xsave_support) \ + , "d" ((unsigned) (fp_xsave_support >> 32))) + +#define xrstors(state) \ + asm volatile("xrstors %0" : : "m" (state) \ + , "a" ((unsigned) fp_xsave_support) \ + , "d" ((unsigned) (fp_xsave_support >> 32))) + +#define fwait() \ + asm("fwait"); + +#define fpu_load_context(pcb) + +#define fpu_save(ifps) \ + do { \ + switch (fp_save_kind) { \ + case FP_XSAVE: \ + xsave(&(ifps)->xfp_save_state); \ + break; \ + case FP_XSAVEOPT: \ + xsaveopt(&(ifps)->xfp_save_state); \ + break; \ + case FP_XSAVEC: \ + xsavec(&(ifps)->xfp_save_state); \ + break; \ + case FP_XSAVES: \ + xsaves(&(ifps)->xfp_save_state); \ + break; \ + case FP_FXSAVE: \ + fxsave(&(ifps)->xfp_save_state); \ + break; \ + case FP_FNSAVE: \ + fnsave(&(ifps)->fp_save_state); \ + break; \ + } \ + (ifps)->fp_valid = TRUE; \ + } while (0) + +#define fpu_rstor(ifps) \ + do { \ + switch (fp_save_kind) { \ + case FP_XSAVE: \ + case FP_XSAVEOPT: \ + case FP_XSAVEC: \ + xrstor((ifps)->xfp_save_state); \ + break; \ + case FP_XSAVES: \ + xrstors((ifps)->xfp_save_state); \ + break; \ + case FP_FXSAVE: \ + fxrstor((ifps)->xfp_save_state); \ + break; \ + case FP_FNSAVE: \ + frstor((ifps)->fp_save_state); \ + break; \ + } \ + } while (0) + +/* + * Save thread`s FPU context. + * If only one CPU, we just set the task-switched bit, + * to keep the new thread from using the coprocessor. + * If multiple CPUs, we save the entire state. + */ +#if NCPUS > 1 +#define fpu_save_context(thread) \ + { \ + struct i386_fpsave_state *ifps; \ + ifps = (thread)->pcb->ims.ifps; \ + if (ifps != 0 && !ifps->fp_valid) { \ + /* registers are in FPU - save to memory */ \ + fpu_save(ifps); \ + set_ts(); \ + } \ + } + +#else /* NCPUS == 1 */ +#define fpu_save_context(thread) \ + { \ + set_ts(); \ + } + +#endif /* NCPUS == 1 */ + +enum fp_save_kind { + FP_FNSAVE, + FP_FXSAVE, + FP_XSAVE, + FP_XSAVEOPT, + FP_XSAVEC, + FP_XSAVES, +}; +extern int fp_kind; +extern enum fp_save_kind fp_save_kind; +extern struct i386_fpsave_state *fp_default_state; +extern uint64_t fp_xsave_support; +extern void fp_save(thread_t thread); +extern void fp_load(thread_t thread); +extern void fp_free(struct i386_fpsave_state *fps); +extern void fpu_module_init(void); +extern kern_return_t fpu_set_state( + thread_t thread, + struct i386_float_state *state); +extern kern_return_t fpu_get_state( + thread_t thread, + struct i386_float_state *state); +extern void fpnoextflt(void); +extern void fpextovrflt(void); +extern void fpexterrflt(void); +extern void fpastintr(void); +extern void init_fpu(void); +extern void fpintr(int unit); +extern void fpinherit(thread_t parent_thread, thread_t thread); + +#endif /* _I386_FPU_H_ */ diff --git a/i386/i386/gdt.c b/i386/i386/gdt.c new file mode 100644 index 0000000..4edd3ec --- /dev/null +++ b/i386/i386/gdt.c @@ -0,0 +1,166 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990 Carnegie Mellon University + * Copyright (c) 1991 IBM Corporation + * 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, + * and that the name IBM not be used in advertising or publicity + * pertaining to distribution of the software without specific, written + * prior permission. + * + * CARNEGIE MELLON AND IBM ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON AND IBM DISCLAIM 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. + */ +/* + * Global descriptor table. + */ +#include +#include + +#include +#include +#include +#include + +#include "vm_param.h" +#include "seg.h" +#include "gdt.h" +#include "mp_desc.h" + +#ifdef MACH_PV_DESCRIPTORS +/* It is actually defined in xen_boothdr.S */ +extern +#endif /* MACH_PV_DESCRIPTORS */ +struct real_descriptor gdt[GDTSZ]; + +static void +gdt_fill(int cpu, struct real_descriptor *mygdt) +{ + /* Initialize the kernel code and data segment descriptors. */ +#ifdef __x86_64__ + assert(LINEAR_MIN_KERNEL_ADDRESS - VM_MIN_KERNEL_ADDRESS == 0); + _fill_gdt_descriptor(mygdt, KERNEL_CS, 0, 0, ACC_PL_K|ACC_CODE_R, SZ_64); + _fill_gdt_descriptor(mygdt, KERNEL_DS, 0, 0, ACC_PL_K|ACC_DATA_W, SZ_64); +#ifndef MACH_PV_DESCRIPTORS + _fill_gdt_descriptor(mygdt, LINEAR_DS, 0, 0, ACC_PL_K|ACC_DATA_W, SZ_64); +#endif /* MACH_PV_DESCRIPTORS */ +#else + _fill_gdt_descriptor(mygdt, KERNEL_CS, + LINEAR_MIN_KERNEL_ADDRESS - VM_MIN_KERNEL_ADDRESS, + LINEAR_MAX_KERNEL_ADDRESS - (LINEAR_MIN_KERNEL_ADDRESS - VM_MIN_KERNEL_ADDRESS) - 1, + ACC_PL_K|ACC_CODE_R, SZ_32); + _fill_gdt_descriptor(mygdt, KERNEL_DS, + LINEAR_MIN_KERNEL_ADDRESS - VM_MIN_KERNEL_ADDRESS, + LINEAR_MAX_KERNEL_ADDRESS - (LINEAR_MIN_KERNEL_ADDRESS - VM_MIN_KERNEL_ADDRESS) - 1, + ACC_PL_K|ACC_DATA_W, SZ_32); +#ifndef MACH_PV_DESCRIPTORS + _fill_gdt_descriptor(mygdt, LINEAR_DS, + 0, + 0xffffffff, + ACC_PL_K|ACC_DATA_W, SZ_32); +#endif /* MACH_PV_DESCRIPTORS */ + vm_offset_t thiscpu = kvtolin(&percpu_array[cpu]); + _fill_gdt_descriptor(mygdt, PERCPU_DS, + thiscpu, + thiscpu + sizeof(struct percpu) - 1, +#ifdef __x86_64__ + ACC_PL_K|ACC_DATA_W, SZ_64 +#else + ACC_PL_K|ACC_DATA_W, SZ_32 +#endif + ); +#endif + +#ifdef MACH_PV_DESCRIPTORS + unsigned long frame = kv_to_mfn(mygdt); + pmap_set_page_readonly(mygdt); + if (hyp_set_gdt(kv_to_la(&frame), GDTSZ)) + panic("couldn't set gdt\n"); +#endif +#ifdef MACH_PV_PAGETABLES + if (hyp_vm_assist(VMASST_CMD_enable, VMASST_TYPE_4gb_segments)) + panic("couldn't set 4gb segments vm assist"); +#if 0 + if (hyp_vm_assist(VMASST_CMD_enable, VMASST_TYPE_4gb_segments_notify)) + panic("couldn't set 4gb segments vm assist notify"); +#endif +#endif /* MACH_PV_PAGETABLES */ + +#ifndef MACH_PV_DESCRIPTORS + /* Load the new GDT. */ + { + struct pseudo_descriptor pdesc; + + pdesc.limit = (GDTSZ * sizeof(struct real_descriptor))-1; + pdesc.linear_base = kvtolin(mygdt); + lgdt(&pdesc); + } +#endif /* MACH_PV_DESCRIPTORS */ +} + +static void +reload_segs(void) +{ + /* Reload all the segment registers from the new GDT. + We must load ds and es with 0 before loading them with KERNEL_DS + because some processors will "optimize out" the loads + if the previous selector values happen to be the same. */ +#ifndef __x86_64__ + asm volatile("ljmp %0,$1f\n" + "1:\n" + "movw %w2,%%ds\n" + "movw %w2,%%es\n" + "movw %w2,%%fs\n" + "movw %w2,%%gs\n" + + "movw %w1,%%ds\n" + "movw %w1,%%es\n" + "movw %w3,%%gs\n" + "movw %w1,%%ss\n" + : : "i" (KERNEL_CS), "r" (KERNEL_DS), "r" (0), "r" (PERCPU_DS)); +#endif +} + +void +gdt_init(void) +{ + gdt_fill(0, gdt); + + reload_segs(); + +#ifdef MACH_PV_PAGETABLES +#if VM_MIN_KERNEL_ADDRESS != LINEAR_MIN_KERNEL_ADDRESS + /* things now get shifted */ +#ifdef MACH_PSEUDO_PHYS + pfn_list = (void*) pfn_list + VM_MIN_KERNEL_ADDRESS - LINEAR_MIN_KERNEL_ADDRESS; +#endif /* MACH_PSEUDO_PHYS */ + la_shift += LINEAR_MIN_KERNEL_ADDRESS - VM_MIN_KERNEL_ADDRESS; +#endif +#endif /* MACH_PV_PAGETABLES */ +} + +#if NCPUS > 1 +void +ap_gdt_init(int cpu) +{ + gdt_fill(cpu, mp_gdt[cpu]); + + reload_segs(); +} +#endif diff --git a/i386/i386/gdt.h b/i386/i386/gdt.h new file mode 100644 index 0000000..c7da012 --- /dev/null +++ b/i386/i386/gdt.h @@ -0,0 +1,121 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990 Carnegie Mellon University + * Copyright (c) 1991 IBM Corporation + * Copyright (c) 1994 The University of Utah and + * the Computer Systems Laboratory (CSL). + * 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, + * and that the name IBM not be used in advertising or publicity + * pertaining to distribution of the software without specific, written + * prior permission. + * + * CARNEGIE MELLON, IBM, AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON, IBM, AND CSL DISCLAIM 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. + */ + +#ifndef _I386_GDT_ +#define _I386_GDT_ + +#include "seg.h" + +/* + * Kernel descriptors for Mach - 32-bit flat address space. + */ +#define KERNEL_CS (0x08 | KERNEL_RING) /* kernel code */ +#define KERNEL_DS (0x10 | KERNEL_RING) /* kernel data */ + + +#ifndef MACH_PV_DESCRIPTORS +#define KERNEL_LDT 0x18 /* master LDT */ +#endif /* MACH_PV_DESCRIPTORS */ + +#ifdef __x86_64__ +/* LDT needs two entries */ +#define KERNEL_TSS 0x40 /* master TSS (uniprocessor) */ +#else +#define KERNEL_TSS 0x20 /* master TSS (uniprocessor) */ +#endif + + +#define USER_LDT 0x28 /* place for per-thread LDT */ + +#ifdef __x86_64__ +/* LDT needs two entries */ +#define USER_TSS 0x58 /* place for per-thread TSS + that holds IO bitmap */ +#else +#define USER_TSS 0x30 /* place for per-thread TSS + that holds IO bitmap */ +#endif + + +#ifndef MACH_PV_DESCRIPTORS +#define LINEAR_DS 0x38 /* linear mapping */ +#endif /* MACH_PV_DESCRIPTORS */ + +/* 0x40 was USER_FPREGS, now used by TSS in 64bit mode */ + +#define USER_GDT 0x48 /* user-defined 32bit GDT entries */ +#define USER_GDT_SLOTS 2 + +/* 0x58 used by user TSS in 64bit mode */ + +#define PERCPU_DS 0x68 /* per-cpu data mapping */ + +#define GDTSZ sel_idx(0x70) + +#ifndef __ASSEMBLER__ + +extern struct real_descriptor gdt[GDTSZ]; + +/* Fill a segment descriptor in the GDT. */ +#define _fill_gdt_descriptor(_gdt, segment, base, limit, access, sizebits) \ + fill_descriptor(&_gdt[sel_idx(segment)], base, limit, access, sizebits) + +#define fill_gdt_descriptor(segment, base, limit, access, sizebits) \ + _fill_gdt_descriptor(gdt, segment, base, limit, access, sizebits) + +/* 64bit variant */ +#ifdef __x86_64__ +#define _fill_gdt_descriptor64(_gdt, segment, base, limit, access, sizebits) \ + fill_descriptor64((struct real_descriptor64 *) &_gdt[sel_idx(segment)], base, limit, access, sizebits) + +#define fill_gdt_descriptor64(segment, base, limit, access, sizebits) \ + _fill_gdt_descriptor64(gdt, segment, base, limit, access, sizebits) +#endif + +/* System descriptor variants */ +#ifdef __x86_64__ +#define _fill_gdt_sys_descriptor(_gdt, segment, base, limit, access, sizebits) \ + _fill_gdt_descriptor64(_gdt, segment, base, limit, access, sizebits) +#define fill_gdt_sys_descriptor(segment, base, limit, access, sizebits) \ + fill_gdt_descriptor64(segment, base, limit, access, sizebits) +#else +#define _fill_gdt_sys_descriptor(_gdt, segment, base, limit, access, sizebits) \ + _fill_gdt_descriptor(_gdt, segment, base, limit, access, sizebits) +#define fill_gdt_sys_descriptor(segment, base, limit, access, sizebits) \ + fill_gdt_descriptor(segment, base, limit, access, sizebits) +#endif + +extern void gdt_init(void); +extern void ap_gdt_init(int cpu); + +#endif /* __ASSEMBLER__ */ +#endif /* _I386_GDT_ */ diff --git a/i386/i386/hardclock.c b/i386/i386/hardclock.c new file mode 100644 index 0000000..9ac4f51 --- /dev/null +++ b/i386/i386/hardclock.c @@ -0,0 +1,81 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990 Carnegie Mellon University + * Copyright (c) 1991 IBM Corporation + * 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, + * and that the name IBM not be used in advertising or publicity + * pertaining to distribution of the software without specific, written + * prior permission. + * + * CARNEGIE MELLON AND IBM ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON AND IBM DISCLAIM 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. + */ +/* + * Clock interrupt. + */ +#include + +#include +#include +#include + +#if defined(AT386) || defined(ATX86_64) +#include +#endif + +#ifdef LINUX_DEV +#include +#endif + +extern char return_to_iret[]; + +void +hardclock(int iunit, /* 'unit' number */ + int old_ipl, /* old interrupt level */ + const char *ret_addr, /* return address in interrupt handler */ + struct i386_interrupt_state *regs /* saved registers */ + ) +{ + if (ret_addr == return_to_iret) + /* + * Interrupt from user mode or from thread stack. + */ + clock_interrupt(tick, /* usec per tick */ + (regs->efl & EFL_VM) || /* user mode */ + ((regs->cs & 0x03) != 0), /* user mode */ +#if defined(LINUX_DEV) + FALSE, /* ignore SPL0 */ +#else /* LINUX_DEV */ + old_ipl == SPL0, /* base priority */ +#endif /* LINUX_DEV */ + regs->eip); /* interrupted eip */ + else + /* + * Interrupt from interrupt stack. + */ + clock_interrupt(tick, /* usec per tick */ + FALSE, /* kernel mode */ + FALSE, /* not SPL0 */ + 0); /* interrupted eip */ + +#ifdef LINUX_DEV + linux_timer_intr(); +#endif /* LINUX_DEV */ +} diff --git a/i386/i386/hardclock.h b/i386/i386/hardclock.h new file mode 100644 index 0000000..b326c3c --- /dev/null +++ b/i386/i386/hardclock.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2013 Free Software Foundation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _I386_HARDCLOCK_H_ +#define _I386_HARDCLOCK_H_ + +void hardclock( + int iunit, + int old_ipl, + const char *ret_addr, + struct i386_interrupt_state *regs); + +#endif /* _I386_HARDCLOCK_H_ */ diff --git a/i386/i386/i386asm.sym b/i386/i386/i386asm.sym new file mode 100644 index 0000000..e1f5c6b --- /dev/null +++ b/i386/i386/i386asm.sym @@ -0,0 +1,194 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990 Carnegie Mellon University + * Copyright (c) 1991 IBM Corporation + * 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, + * and that the name IBM not be used in advertising or publicity + * pertaining to distribution of the software without specific, written + * prior permission. + * + * CARNEGIE MELLON AND IBM ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON AND IBM DISCLAIM 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. + */ + +/* + * Pass field offsets to assembly code. + */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +expr CALL_AST_CHECK +expr CALL_PMAP_UPDATE + +offset ApicLocalUnit lu apic_id APIC_ID + +offset percpu pc cpu_id PERCPU_CPU_ID +offset percpu pc active_thread PERCPU_ACTIVE_THREAD +offset percpu pc active_stack PERCPU_ACTIVE_STACK + +offset pcb pcb iss + +size percpu pc + +offset thread th pcb +offset thread th task +offset thread th recover +offset thread th kernel_stack +offset thread th swap_func + +offset task task eml_dispatch TASK_EMUL + +offset eml_dispatch eml disp_min DISP_MIN +offset eml_dispatch eml disp_count DISP_COUNT +offset eml_dispatch eml disp_vector DISP_VECTOR + +expr &STACK_IKS(0)->k_ebx KSS_EBX +expr &STACK_IKS(0)->k_esp KSS_ESP +expr &STACK_IKS(0)->k_ebp KSS_EBP +#ifdef __i386__ +expr &STACK_IKS(0)->k_esi KSS_ESI +expr &STACK_IKS(0)->k_edi KSS_EDI +#endif +expr &STACK_IKS(0)->k_eip KSS_EIP +#ifdef __x86_64__ +expr &STACK_IKS(0)->k_r12 KSS_R12 +expr &STACK_IKS(0)->k_r13 KSS_R13 +expr &STACK_IKS(0)->k_r14 KSS_R14 +expr &STACK_IKS(0)->k_r15 KSS_R15 +#endif +size i386_kernel_state iks + +size i386_exception_link iel + +#if !defined(__x86_64__) || defined(USER32) +offset i386_saved_state r gs +offset i386_saved_state r fs +#endif +offset i386_saved_state r cs +offset i386_saved_state r uesp +offset i386_saved_state r eax +offset i386_saved_state r ebx +offset i386_saved_state r ecx +offset i386_saved_state r edx +offset i386_saved_state r ebp +offset i386_saved_state r trapno +offset i386_saved_state r err +offset i386_saved_state r efl R_EFLAGS +offset i386_saved_state r eip +offset i386_saved_state r cr2 +offset i386_saved_state r edi +offset i386_saved_state r esi +#ifdef __x86_64__ +offset i386_saved_state r r8 +offset i386_saved_state r r9 +offset i386_saved_state r r10 +offset i386_saved_state r r12 +offset i386_saved_state r r13 +offset i386_saved_state r r14 +offset i386_saved_state r r15 +#endif + +offset i386_interrupt_state i eip +offset i386_interrupt_state i cs +offset i386_interrupt_state i efl + +#ifdef __x86_64__ +offset i386_tss tss rsp0 +#else +offset i386_tss tss esp0 +offset i386_tss tss ss0 +#endif + +offset machine_slot sub_type cpu_type + +expr I386_PGBYTES NBPG +expr VM_MIN_ADDRESS +expr VM_MAX_ADDRESS +expr VM_MIN_KERNEL_ADDRESS KERNELBASE +expr KERNEL_STACK_SIZE +#if defined MACH_PSEUDO_PHYS && (VM_MIN_KERNEL_ADDRESS == LINEAR_MIN_KERNEL_ADDRESS) +expr PFN_LIST pfn_list +#endif + +#if PAE +expr PDPSHIFT +#endif /* PAE */ +expr PDESHIFT +expr PDEMASK +expr PTESHIFT +expr PTEMASK + +expr sizeof(pt_entry_t) PTE_SIZE + +expr INTEL_PTE_PFN PTE_PFN +expr INTEL_PTE_VALID PTE_V +expr INTEL_PTE_WRITE PTE_W +expr INTEL_PTE_PS PTE_S +expr ~INTEL_PTE_VALID PTE_INVALID +expr NPTES PTES_PER_PAGE +expr INTEL_PTE_VALID|INTEL_PTE_WRITE INTEL_PTE_KERNEL + +expr IDTSZ + +expr KERNEL_RING + +expr (VM_MIN_KERNEL_ADDRESS>>PDESHIFT)*sizeof(pt_entry_t) KERNELBASEPDE + +#if MACH_KDB +expr RB_KDB +#endif /* MACH_KDB */ + +expr INTSTACK_SIZE + +#if !STAT_TIME +offset timer tm low_bits LOW_BITS +offset timer tm high_bits HIGH_BITS +offset timer tm high_bits_check HIGH_BITS_CHECK +expr TIMER_HIGH_UNIT +offset thread th system_timer +offset thread th user_timer +#endif + +#ifdef MACH_XEN +offset shared_info si vcpu_info[0].evtchn_upcall_mask CPU_CLI +offset shared_info si vcpu_info[0].evtchn_upcall_pending CPU_PENDING +offset shared_info si vcpu_info[0].evtchn_pending_sel CPU_PENDING_SEL +offset shared_info si evtchn_pending PENDING +offset shared_info si evtchn_mask EVTMASK +#ifdef MACH_PV_PAGETABLES +offset shared_info si vcpu_info[0].arch.cr2 CR2 +#endif /* MACH_PV_PAGETABLES */ +#endif /* MACH_XEN */ + +offset mach_msg_header msgh msgh_size diff --git a/i386/i386/idt-gen.h b/i386/i386/idt-gen.h new file mode 100644 index 0000000..daa6aaf --- /dev/null +++ b/i386/i386/idt-gen.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 1994 The University of Utah and + * the Computer Systems Laboratory at the University of Utah (CSL). + * All rights reserved. + * + * Permission to use, copy, modify and distribute this software is hereby + * granted provided that (1) source code retains these copyright, permission, + * and disclaimer notices, and (2) redistributions including binaries + * reproduce the notices in supporting documentation, and (3) all advertising + * materials mentioning features or use of this software display the following + * acknowledgement: ``This product includes software developed by the + * Computer Systems Laboratory at the University of Utah.'' + * + * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS + * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF + * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * CSL requests users of this software to return to csl-dist@cs.utah.edu any + * improvements that they make and grant CSL redistribution rights. + * + * Author: Bryan Ford, University of Utah CSL + */ + +#ifndef _I386_IDT_ +#define _I386_IDT_ + +#include + +#include "seg.h" + +/* + * Interrupt table must always be at least 32 entries long, + * to cover the basic i386 exception vectors. + * More-specific code will probably define it to be longer, + * to allow separate entrypoints for hardware interrupts. + */ +#ifndef IDTSZ +#error you need to define IDTSZ +#endif + +extern struct real_gate idt[IDTSZ]; + +/* Fill a gate in the IDT. */ +#define fill_idt_gate(_idt, int_num, entry, selector, access, dword_count) \ + fill_gate(&_idt[int_num], entry, selector, access, dword_count) + +#endif /* _I386_IDT_ */ diff --git a/i386/i386/idt.c b/i386/i386/idt.c new file mode 100644 index 0000000..caa44d7 --- /dev/null +++ b/i386/i386/idt.c @@ -0,0 +1,87 @@ +/* + * Copyright (c) 1994 The University of Utah and + * the Computer Systems Laboratory at the University of Utah (CSL). + * All rights reserved. + * + * Permission to use, copy, modify and distribute this software is hereby + * granted provided that (1) source code retains these copyright, permission, + * and disclaimer notices, and (2) redistributions including binaries + * reproduce the notices in supporting documentation, and (3) all advertising + * materials mentioning features or use of this software display the following + * acknowledgement: ``This product includes software developed by the + * Computer Systems Laboratory at the University of Utah.'' + * + * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS + * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF + * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * CSL requests users of this software to return to csl-dist@cs.utah.edu any + * improvements that they make and grant CSL redistribution rights. + * + * Author: Bryan Ford, University of Utah CSL + */ + +#include +#include +#include +#include +#include + +struct real_gate idt[IDTSZ]; + +struct idt_init_entry +{ + unsigned long entrypoint; + unsigned short vector; + unsigned short type; +#ifdef __x86_64__ + unsigned short ist; + unsigned short pad_0; +#endif +}; +extern struct idt_init_entry idt_inittab[]; + +static void +idt_fill(struct real_gate *myidt) +{ +#ifdef MACH_PV_DESCRIPTORS + if (hyp_set_trap_table(kvtolin(idt_inittab))) + panic("couldn't set trap table\n"); +#else /* MACH_PV_DESCRIPTORS */ + struct idt_init_entry *iie = idt_inittab; + + /* Initialize the exception vectors from the idt_inittab. */ + while (iie->entrypoint) + { + fill_idt_gate(myidt, iie->vector, iie->entrypoint, KERNEL_CS, iie->type, +#ifdef __x86_64__ + iie->ist +#else + 0 +#endif + ); + iie++; + } + + /* Load the IDT pointer into the processor. */ + { + struct pseudo_descriptor pdesc; + + pdesc.limit = (IDTSZ * sizeof(struct real_gate))-1; + pdesc.linear_base = kvtolin(myidt); + lidt(&pdesc); + } +#endif /* MACH_PV_DESCRIPTORS */ +} + +void idt_init(void) +{ + idt_fill(idt); +} + +#if NCPUS > 1 +void ap_idt_init(int cpu) +{ + idt_fill(mp_desc_table[cpu]->idt); +} +#endif diff --git a/i386/i386/idt_inittab.S b/i386/i386/idt_inittab.S new file mode 100644 index 0000000..fc80e21 --- /dev/null +++ b/i386/i386/idt_inittab.S @@ -0,0 +1,140 @@ +/* + * 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. + */ +#include + +#include +#include +#include + + +/* We'll be using macros to fill in a table in data hunk 2 + while writing trap entrypoint routines at the same time. + Here's the header that comes before everything else. */ + .data 2 +ENTRY(idt_inittab) + .text + +/* + * Interrupt descriptor table and code vectors for it. + */ +#ifdef MACH_PV_DESCRIPTORS +#define IDT_ENTRY(n,entry,type) \ + .data 2 ;\ + .byte n ;\ + .byte (((type)&ACC_PL)>>5)|((((type)&(ACC_TYPE|ACC_A))==ACC_INTR_GATE)<<2) ;\ + .word KERNEL_CS ;\ + .long entry ;\ + .text +#else /* MACH_PV_DESCRIPTORS */ +#define IDT_ENTRY(n,entry,type) \ + .data 2 ;\ + .long entry ;\ + .word n ;\ + .word type ;\ + .text +#endif /* MACH_PV_DESCRIPTORS */ + +/* + * No error code. Clear error code and push trap number. + */ +#define EXCEPTION(n,name) \ + IDT_ENTRY(n,EXT(name),ACC_PL_K|ACC_TRAP_GATE);\ +ENTRY(name) ;\ + pushl $(0) ;\ + pushl $(n) ;\ + jmp EXT(alltraps) + +/* + * User-accessible exception. Otherwise, same as above. + */ +#define EXCEP_USR(n,name) \ + IDT_ENTRY(n,EXT(name),ACC_PL_U|ACC_TRAP_GATE);\ +ENTRY(name) ;\ + pushl $(0) ;\ + pushl $(n) ;\ + jmp EXT(alltraps) + +/* + * Error code has been pushed. Just push trap number. + */ +#define EXCEP_ERR(n,name) \ + IDT_ENTRY(n,EXT(name),ACC_PL_K|ACC_INTR_GATE);\ +ENTRY(name) ;\ + pushl $(n) ;\ + jmp EXT(alltraps) + +/* + * Special interrupt code: dispatches to a unique entrypoint, + * not defined automatically here. + */ +#define EXCEP_SPC(n,name) \ + IDT_ENTRY(n,EXT(name),ACC_PL_K|ACC_TRAP_GATE) + + +EXCEPTION(0x00,t_zero_div) +EXCEP_SPC(0x01,t_debug) +/* skip NMI interrupt - let more specific code figure that out. */ +EXCEP_USR(0x03,t_int3) +EXCEP_USR(0x04,t_into) +EXCEP_USR(0x05,t_bounds) +EXCEPTION(0x06,t_invop) +EXCEPTION(0x07,t_nofpu) +EXCEPTION(0x08,a_dbl_fault) +EXCEPTION(0x09,a_fpu_over) +EXCEPTION(0x0a,a_inv_tss) +EXCEP_SPC(0x0b,t_segnp) +EXCEP_ERR(0x0c,t_stack_fault) +EXCEP_SPC(0x0d,t_gen_prot) +EXCEP_SPC(0x0e,t_page_fault) +#ifdef MACH_PV_DESCRIPTORS +EXCEP_ERR(0x0f,t_trap_0f) +#else +EXCEPTION(0x0f,t_trap_0f) +#endif +EXCEPTION(0x10,t_fpu_err) +EXCEPTION(0x11,t_trap_11) +EXCEPTION(0x12,t_trap_12) +EXCEPTION(0x13,t_trap_13) +EXCEPTION(0x14,t_trap_14) +EXCEPTION(0x15,t_trap_15) +EXCEPTION(0x16,t_trap_16) +EXCEPTION(0x17,t_trap_17) +EXCEPTION(0x18,t_trap_18) +EXCEPTION(0x19,t_trap_19) +EXCEPTION(0x1a,t_trap_1a) +EXCEPTION(0x1b,t_trap_1b) +EXCEPTION(0x1c,t_trap_1c) +EXCEPTION(0x1d,t_trap_1d) +EXCEPTION(0x1e,t_trap_1e) +EXCEPTION(0x1f,t_trap_1f) + +/* Terminator */ + .data 2 + .long 0 +#ifdef MACH_PV_DESCRIPTORS + .long 0 +#endif /* MACH_PV_DESCRIPTORS */ + diff --git a/i386/i386/io_perm.c b/i386/i386/io_perm.c new file mode 100644 index 0000000..aabff49 --- /dev/null +++ b/i386/i386/io_perm.c @@ -0,0 +1,329 @@ +/* Manipulate I/O permission bitmap objects. + + Copyright (C) 2002, 2007 Free Software Foundation, Inc. + + Written by Marcus Brinkmann. Glued into GNU Mach by Thomas Schwinge. + + This file is part of GNU Mach. + + GNU Mach is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any later + version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* + * Mach Operating System + * Copyright (c) 1993,1992,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. + */ + +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "io_perm.h" +#include "gdt.h" +#include "pcb.h" + +#define PCI_CFG1_START 0xcf8 +#define PCI_CFG1_END 0xcff + +#define CONTAINS_PCI_CFG(from, to) \ + ( ( from <= PCI_CFG1_END ) && ( to >= PCI_CFG1_START ) ) + + +/* Our device emulation ops. See below, at the bottom of this file. */ +static struct device_emulation_ops io_perm_device_emulation_ops; + +/* Flag to hold PCI io cfg access lock */ +static boolean_t taken_pci_cfg = FALSE; + +/* The outtran which allows MIG to convert an io_perm_t object to a port + representing it. */ +ipc_port_t +convert_io_perm_to_port (io_perm_t io_perm) +{ + if (io_perm == IO_PERM_NULL) + return IP_NULL; + + ipc_port_t port; + + port = ipc_port_make_send (io_perm->port); + + return port; +} + + +/* The intran which allows MIG to convert a port representing an + io_perm_t object to the object itself. */ +io_perm_t +convert_port_to_io_perm (ipc_port_t port) +{ + device_t device; + + device = dev_port_lookup (port); + + if (device == DEVICE_NULL) + return IO_PERM_NULL; + + io_perm_t io_perm; + + io_perm = device->emul_data; + + return io_perm; +} + +/* The destructor which is called when the last send right to a port + representing an io_perm_t object vanishes. */ +void +io_perm_deallocate (io_perm_t io_perm) +{ + /* We need to check if the io_perm was a PCI cfg one and release it */ + if (CONTAINS_PCI_CFG(io_perm->from, io_perm->to)) + taken_pci_cfg = FALSE; +} + +/* Our ``no senders'' handling routine. Deallocate the object. */ +static +void +no_senders (mach_no_senders_notification_t *notification) +{ + io_perm_t io_perm; + + io_perm = convert_port_to_io_perm + ((ipc_port_t) notification->not_header.msgh_remote_port); + + assert (io_perm != IO_PERM_NULL); + + ipc_kobject_set (io_perm->port, IKO_NULL, IKOT_NONE); + ipc_port_dealloc_kernel (io_perm->port); + + kfree ((vm_offset_t) io_perm, sizeof *io_perm); +} + + +/* Initialize bitmap by setting all bits to OFF == 1. */ +static inline void +io_bitmap_init (unsigned char *iopb) +{ + memset (iopb, ~0, IOPB_BYTES); +} + + +/* Set selected bits in bitmap to ON == 0. */ +static inline void +io_bitmap_set (unsigned char *iopb, io_port_t from, io_port_t to) +{ + do + iopb[from >> 3] &= ~(1 << (from & 0x7)); + while (from++ != to); +} + + +/* Set selected bits in bitmap to OFF == 1. */ +static inline void +io_bitmap_clear (unsigned char *iopb, io_port_t from, io_port_t to) +{ + do + iopb[from >> 3] |= (1 << (from & 0x7)); + while (from++ != to); +} + + +/* Request a new port IO_PERM that represents the capability to access + the I/O ports [FROM; TO] directly. MASTER_PORT is the master device port. + + The function returns KERN_INVALID_ARGUMENT if TARGET_TASK is not a task, + or FROM is greater than TO. + + The function is exported. */ +kern_return_t +i386_io_perm_create (const ipc_port_t master_port, io_port_t from, io_port_t to, + io_perm_t *new) +{ + if (master_port != master_device_port) + return KERN_INVALID_ARGUMENT; + + /* We do not have to check FROM and TO for the limits [0;IOPB_MAX], as + they're short integers and all values are within these very limits. */ + if (from > to) + return KERN_INVALID_ARGUMENT; + + /* Only one process may take a range that includes PCI cfg registers */ + if (taken_pci_cfg && CONTAINS_PCI_CFG(from, to)) + return KERN_PROTECTION_FAILURE; + + io_perm_t io_perm; + + io_perm = (io_perm_t) kalloc (sizeof *io_perm); + if (io_perm == NULL) + return KERN_RESOURCE_SHORTAGE; + + io_perm->from = from; + io_perm->to = to; + + io_perm->port = ipc_port_alloc_kernel (); + if (io_perm->port == IP_NULL) + { + kfree ((vm_offset_t) io_perm, sizeof *io_perm); + return KERN_RESOURCE_SHORTAGE; + } + + /* Set up the dummy device. */ + ipc_kobject_set(io_perm->port, + (ipc_kobject_t) &io_perm->device, IKOT_DEVICE); + io_perm->device.emul_data = io_perm; + io_perm->device.emul_ops = &io_perm_device_emulation_ops; + + ipc_port_t notify; + + notify = ipc_port_make_sonce(io_perm->port); + ip_lock(io_perm->port); + ipc_port_nsrequest(io_perm->port, 1, notify, ¬ify); + assert(notify == IP_NULL); + + *new = io_perm; + + if (CONTAINS_PCI_CFG(from, to)) + taken_pci_cfg = TRUE; + + return KERN_SUCCESS; +} + +/* Modify the I/O permissions for TARGET_TASK. If ENABLE is TRUE, the + permission to access the I/O ports specified by IO_PERM is granted, + otherwise it is withdrawn. + + The function returns KERN_INVALID_ARGUMENT if TARGET_TASK is not a valid + task or IO_PERM not a valid I/O permission port. + + The function is exported. */ +kern_return_t +i386_io_perm_modify (task_t target_task, io_perm_t io_perm, boolean_t enable) +{ + io_port_t from, to; + unsigned char *iopb; + io_port_t iopb_size; + + if (target_task == TASK_NULL || io_perm == IO_PERM_NULL) + return KERN_INVALID_ARGUMENT; + + from = io_perm->from; + to = io_perm->to; + + simple_lock (&target_task->machine.iopb_lock); + iopb = target_task->machine.iopb; + iopb_size = target_task->machine.iopb_size; + + if (!enable && !iopb_size) + { + simple_unlock (&target_task->machine.iopb_lock); + return KERN_SUCCESS; + } + + if (!iopb) + { + simple_unlock (&target_task->machine.iopb_lock); + iopb = (unsigned char *) kmem_cache_alloc (&machine_task_iopb_cache); + simple_lock (&target_task->machine.iopb_lock); + if (target_task->machine.iopb) + { + if (iopb) + kmem_cache_free (&machine_task_iopb_cache, (vm_offset_t) iopb); + iopb = target_task->machine.iopb; + iopb_size = target_task->machine.iopb_size; + } + else if (iopb) + { + target_task->machine.iopb = iopb; + io_bitmap_init (iopb); + } + else + { + simple_unlock (&target_task->machine.iopb_lock); + return KERN_RESOURCE_SHORTAGE; + } + } + + if (enable) + { + io_bitmap_set (iopb, from, to); + if ((to >> 3) + 1 > iopb_size) + target_task->machine.iopb_size = (to >> 3) + 1; + } + else + { + if ((from >> 3) + 1 > iopb_size) + { + simple_unlock (&target_task->machine.iopb_lock); + return KERN_SUCCESS; + } + + io_bitmap_clear (iopb, from, to); + while (iopb_size > 0 && iopb[iopb_size - 1] == 0xff) + iopb_size--; + target_task->machine.iopb_size = iopb_size; + } + +#if NCPUS>1 +#warning SMP support missing (notify all CPUs running threads in that of the I/O bitmap change). +#endif + if (target_task == current_task()) + update_ktss_iopb (iopb, target_task->machine.iopb_size); + + simple_unlock (&target_task->machine.iopb_lock); + return KERN_SUCCESS; +} + +/* We are some sort of Mach device... */ +static struct device_emulation_ops io_perm_device_emulation_ops = +{ + /* ... in order to be easily able to receive a ``no senders'' notification + which we then use to deallocate ourselves. */ + .no_senders = no_senders +}; diff --git a/i386/i386/io_perm.h b/i386/i386/io_perm.h new file mode 100644 index 0000000..b97cf97 --- /dev/null +++ b/i386/i386/io_perm.h @@ -0,0 +1,63 @@ +/* Data types for I/O permission bitmap objects. + + Copyright (C) 2002, 2007 Free Software Foundation, Inc. + + Written by Marcus Brinkmann. Glued into GNU Mach by Thomas Schwinge. + + This file is part of GNU Mach. + + GNU Mach is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any later + version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _I386_IO_PERM_H_ +#define _I386_IO_PERM_H_ + +#include +#include + + +/* The highest possible I/O port. */ +#define IOPB_MAX 0xffff + +/* The number of bytes needed to hold all permission bits. */ +#define IOPB_BYTES (((IOPB_MAX + 1) + 7) / 8) + +/* An offset that points outside of the permission bitmap, used to + disable all permission. */ +#define IOPB_INVAL 0x2fff + + +/* The type of an I/O port address. */ +typedef unsigned short io_port_t; + + +struct io_perm +{ + /* We use a ``struct device'' for easy management. */ + struct device device; + + ipc_port_t port; + + io_port_t from, to; +}; + +typedef struct io_perm *io_perm_t; + +#define IO_PERM_NULL ((io_perm_t) 0) + +extern io_perm_t convert_port_to_io_perm (ipc_port_t); +extern ipc_port_t convert_io_perm_to_port (io_perm_t); +extern void io_perm_deallocate (io_perm_t); + +#endif /* _I386_IO_PERM_H_ */ diff --git a/i386/i386/ipl.h b/i386/i386/ipl.h new file mode 100644 index 0000000..6e59b36 --- /dev/null +++ b/i386/i386/ipl.h @@ -0,0 +1,83 @@ +/* + * 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. + */ +/* +Copyright (c) 1988,1989 Prime Computer, Inc. Natick, MA 01760 +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 Prime +Computer, Inc. not be used in advertising or publicity +pertaining to distribution of the software without +specific, written prior permission. + +THIS SOFTWARE IS PROVIDED "AS IS", AND PRIME COMPUTER, +INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS +SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN +NO EVENT SHALL PRIME COMPUTER, INC. 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 OUR OF OR IN CONNECTION +WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#ifndef _I386_IPL_H_ +#define _I386_IPL_H_ + +#define SPL0 0 +#define SPL1 1 +#define SPL2 2 +#define SPL3 3 +#define SPL4 4 +#define SPL5 5 +#define SPL6 6 +#define SPL7 7 + +#define SPLPP 5 +#define SPLTTY 6 +#define SPLNI 6 +#define SPLHI 7 +#define IPLHI SPLHI + +#define NSPL (SPL7 + 1) + +#ifdef KERNEL +#ifndef __ASSEMBLER__ +#include +/* Note that interrupts have varying signatures */ +typedef void (*interrupt_handler_fn)(int); +extern interrupt_handler_fn ivect[]; +extern int iunit[]; +extern spl_t curr_ipl[NCPUS]; +#endif /* __ASSEMBLER__ */ +#endif /* KERNEL */ + +#endif /* _I386_IPL_H_ */ diff --git a/i386/i386/irq.c b/i386/i386/irq.c new file mode 100644 index 0000000..a7c9889 --- /dev/null +++ b/i386/i386/irq.c @@ -0,0 +1,73 @@ +/* + * Copyright (C) 1995 Shantanu Goel + * Copyright (C) 2020 Free Software Foundation, Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include + +extern queue_head_t main_intr_queue; + +static void +irq_eoi (struct irqdev *dev, int id) +{ +#ifdef APIC + ioapic_irq_eoi (dev->irq[id]); +#endif +} + +static unsigned int ndisabled_irq[NINTR]; + +void +__disable_irq (irq_t irq_nr) +{ + assert (irq_nr < NINTR); + + spl_t s = splhigh(); + ndisabled_irq[irq_nr]++; + assert (ndisabled_irq[irq_nr] > 0); + if (ndisabled_irq[irq_nr] == 1) + mask_irq (irq_nr); + splx(s); +} + +void +__enable_irq (irq_t irq_nr) +{ + assert (irq_nr < NINTR); + + spl_t s = splhigh(); + assert (ndisabled_irq[irq_nr] > 0); + ndisabled_irq[irq_nr]--; + if (ndisabled_irq[irq_nr] == 0) + unmask_irq (irq_nr); + splx(s); +} + +struct irqdev irqtab = { + "irq", irq_eoi, &main_intr_queue, 0, +#ifdef APIC + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}, +#else + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, +#endif +}; + diff --git a/i386/i386/irq.h b/i386/i386/irq.h new file mode 100644 index 0000000..72bbe57 --- /dev/null +++ b/i386/i386/irq.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2020 Free Software Foundation, Inc. + * + * 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. + * + * THE FREE SOFTWARE FOUNDATION ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. THE FREE SOFTWARE FOUNDATION DISCLAIMS ANY LIABILITY OF ANY KIND + * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + */ + +#ifndef _I386_IRQ_H +#define _I386_IRQ_H + +#ifdef APIC +# include +#else +# include +#endif + +typedef unsigned int irq_t; + +void __enable_irq (irq_t irq); +void __disable_irq (irq_t irq); + +extern struct irqdev irqtab; + +#endif diff --git a/i386/i386/ktss.c b/i386/i386/ktss.c new file mode 100644 index 0000000..34cb6df --- /dev/null +++ b/i386/i386/ktss.c @@ -0,0 +1,92 @@ +/* + * 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. + */ +/* + * Kernel task state segment. + * + * We don't use the i386 task switch mechanism. We need a TSS + * only to hold the kernel stack pointer for the current thread. + * + * XXX multiprocessor?? + */ +#include "vm_param.h" +#include "seg.h" +#include "gdt.h" +#include "ktss.h" +#include "mp_desc.h" + +/* A kernel TSS with a complete I/O bitmap. */ +struct task_tss ktss; + +void +ktss_fill(struct task_tss *myktss, struct real_descriptor *mygdt) +{ + /* XXX temporary exception stacks */ + /* FIXME: make it per-processor */ + static int exception_stack[1024]; + static int double_fault_stack[1024]; + +#ifdef MACH_RING1 + /* Xen won't allow us to do any I/O by default anyway, just register + * exception stack */ + if (hyp_stack_switch(KERNEL_DS, (unsigned long)(exception_stack+1024))) + panic("couldn't register exception stack\n"); +#else /* MACH_RING1 */ + /* Initialize the master TSS descriptor. */ + _fill_gdt_sys_descriptor(mygdt, KERNEL_TSS, + kvtolin(myktss), sizeof(struct task_tss) - 1, + ACC_PL_K|ACC_TSS, 0); + + /* Initialize the master TSS. */ +#ifdef __x86_64__ + myktss->tss.rsp0 = (unsigned long)(exception_stack+1024); + myktss->tss.ist1 = (unsigned long)(double_fault_stack+1024); +#else /* ! __x86_64__ */ + myktss->tss.ss0 = KERNEL_DS; + myktss->tss.esp0 = (unsigned long)(exception_stack+1024); +#endif /* __x86_64__ */ + + myktss->tss.io_bit_map_offset = IOPB_INVAL; + /* Set the last byte in the I/O bitmap to all 1's. */ + myktss->barrier = 0xff; + + /* Load the TSS. */ + ltr(KERNEL_TSS); +#endif /* MACH_RING1 */ +} + +void +ktss_init(void) +{ + ktss_fill(&ktss, gdt); +} + +#if NCPUS > 1 +void +ap_ktss_init(int cpu) +{ + ktss_fill(&mp_desc_table[cpu]->ktss, mp_gdt[cpu]); +} +#endif diff --git a/i386/i386/ktss.h b/i386/i386/ktss.h new file mode 100644 index 0000000..171332d --- /dev/null +++ b/i386/i386/ktss.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 1994 The University of Utah and + * the Computer Systems Laboratory at the University of Utah (CSL). + * All rights reserved. + * + * Permission to use, copy, modify and distribute this software is hereby + * granted provided that (1) source code retains these copyright, permission, + * and disclaimer notices, and (2) redistributions including binaries + * reproduce the notices in supporting documentation, and (3) all advertising + * materials mentioning features or use of this software display the following + * acknowledgement: ``This product includes software developed by the + * Computer Systems Laboratory at the University of Utah.'' + * + * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS + * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF + * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * CSL requests users of this software to return to csl-dist@cs.utah.edu any + * improvements that they make and grant CSL redistribution rights. + * + * Author: Bryan Ford, University of Utah CSL + */ +#ifndef _I386_KTSS_ +#define _I386_KTSS_ + +#include "tss.h" + +extern struct task_tss ktss; + +extern void ktss_init(void); +extern void ap_ktss_init(int cpu); + +#endif /* _I386_KTSS_ */ diff --git a/i386/i386/kttd_interface.c b/i386/i386/kttd_interface.c new file mode 100644 index 0000000..f48fe8e --- /dev/null +++ b/i386/i386/kttd_interface.c @@ -0,0 +1,574 @@ +/* + * Mach Operating System + * Copyright (c) 1993,1992 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. + */ + +#if MACH_TTD + +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +/* + * Shamelessly copied from the ddb sources: + */ +struct i386_saved_state *kttd_last_saved_statep; +struct i386_saved_state kttd_nested_saved_state; +unsigned last_kttd_sp; + +struct i386_saved_state kttd_regs; /* was ddb_regs */ + +extern int kttd_debug; +extern boolean_t kttd_enabled; +extern vm_offset_t virtual_end; + +#define I386_BREAKPOINT 0xcc + +/* + * kernel map + */ +extern vm_map_t kernel_map; + +boolean_t kttd_console_init(void) +{ + /* + * Get local machine's IP address via bootp. + */ + return(ttd_ip_bootp()); +} + +/* + * Execute a break instruction that will invoke ttd + */ +void kttd_break(void) +{ + if (!kttd_enabled) + return; + asm("int3"); +} + +/* + * Halt all processors on the 386at (not really applicable). + */ +void kttd_halt_processors(void) +{ + /* XXX Fix for Sequent!!! */ + /* Only one on AT386, so ignore for now... */ +} + +/* + * Determine whether or not the ehternet device driver supports + * ttd. + */ +boolean_t kttd_supported(void) +{ + return ((int)ttd_get_packet != NULL); +} + +/* + * Return the ttd machine type for the i386at + */ +ttd_machine_type get_ttd_machine_type(void) +{ + return TTD_AT386; +} + +void kttd_machine_getregs(struct i386_gdb_register_state *ttd_state) +{ + ttd_state->gs = kttd_regs.gs; + ttd_state->fs = kttd_regs.fs; + ttd_state->es = kttd_regs.es; + ttd_state->ds = kttd_regs.ds; + ttd_state->edi = kttd_regs.edi; + ttd_state->esi = kttd_regs.esi; + ttd_state->ebp = kttd_regs.ebp; + + /* + * This is set up to point to the right place in + * kttd_trap and . + */ + ttd_state->esp = kttd_regs.uesp; + + ttd_state->ebx = kttd_regs.ebx; + ttd_state->edx = kttd_regs.edx; + ttd_state->ecx = kttd_regs.ecx; + ttd_state->eax = kttd_regs.eax; + ttd_state->eip = kttd_regs.eip; + ttd_state->cs = kttd_regs.cs; + ttd_state->efl = kttd_regs.efl; + ttd_state->ss = kttd_regs.ss; +} + +void kttd_machine_setregs(struct i386_gdb_register_state *ttd_state) +{ + if (kttd_regs.gs != ttd_state->gs) { + if (kttd_debug) + printf("gs 0x%x:0x%x, ", kttd_regs.gs, ttd_state->gs); + kttd_regs.gs = ttd_state->gs; + } + if (kttd_regs.fs != ttd_state->fs) { + if (kttd_debug) + printf("fs 0x%x:0x%x, ", kttd_regs.fs, ttd_state->fs); + kttd_regs.fs = ttd_state->fs; + } + if (kttd_regs.es != ttd_state->es) { + if (kttd_debug) + printf("es 0x%x:0x%x, ", kttd_regs.es, ttd_state->es); + kttd_regs.es = ttd_state->es; + } + if (kttd_regs.ds != ttd_state->ds) { + if (kttd_debug) + printf("ds 0x%x:0x%x, ", kttd_regs.ds, ttd_state->ds); + kttd_regs.ds = ttd_state->ds; + } + if (kttd_regs.edi != ttd_state->edi) { + if (kttd_debug) + printf("edi 0x%x:0x%x, ", kttd_regs.edi, ttd_state->edi); + kttd_regs.edi = ttd_state->edi; + } + if (kttd_regs.esi != ttd_state->esi) { + if (kttd_debug) + printf("esi 0x%x:0x%x, ", kttd_regs.esi, ttd_state->esi); + kttd_regs.esi = ttd_state->esi; + } + if (kttd_regs.ebp != ttd_state->ebp) { + if (kttd_debug) + printf("ebp 0x%x:0x%x, ", kttd_regs.ebp, ttd_state->ebp); + kttd_regs.ebp = ttd_state->ebp; + } + if (kttd_regs.ebx != ttd_state->ebx) { + if (kttd_debug) + printf("ebx 0x%x:0x%x, ", kttd_regs.ebx, ttd_state->ebx); + kttd_regs.ebx = ttd_state->ebx; + } + if (kttd_regs.edx != ttd_state->edx) { + if (kttd_debug) + printf("edx 0x%x:0x%x, ", kttd_regs.edx, ttd_state->edx); + kttd_regs.edx = ttd_state->edx; + } + if (kttd_regs.ecx != ttd_state->ecx) { + if (kttd_debug) + printf("ecx 0x%x:0x%x, ", kttd_regs.ecx, ttd_state->ecx); + kttd_regs.ecx = ttd_state->ecx; + } + if (kttd_regs.eax != ttd_state->eax) { + if (kttd_debug) + printf("eax 0x%x:0x%x, ", kttd_regs.eax, ttd_state->eax); + kttd_regs.eax = ttd_state->eax; + } + if (kttd_regs.eip != ttd_state->eip) { + if (kttd_debug) + printf("eip 0x%x:0x%x, ", kttd_regs.eip, ttd_state->eip); + kttd_regs.eip = ttd_state->eip; + } + if (kttd_regs.cs != ttd_state->cs) { + if (kttd_debug) + printf("cs 0x%x:0x%x, ", kttd_regs.cs, ttd_state->cs); + kttd_regs.cs = ttd_state->cs; + } + if (kttd_regs.efl != ttd_state->efl) { + if (kttd_debug) + printf("efl 0x%x:0x%x, ", kttd_regs.efl, ttd_state->efl); + kttd_regs.efl = ttd_state->efl; + } +#if 0 + /* + * We probably shouldn't mess with the uesp or the ss? XXX + */ + if (kttd_regs.ss != ttd_state->ss) { + if (kttd_debug) + printf("ss 0x%x:0x%x, ", kttd_regs.ss, ttd_state->ss); + kttd_regs.ss = ttd_state->ss; + } +#endif /* 0 */ + +} + +/* + * Enable a page for access, faulting it in if necessary + */ +boolean_t kttd_mem_access(vm_offset_t offset, vm_prot_t access) +{ + kern_return_t code; + + /* + * VM_MIN_KERNEL_ADDRESS if the beginning of equiv + * mapped kernel memory. virtual_end is the end. + * If it's in between it's always accessible + */ + if (offset >= VM_MIN_KERNEL_ADDRESS && offset < virtual_end) + return TRUE; + + if (offset >= virtual_end) { + /* + * fault in the memory just to make sure we can access it + */ + if (kttd_debug) + printf(">>>>>>>>>>Faulting in memory: 0x%x, 0x%x\n", + trunc_page(offset), access); + code = vm_fault(kernel_map, trunc_page(offset), access, FALSE, + FALSE, (void (*)()) 0); + } else { + /* + * Check for user thread + */ +#if 1 + if ((current_thread() != THREAD_NULL) && + (current_thread()->task->map->pmap != kernel_pmap) && + (current_thread()->task->map->pmap != PMAP_NULL)) { + code = vm_fault(current_thread()->task->map, + trunc_page(offset), access, FALSE, + FALSE, (void (*)()) 0); + }else{ + /* + * Invalid kernel address (below VM_MIN_KERNEL_ADDRESS) + */ + return FALSE; + } +#else + if (kttd_debug) + printf("==========Would've tried to map in user area 0x%x\n", + trunc_page(offset)); + return FALSE; +#endif /* 0 */ + } + + return (code == KERN_SUCCESS); +} + +/* + * See if we modified the kernel text and if so flush the caches. + * This routine is never called with a range that crosses a page + * boundary. + */ +void kttd_flush_cache(vm_offset_t offset, vm_size_t length) +{ + /* 386 doesn't need this */ + return; +} + +/* + * Insert a breakpoint into memory. + */ +boolean_t kttd_insert_breakpoint(vm_address_t address, + ttd_saved_inst *saved_inst) +{ + /* + * Saved old memory data: + */ + *saved_inst = *(unsigned char *)address; + + /* + * Put in a Breakpoint: + */ + *(unsigned char *)address = I386_BREAKPOINT; + + return TRUE; +} + +/* + * Remove breakpoint from memory. + */ +boolean_t kttd_remove_breakpoint(vm_address_t address, + ttd_saved_inst saved_inst) +{ + /* + * replace it: + */ + *(unsigned char *)address = (saved_inst & 0xff); + + return TRUE; +} + +/* + * Set single stepping mode. Assumes that program counter is set + * to the location where single stepping is to begin. The 386 is + * an easy single stepping machine, ie. built into the processor. + */ +boolean_t kttd_set_machine_single_step(void) +{ + /* Turn on Single Stepping */ + kttd_regs.efl |= EFL_TF; + + return TRUE; +} + +/* + * Clear single stepping mode. + */ +boolean_t kttd_clear_machine_single_step(void) +{ + /* Turn off the trace flag */ + kttd_regs.efl &= ~EFL_TF; + + return TRUE; +} + + +/* + * kttd_type_to_ttdtrap: + * + * Fills in the task and thread info structures with the reason + * for entering the Teledebugger (bp, single step, pg flt, etc.) + * + */ +void kttd_type_to_ttdtrap(int type) +{ + /* XXX Fill this in sometime for i386 */ +} + +/* + * kttd_trap: + * + * This routine is called from the trap or interrupt handler when a + * breakpoint instruction is encountered or a single step operation + * completes. The argument is a pointer to a machine dependent + * saved_state structure that was built on the interrupt or kernel stack. + * + */ +boolean_t kttd_trap(int type, int code, struct i386_saved_state *regs) +{ + int s; + + if (kttd_debug) + printf("kttd_TRAP, before splhigh()\n"); + + /* + * TTD isn't supported by the driver. + * + * Try to switch off to kdb if it is resident. + * Otherwise just hang (this might be panic). + * + * Check to make sure that TTD is supported. + * (Both by the machine's driver's, and bootp if using ether). + */ + if (!kttd_supported()) { + kttd_enabled = FALSE; + return FALSE; + } + + s = splhigh(); + + /* + * We are already in TTD! + */ + if (++kttd_active > MAX_KTTD_ACTIVE) { + printf("kttd_trap: RE-ENTERED!!!\n"); + } + + if (kttd_debug) + printf("kttd_TRAP, after splhigh()\n"); + + /* Should switch to kttd's own stack here. */ + + kttd_regs = *regs; + + if ((regs->cs & 0x3) == KERNEL_RING) { + /* + * Kernel mode - esp and ss not saved + */ + kttd_regs.uesp = (int)®s->uesp; /* kernel stack pointer */ + kttd_regs.ss = KERNEL_DS; + } + + /* + * If this was not entered via an interrupt (type != -1) + * then we've entered via a bpt, single, etc. and must + * set the globals. + * + * Setup the kttd globals for entry.... + */ + if (type != -1) { + kttd_current_request = NULL; + kttd_current_length = 0; + kttd_current_kmsg = NULL; + kttd_run_status = FULL_STOP; + }else{ + /* + * We know that we can only get here if we did a kttd_intr + * since it's the way that we are called with type -1 (via + * the trampoline), so we don't have to worry about entering + * from Cntl-Alt-D like the mips does. + */ + /* + * Perform sanity check! + */ + if ((kttd_current_request == NULL) || + (kttd_current_length == 0) || + (kttd_current_kmsg == NULL) || + (kttd_run_status != ONE_STOP)) { + + printf("kttd_trap: INSANITY!!!\n"); + } + } + + kttd_task_trap(type, code, (regs->cs & 0x3) != 0); + + regs->eip = kttd_regs.eip; + regs->efl = kttd_regs.efl; + regs->eax = kttd_regs.eax; + regs->ecx = kttd_regs.ecx; + regs->edx = kttd_regs.edx; + regs->ebx = kttd_regs.ebx; + if ((regs->cs & 0x3) != KERNEL_RING) { + /* + * user mode - saved esp and ss valid + */ + regs->uesp = kttd_regs.uesp; /* user stack pointer */ + regs->ss = kttd_regs.ss & 0xffff; /* user stack segment */ + } + regs->ebp = kttd_regs.ebp; + regs->esi = kttd_regs.esi; + regs->edi = kttd_regs.edi; + regs->es = kttd_regs.es & 0xffff; + regs->cs = kttd_regs.cs & 0xffff; + regs->ds = kttd_regs.ds & 0xffff; + regs->fs = kttd_regs.fs & 0xffff; + regs->gs = kttd_regs.gs & 0xffff; + + if (--kttd_active < MIN_KTTD_ACTIVE) + printf("ttd_trap: kttd_active < 0\n"); + + if (kttd_debug) { + printf("Leaving kttd_trap, kttd_active = %d\n", kttd_active); + } + + /* + * Only reset this if we entered kttd_trap via an async trampoline. + */ + if (type == -1) { + if (kttd_run_status == RUNNING) + printf("kttd_trap: $$$$$ run_status already RUNNING! $$$$$\n"); + kttd_run_status = RUNNING; + } + + /* Is this right? XXX */ + kttd_run_status = RUNNING; + + (void) splx(s); + + /* + * Return true, that yes we handled the trap. + */ + return TRUE; +} + +/* + * Enter KTTD through a network packet trap. + * We show the registers as of the network interrupt + * instead of those at its call to KDB. + */ +struct int_regs { + int edi; + int esi; + int ebp; + int ebx; + struct i386_interrupt_state *is; +}; + +void +kttd_netentry(struct int_regs *int_regs) +{ + struct i386_interrupt_state *is = int_regs->is; + int s; + + if (kttd_debug) + printf("kttd_NETENTRY before slphigh()\n"); + + s = splhigh(); + + if (kttd_debug) + printf("kttd_NETENTRY after slphigh()\n"); + + if ((is->cs & 0x3) != KERNEL_RING) { + /* + * Interrupted from User Space + */ + kttd_regs.uesp = ((int *)(is+1))[0]; + kttd_regs.ss = ((int *)(is+1))[1]; + } + else { + /* + * Interrupted from Kernel Space + */ + kttd_regs.ss = KERNEL_DS; + kttd_regs.uesp= (int)(is+1); + } + kttd_regs.efl = is->efl; + kttd_regs.cs = is->cs; + kttd_regs.eip = is->eip; + kttd_regs.eax = is->eax; + kttd_regs.ecx = is->ecx; + kttd_regs.edx = is->edx; + kttd_regs.ebx = int_regs->ebx; + kttd_regs.ebp = int_regs->ebp; + kttd_regs.esi = int_regs->esi; + kttd_regs.edi = int_regs->edi; + kttd_regs.ds = is->ds; + kttd_regs.es = is->es; + kttd_regs.fs = is->fs; + kttd_regs.gs = is->gs; + + kttd_active++; + kttd_task_trap(-1, 0, (kttd_regs.cs & 0x3) != 0); + kttd_active--; + + if ((kttd_regs.cs & 0x3) != KERNEL_RING) { + ((int *)(is+1))[0] = kttd_regs.uesp; + ((int *)(is+1))[1] = kttd_regs.ss & 0xffff; + } + is->efl = kttd_regs.efl; + is->cs = kttd_regs.cs & 0xffff; + is->eip = kttd_regs.eip; + is->eax = kttd_regs.eax; + is->ecx = kttd_regs.ecx; + is->edx = kttd_regs.edx; + int_regs->ebx = kttd_regs.ebx; + int_regs->ebp = kttd_regs.ebp; + int_regs->esi = kttd_regs.esi; + int_regs->edi = kttd_regs.edi; + is->ds = kttd_regs.ds & 0xffff; + is->es = kttd_regs.es & 0xffff; + is->fs = kttd_regs.fs & 0xffff; + is->gs = kttd_regs.gs & 0xffff; + + if (kttd_run_status == RUNNING) + printf("kttd_netentry: %%%%% run_status already RUNNING! %%%%%\n"); + kttd_run_status = RUNNING; + + (void) splx(s); +} + +#endif /* MACH_TTD */ diff --git a/i386/i386/kttd_machdep.h b/i386/i386/kttd_machdep.h new file mode 100644 index 0000000..8ac7de1 --- /dev/null +++ b/i386/i386/kttd_machdep.h @@ -0,0 +1,59 @@ +/* + * Mach Operating System + * Copyright (c) 1992 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. + */ + +#ifndef _KTTD_MACHDEP_H_ +#define _KTTD_MACHDEP_H_ + +#define MAX_KTTD_ACTIVE 2 +#define MIN_KTTD_ACTIVE 0 + +/* + * Register state for gdb + */ +struct i386_gdb_register_state { + int eax; + int ecx; + int edx; + int ebx; + int esp; /* 4 */ + int ebp; /* 5 */ + int esi; + int edi; + int eip; /* 8 */ + int efl; /* 9 */ + int cs; + int ss; + int ds; + int es; + int fs; + int gs; +}; + +typedef struct i386_gdb_register_state ttd_machine_state; + +typedef unsigned long ttd_saved_inst; + +#endif /* _KTTD_MACHDEP_H_ */ diff --git a/i386/i386/ldt.c b/i386/i386/ldt.c new file mode 100644 index 0000000..5db3642 --- /dev/null +++ b/i386/i386/ldt.c @@ -0,0 +1,117 @@ +/* + * 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. + */ +/* + * "Local" descriptor table. At the moment, all tasks use the + * same LDT. + */ +#include +#include +#include + +#include +#include + +#include "vm_param.h" +#include "seg.h" +#include "gdt.h" +#include "ldt.h" +#include "locore.h" +#include "mp_desc.h" +#include "msr.h" + +#ifdef MACH_PV_DESCRIPTORS +/* It is actually defined in xen_boothdr.S */ +extern +#endif /* MACH_PV_DESCRIPTORS */ +struct real_descriptor ldt[LDTSZ]; + +#if defined(__x86_64__) && ! defined(USER32) +#define USER_SEGMENT_SIZEBITS SZ_64 +#else +#define USER_SEGMENT_SIZEBITS SZ_32 +#endif + +void +ldt_fill(struct real_descriptor *myldt, struct real_descriptor *mygdt) +{ +#ifdef MACH_PV_DESCRIPTORS +#ifdef MACH_PV_PAGETABLES + pmap_set_page_readwrite(myldt); +#endif /* MACH_PV_PAGETABLES */ +#else /* MACH_PV_DESCRIPTORS */ + /* Initialize the master LDT descriptor in the GDT. */ + _fill_gdt_sys_descriptor(mygdt, KERNEL_LDT, + kvtolin(myldt), (LDTSZ * sizeof(struct real_descriptor))-1, + ACC_PL_K|ACC_LDT, 0); +#endif /* MACH_PV_DESCRIPTORS */ + + /* Initialize the syscall entry point */ +#if defined(__x86_64__) && ! defined(USER32) + if (!CPU_HAS_FEATURE(CPU_FEATURE_SEP)) + panic("syscall support is missing on 64 bit"); + /* Enable 64-bit syscalls */ + wrmsr(MSR_REG_EFER, rdmsr(MSR_REG_EFER) | MSR_EFER_SCE); + wrmsr(MSR_REG_LSTAR, (vm_offset_t)syscall64); + wrmsr(MSR_REG_STAR, ((((long)USER_CS - 16) << 16) | (long)KERNEL_CS) << 32); + wrmsr(MSR_REG_FMASK, EFL_IF | EFL_IOPL_USER); +#else /* defined(__x86_64__) && ! defined(USER32) */ + fill_ldt_gate(myldt, USER_SCALL, + (vm_offset_t)&syscall, KERNEL_CS, + ACC_PL_U|ACC_CALL_GATE, 0); +#endif /* defined(__x86_64__) && ! defined(USER32) */ + + /* Initialize the 32bit LDT descriptors. */ + fill_ldt_descriptor(myldt, USER_CS, + VM_MIN_USER_ADDRESS, + VM_MAX_USER_ADDRESS-VM_MIN_USER_ADDRESS-4096, + /* XXX LINEAR_... */ + ACC_PL_U|ACC_CODE_R, USER_SEGMENT_SIZEBITS); + fill_ldt_descriptor(myldt, USER_DS, + VM_MIN_USER_ADDRESS, + VM_MAX_USER_ADDRESS-VM_MIN_USER_ADDRESS-4096, + ACC_PL_U|ACC_DATA_W, USER_SEGMENT_SIZEBITS); + + /* Activate the LDT. */ +#ifdef MACH_PV_DESCRIPTORS + hyp_set_ldt(myldt, LDTSZ); +#else /* MACH_PV_DESCRIPTORS */ + lldt(KERNEL_LDT); +#endif /* MACH_PV_DESCRIPTORS */ +} + +void +ldt_init(void) +{ + ldt_fill(ldt, gdt); +} + +#if NCPUS > 1 +void +ap_ldt_init(int cpu) +{ + ldt_fill(mp_desc_table[cpu]->ldt, mp_gdt[cpu]); +} +#endif diff --git a/i386/i386/ldt.h b/i386/i386/ldt.h new file mode 100644 index 0000000..51867f4 --- /dev/null +++ b/i386/i386/ldt.h @@ -0,0 +1,77 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990 Carnegie Mellon University + * Copyright (c) 1991 IBM Corporation + * Copyright (c) 1994 The University of Utah and + * the Computer Systems Laboratory (CSL). + * 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, + * and that the name IBM not be used in advertising or publicity + * pertaining to distribution of the software without specific, written + * prior permission. + * + * CARNEGIE MELLON, IBM, AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON, IBM, AND CSL DISCLAIM 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. + */ + +/* + * This file describes the standard LDT provided by default + * to all user-level Mach tasks. + */ +#ifndef _I386_LDT_ +#define _I386_LDT_ + +#include "seg.h" + +/* + * User descriptors for Mach - 32-bit flat address space + */ +#define USER_SCALL 0x07 /* system call gate */ +#if defined(__x86_64__) && ! defined(USER32) +/* Call gate needs two entries */ + +/* The sysret instruction puts some constraints on the user segment indexes */ +#define USER_CS 0x1f /* user code segment */ +#define USER_DS 0x17 /* user data segment */ +#else +#define USER_CS 0x17 /* user code segment */ +#define USER_DS 0x1f /* user data segment */ +#endif + +#define LDTSZ 4 + + +#ifndef __ASSEMBLER__ + +extern struct real_descriptor ldt[LDTSZ]; + +/* Fill a 32bit segment descriptor in the LDT. */ +#define fill_ldt_descriptor(_ldt, selector, base, limit, access, sizebits) \ + fill_descriptor(&_ldt[sel_idx(selector)], base, limit, access, sizebits) + +#define fill_ldt_gate(_ldt, selector, offset, dest_selector, access, word_count) \ + fill_gate((struct real_gate*)&_ldt[sel_idx(selector)], \ + offset, dest_selector, access, word_count) + +void ldt_init(void); +void ap_ldt_init(int cpu); + +#endif /* !__ASSEMBLER__ */ + +#endif /* _I386_LDT_ */ diff --git a/i386/i386/lock.h b/i386/i386/lock.h new file mode 100644 index 0000000..b325ae0 --- /dev/null +++ b/i386/i386/lock.h @@ -0,0 +1,132 @@ +/* + * 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. + */ +/* + * Machine-dependent simple locks for the i386. + */ +#ifndef _I386_LOCK_H_ +#define _I386_LOCK_H_ + +#if NCPUS > 1 +#include + +/* + * All of the locking routines are built from calls on + * a locked-exchange operation. Values of the lock are + * 0 for unlocked, 1 for locked. + */ + +#ifdef __GNUC__ + +/* + * The code here depends on the GNU C compiler. + */ + +#define _simple_lock_xchg_(lock, new_val) \ +({ natural_t _old_val_; \ + asm volatile("xchg %0, %2" \ + : "=r" (_old_val_) \ + : "0" ((natural_t)(new_val)), "m" (*(lock)) : "memory" \ + ); \ + _old_val_; \ + }) + +#define simple_lock_init(l) \ + ((l)->lock_data = 0) + +#define SIMPLE_LOCK_INITIALIZER(l) \ + {.lock_data = 0} + +#define _simple_lock(l) \ + ({ \ + while(_simple_lock_xchg_(l, 1)) \ + while (*(volatile natural_t *)&(l)->lock_data) \ + cpu_pause(); \ + 0; \ + }) + +#define _simple_unlock(l) \ + (_simple_lock_xchg_(l, 0)) + +#define _simple_lock_try(l) \ + (!_simple_lock_xchg_(l, 1)) + +/* + * General bit-lock routines. + */ +#define bit_lock(bit, l) \ + ({ \ + asm volatile(" jmp 1f \n\ + 0: btl %0, %1 \n\ + jb 0b \n\ + 1: lock \n\ + btsl %0, %1 \n\ + jb 0b" \ + : \ + : "r" ((int)(bit)), "m" (*(volatile int *)(l)) : "memory"); \ + 0; \ + }) + +#define bit_unlock(bit, l) \ + ({ \ + asm volatile(" lock \n\ + btrl %0, %1" \ + : \ + : "r" ((int)(bit)), "m" (*(volatile int *)(l)) : "memory"); \ + 0; \ + }) + +/* + * Set or clear individual bits in a long word. + * The locked access is needed only to lock access + * to the word, not to individual bits. + */ +#define i_bit_set(bit, l) \ + ({ \ + asm volatile(" lock \n\ + btsl %0, %1" \ + : \ + : "r" ((int)(bit)), "m" (*(l)) ); \ + 0; \ + }) + +#define i_bit_clear(bit, l) \ + ({ \ + asm volatile(" lock \n\ + btrl %0, %1" \ + : \ + : "r" ((int)(bit)), "m" (*(l)) ); \ + 0; \ + }) + +#endif /* __GNUC__ */ + +extern void simple_lock_pause(void); + +#endif /* NCPUS > 1 */ + + + +#endif /* _I386_LOCK_H_ */ diff --git a/i386/i386/locore.S b/i386/i386/locore.S new file mode 100644 index 0000000..9d0513a --- /dev/null +++ b/i386/i386/locore.S @@ -0,0 +1,1603 @@ +/* + * Mach Operating System + * Copyright (c) 1993,1992,1991,1990 Carnegie Mellon University + * Copyright (c) 1991 IBM Corporation + * 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, + * and that the nema IBM not be used in advertising or publicity + * pertaining to distribution of the software without specific, written + * prior permission. + * + * CARNEGIE MELLON AND IBM ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON AND IBM DISCLAIM 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PUSH_REGS_ISR \ + pushl %ecx ;\ + pushl %edx + +#define PUSH_AREGS_ISR \ + pushl %eax ;\ + PUSH_REGS_ISR + + +#define POP_REGS_ISR \ + popl %edx ;\ + popl %ecx + +#define POP_AREGS_ISR \ + POP_REGS_ISR ;\ + popl %eax + +/* + * Note that we have to load the kernel segment registers even if this + * is a trap from the kernel, because the kernel uses user segment + * registers for copyin/copyout. + * (XXX Would it be smarter just to use fs or gs for that?) + */ +#define PUSH_SEGMENTS \ + pushl %ds ;\ + pushl %es ;\ + pushl %fs ;\ + pushl %gs + +#define POP_SEGMENTS \ + popl %gs ;\ + popl %fs ;\ + popl %es ;\ + popl %ds + +#define PUSH_SEGMENTS_ISR \ + pushl %ds ;\ + pushl %es ;\ + pushl %fs ;\ + pushl %gs + +#define POP_SEGMENTS_ISR \ + popl %gs ;\ + popl %fs ;\ + popl %es ;\ + popl %ds + +#define SET_KERNEL_SEGMENTS(reg) \ + mov %ss,reg /* switch to kernel segments */ ;\ + mov reg,%ds /* (same as kernel stack segment) */ ;\ + mov reg,%es ;\ + mov reg,%fs ;\ + mov $(PERCPU_DS),reg ;\ + mov reg,%gs + +/* + * Fault recovery. + */ +#define RECOVER_TABLE_START \ + .text 2 ;\ +DATA(recover_table) ;\ + .text + +#define RECOVER(addr) \ + .text 2 ;\ + .long 9f ;\ + .long addr ;\ + .text ;\ +9: + +#define RECOVER_TABLE_END \ + .text 2 ;\ + .globl EXT(recover_table_end) ;\ +LEXT(recover_table_end) ;\ + .text + +/* + * Retry table for certain successful faults. + */ +#define RETRY_TABLE_START \ + .text 3 ;\ +DATA(retry_table) ;\ + .text + +#define RETRY(addr) \ + .text 3 ;\ + .long 9f ;\ + .long addr ;\ + .text ;\ +9: + +#define RETRY_TABLE_END \ + .text 3 ;\ + .globl EXT(retry_table_end) ;\ +LEXT(retry_table_end) ;\ + .text + +/* + * Allocate recovery and retry tables. + */ + RECOVER_TABLE_START + RETRY_TABLE_START + +/* + * Timing routines. + */ +#if STAT_TIME + +#define TIME_TRAP_UENTRY +#define TIME_TRAP_SENTRY +#define TIME_TRAP_UEXIT +#define TIME_INT_ENTRY +#define TIME_INT_EXIT + +#else /* microsecond timing */ + +/* + * Microsecond timing. + * Assumes a free-running microsecond counter. + * no TIMER_MAX check needed. + */ + +/* + * There is only one current time-stamp per CPU, since only + * the time-stamp in the current timer is used. + * To save time, we allocate the current time-stamps here. + */ + .comm EXT(current_tstamp), 4*NCPUS + +/* + * Update time on user trap entry. + * 11 instructions (including cli on entry) + * Assumes CPU number in %edx. + * Uses %eax, %ebx, %ecx. + */ +#define TIME_TRAP_UENTRY \ + pushf /* Save flags */ ;\ + cli /* block interrupts */ ;\ + movl VA_ETC,%ebx /* get timer value */ ;\ + movl CX(EXT(current_tstamp),%edx),%ecx /* get old time stamp */;\ + movl %ebx,CX(EXT(current_tstamp),%edx) /* set new time stamp */;\ + subl %ecx,%ebx /* elapsed = new-old */ ;\ + movl CX(EXT(current_timer),%edx),%ecx /* get current timer */ ;\ + addl %ebx,LOW_BITS(%ecx) /* add to low bits */ ;\ + jns 0f /* if overflow, */ ;\ + call timer_normalize /* normalize timer */ ;\ +0: addl $(TH_SYSTEM_TIMER-TH_USER_TIMER),%ecx ;\ + /* switch to sys timer */;\ + movl %ecx,CX(EXT(current_timer),%edx) /* make it current */ ;\ + popf /* allow interrupts */ + +/* + * Update time on system call entry. + * 11 instructions (including cli on entry) + * Assumes CPU number in %edx. + * Uses %ebx, %ecx. + * Same as TIME_TRAP_UENTRY, but preserves %eax. + */ +#define TIME_TRAP_SENTRY \ + pushf /* Save flags */ ;\ + cli /* block interrupts */ ;\ + movl VA_ETC,%ebx /* get timer value */ ;\ + movl CX(EXT(current_tstamp),%edx),%ecx /* get old time stamp */;\ + movl %ebx,CX(EXT(current_tstamp),%edx) /* set new time stamp */;\ + subl %ecx,%ebx /* elapsed = new-old */ ;\ + movl CX(EXT(current_timer),%edx),%ecx /* get current timer */ ;\ + addl %ebx,LOW_BITS(%ecx) /* add to low bits */ ;\ + jns 0f /* if overflow, */ ;\ + pushl %eax /* save %eax */ ;\ + call timer_normalize /* normalize timer */ ;\ + popl %eax /* restore %eax */ ;\ +0: addl $(TH_SYSTEM_TIMER-TH_USER_TIMER),%ecx ;\ + /* switch to sys timer */;\ + movl %ecx,CX(EXT(current_timer),%edx) /* make it current */ ;\ + popf /* allow interrupts */ + +/* + * update time on user trap exit. + * 10 instructions. + * Assumes CPU number in %edx. + * Uses %ebx, %ecx. + */ +#define TIME_TRAP_UEXIT \ + cli /* block interrupts */ ;\ + movl VA_ETC,%ebx /* get timer */ ;\ + movl CX(EXT(current_tstamp),%edx),%ecx /* get old time stamp */;\ + movl %ebx,CX(EXT(current_tstamp),%edx) /* set new time stamp */;\ + subl %ecx,%ebx /* elapsed = new-old */ ;\ + movl CX(EXT(current_timer),%edx),%ecx /* get current timer */ ;\ + addl %ebx,LOW_BITS(%ecx) /* add to low bits */ ;\ + jns 0f /* if overflow, */ ;\ + call timer_normalize /* normalize timer */ ;\ +0: addl $(TH_USER_TIMER-TH_SYSTEM_TIMER),%ecx ;\ + /* switch to user timer */;\ + movl %ecx,CX(EXT(current_timer),%edx) /* make it current */ + +/* + * update time on interrupt entry. + * 9 instructions. + * Assumes CPU number in %edx. + * Leaves old timer in %ebx. + * Uses %ecx. + */ +#define TIME_INT_ENTRY \ + movl VA_ETC,%ecx /* get timer */ ;\ + movl CX(EXT(current_tstamp),%edx),%ebx /* get old time stamp */;\ + movl %ecx,CX(EXT(current_tstamp),%edx) /* set new time stamp */;\ + subl %ebx,%ecx /* elapsed = new-old */ ;\ + movl CX(EXT(current_timer),%edx),%ebx /* get current timer */ ;\ + addl %ecx,LOW_BITS(%ebx) /* add to low bits */ ;\ + leal CX(0,%edx),%ecx /* timer is 16 bytes */ ;\ + lea CX(EXT(kernel_timer),%edx),%ecx /* get interrupt timer*/;\ + movl %ecx,CX(EXT(current_timer),%edx) /* set timer */ + +/* + * update time on interrupt exit. + * 11 instructions + * Assumes CPU number in %edx, old timer in %ebx. + * Uses %eax, %ecx. + */ +#define TIME_INT_EXIT \ + movl VA_ETC,%eax /* get timer */ ;\ + movl CX(EXT(current_tstamp),%edx),%ecx /* get old time stamp */;\ + movl %eax,CX(EXT(current_tstamp),%edx) /* set new time stamp */;\ + subl %ecx,%eax /* elapsed = new-old */ ;\ + movl CX(EXT(current_timer),%edx),%ecx /* get current timer */ ;\ + addl %eax,LOW_BITS(%ecx) /* add to low bits */ ;\ + jns 0f /* if overflow, */ ;\ + call timer_normalize /* normalize timer */ ;\ +0: testb $0x80,LOW_BITS+3(%ebx) /* old timer overflow? */;\ + jz 0f /* if overflow, */ ;\ + movl %ebx,%ecx /* get old timer */ ;\ + call timer_normalize /* normalize timer */ ;\ +0: movl %ebx,CX(EXT(current_timer),%edx) /* set timer */ + + +/* + * Normalize timer in ecx. + * Preserves edx; clobbers eax. + */ + .align 2 +timer_high_unit: + .long TIMER_HIGH_UNIT /* div has no immediate opnd */ + +timer_normalize: + pushl %edx /* save register */ + xorl %edx,%edx /* clear divisor high */ + movl LOW_BITS(%ecx),%eax /* get divisor low */ + divl timer_high_unit,%eax /* quotient in eax */ + /* remainder in edx */ + addl %eax,HIGH_BITS_CHECK(%ecx) /* add high_inc to check */ + movl %edx,LOW_BITS(%ecx) /* remainder to low_bits */ + addl %eax,HIGH_BITS(%ecx) /* add high_inc to high bits */ + popl %edx /* restore register */ + ret + +/* + * Switch to a new timer. + */ +ENTRY(timer_switch) + CPU_NUMBER(%edx) /* get this CPU */ + movl VA_ETC,%ecx /* get timer */ + movl CX(EXT(current_tstamp),%edx),%eax /* get old time stamp */ + movl %ecx,CX(EXT(current_tstamp),%edx) /* set new time stamp */ + subl %ecx,%eax /* elapsed = new - old */ + movl CX(EXT(current_timer),%edx),%ecx /* get current timer */ + addl %eax,LOW_BITS(%ecx) /* add to low bits */ + jns 0f /* if overflow, */ + call timer_normalize /* normalize timer */ +0: + movl S_ARG0,%ecx /* get new timer */ + movl %ecx,CX(EXT(current_timer),%edx) /* set timer */ + ret + +/* + * Initialize the first timer for a CPU. + */ +ENTRY(start_timer) + CPU_NUMBER(%edx) /* get this CPU */ + movl VA_ETC,%ecx /* get timer */ + movl %ecx,CX(EXT(current_tstamp),%edx) /* set initial time stamp */ + movl S_ARG0,%ecx /* get timer */ + movl %ecx,CX(EXT(current_timer),%edx) /* set initial timer */ + ret + +#endif /* accurate timing */ + +/* */ + +/* + * Trap/interrupt entry points. + * + * All traps must create the following save area on the kernel stack: + * + * gs + * fs + * es + * ds + * edi + * esi + * ebp + * cr2 if page fault - otherwise unused + * ebx + * edx + * ecx + * eax + * trap number + * error code + * eip + * cs + * eflags + * user esp - if from user + * user ss - if from user + * es - if from V86 thread + * ds - if from V86 thread + * fs - if from V86 thread + * gs - if from V86 thread + * + */ + +/* + * General protection or segment-not-present fault. + * Check for a GP/NP fault in the kernel_return + * sequence; if there, report it as a GP/NP fault on the user's instruction. + * + * esp-> 0: trap code (NP or GP) + * 4: segment number in error + * 8 eip + * 12 cs + * 16 eflags + * 20 old registers (trap is from kernel) + */ +ENTRY(t_gen_prot) + pushl $(T_GENERAL_PROTECTION) /* indicate fault type */ + jmp trap_check_kernel_exit /* check for kernel exit sequence */ + +ENTRY(t_segnp) + pushl $(T_SEGMENT_NOT_PRESENT) + /* indicate fault type */ + +trap_check_kernel_exit: + testl $(EFL_VM),16(%esp) /* is trap from V86 mode? */ + jnz EXT(alltraps) /* isn`t kernel trap if so */ + /* Note: handling KERNEL_RING value by hand */ + testl $2,12(%esp) /* is trap from kernel mode? */ + jnz EXT(alltraps) /* if so: */ + /* check for the kernel exit sequence */ + cmpl $_kret_iret,8(%esp) /* on IRET? */ + je fault_iret + cmpl $_kret_popl_ds,8(%esp) /* popping DS? */ + je fault_popl_ds + cmpl $_kret_popl_es,8(%esp) /* popping ES? */ + je fault_popl_es + cmpl $_kret_popl_fs,8(%esp) /* popping FS? */ + je fault_popl_fs + cmpl $_kret_popl_gs,8(%esp) /* popping GS? */ + je fault_popl_gs +take_fault: /* if none of the above: */ + jmp EXT(alltraps) /* treat as normal trap. */ + +/* + * GP/NP fault on IRET: CS or SS is in error. + * All registers contain the user's values. + * + * on SP is + * 0 trap number + * 4 errcode + * 8 eip + * 12 cs --> trapno + * 16 efl --> errcode + * 20 user eip + * 24 user cs + * 28 user eflags + * 32 user esp + * 36 user ss + */ +fault_iret: + movl %eax,8(%esp) /* save eax (we don`t need saved eip) */ + popl %eax /* get trap number */ + movl %eax,12-4(%esp) /* put in user trap number */ + popl %eax /* get error code */ + movl %eax,16-8(%esp) /* put in user errcode */ + popl %eax /* restore eax */ + jmp EXT(alltraps) /* take fault */ + +/* + * Fault restoring a segment register. The user's registers are still + * saved on the stack. The offending segment register has not been + * popped. + */ +fault_popl_ds: + popl %eax /* get trap number */ + popl %edx /* get error code */ + addl $12,%esp /* pop stack to user regs */ + jmp push_es /* (DS on top of stack) */ +fault_popl_es: + popl %eax /* get trap number */ + popl %edx /* get error code */ + addl $12,%esp /* pop stack to user regs */ + jmp push_fs /* (ES on top of stack) */ +fault_popl_fs: + popl %eax /* get trap number */ + popl %edx /* get error code */ + addl $12,%esp /* pop stack to user regs */ + jmp push_gs /* (FS on top of stack) */ +fault_popl_gs: + popl %eax /* get trap number */ + popl %edx /* get error code */ + addl $12,%esp /* pop stack to user regs */ + jmp push_segregs /* (GS on top of stack) */ + +push_es: + pushl %es /* restore es, */ +push_fs: + pushl %fs /* restore fs, */ +push_gs: + pushl %gs /* restore gs. */ +push_segregs: + movl %eax,R_TRAPNO(%esp) /* set trap number */ + movl %edx,R_ERR(%esp) /* set error code */ + jmp trap_set_segs /* take trap */ + +/* + * Debug trap. Check for single-stepping across system call into + * kernel. If this is the case, taking the debug trap has turned + * off single-stepping - save the flags register with the trace + * bit set. + */ +ENTRY(t_debug) + testl $(EFL_VM),8(%esp) /* is trap from V86 mode? */ + jnz 0f /* isn`t kernel trap if so */ + /* Note: handling KERNEL_RING value by hand */ + testl $2,4(%esp) /* is trap from kernel mode? */ + jnz 0f /* if so: */ + cmpl $syscall_entry,(%esp) /* system call entry? */ + jne 0f /* if so: */ + /* flags are sitting where syscall */ + /* wants them */ + addl $8,%esp /* remove eip/cs */ + jmp syscall_entry_2 /* continue system call entry */ + +0: pushl $0 /* otherwise: */ + pushl $(T_DEBUG) /* handle as normal */ + jmp EXT(alltraps) /* debug fault */ + +/* + * Page fault traps save cr2. + */ +ENTRY(t_page_fault) + pushl $(T_PAGE_FAULT) /* mark a page fault trap */ + pusha /* save the general registers */ +#ifdef MACH_PV_PAGETABLES + movl %ss:hyp_shared_info+CR2,%eax +#else /* MACH_PV_PAGETABLES */ + movl %cr2,%eax /* get the faulting address */ +#endif /* MACH_PV_PAGETABLES */ + movl %eax,R_CR2-R_EDI(%esp) /* save in esp save slot */ + jmp trap_push_segs /* continue fault */ + +/* + * All 'exceptions' enter here with: + * esp-> trap number + * error code + * old eip + * old cs + * old eflags + * old esp if trapped from user + * old ss if trapped from user + */ +ENTRY(alltraps) + pusha /* save the general registers */ +trap_push_segs: + PUSH_SEGMENTS /* and the segment registers */ + SET_KERNEL_SEGMENTS(%ax) /* switch to kernel data segment */ +trap_set_segs: + cld /* clear direction flag */ + testl $(EFL_VM),R_EFLAGS(%esp) /* in V86 mode? */ + jnz trap_from_user /* user mode trap if so */ + /* Note: handling KERNEL_RING value by hand */ + testb $2,R_CS(%esp) /* user mode trap? */ + jz trap_from_kernel /* kernel trap if not */ +trap_from_user: + + CPU_NUMBER(%edx) + TIME_TRAP_UENTRY + + movl CX(EXT(kernel_stack),%edx),%ebx + xchgl %ebx,%esp /* switch to kernel stack */ + /* user regs pointer already set */ +_take_trap: + pushl %ebx /* pass register save area to trap */ + call EXT(user_trap) /* call user trap routine */ + movl 4(%esp),%esp /* switch back to PCB stack */ + + orl %eax,%eax /* emulated syscall? */ + jz _return_from_trap /* no, just return */ + movl R_EAX(%ebx),%eax /* yes, get syscall number */ + jmp syscall_entry_3 /* and emulate it */ + +/* + * Return from trap or system call, checking for ASTs. + * On PCB stack. + */ + +_return_from_trap: + CPU_NUMBER(%edx) + cmpl $0,CX(EXT(need_ast),%edx) + jz _return_to_user /* if we need an AST: */ + + movl CX(EXT(kernel_stack),%edx),%esp + /* switch to kernel stack */ + call EXT(i386_astintr) /* take the AST */ + popl %esp /* switch back to PCB stack */ + jmp _return_from_trap /* and check again (rare) */ + /* ASTs after this point will */ + /* have to wait */ + +_return_to_user: + TIME_TRAP_UEXIT + +/* + * Return from kernel mode to interrupted thread. + */ + +_return_from_kernel: +_kret_popl_gs: + popl %gs /* restore segment registers */ +_kret_popl_fs: + popl %fs +_kret_popl_es: + popl %es +_kret_popl_ds: + popl %ds + popa /* restore general registers */ + addl $8,%esp /* discard trap number and error code */ +_kret_iret: + iret /* return from interrupt */ + + +/* + * Trap from kernel mode. No need to switch stacks. + */ +trap_from_kernel: +#if MACH_KDB || MACH_TTD + movl %esp,%ebx /* save current stack */ + movl %esp,%edx /* on an interrupt stack? */ + + CPU_NUMBER(%ecx) + and $(~(INTSTACK_SIZE-1)),%edx + cmpl CX(EXT(int_stack_base),%ecx),%edx + je 1f /* OK if so */ + + movl %ecx,%edx + cmpl CX(EXT(kernel_stack),%edx),%esp + /* already on kernel stack? */ + ja 0f + cmpl MY(ACTIVE_STACK),%esp + ja 1f /* switch if not */ +0: + movl CX(EXT(kernel_stack),%edx),%esp +1: + pushl %ebx /* save old stack */ + pushl %ebx /* pass as parameter */ + call EXT(kernel_trap) /* to kernel trap routine */ + addl $4,%esp /* pop parameter */ + popl %esp /* return to old stack */ +#else /* MACH_KDB || MACH_TTD */ + + pushl %esp /* pass parameter */ + call EXT(kernel_trap) /* to kernel trap routine */ + addl $4,%esp /* pop parameter */ +#endif /* MACH_KDB || MACH_TTD */ + + jmp _return_from_kernel + + +/* + * Called as a function, makes the current thread + * return from the kernel as if from an exception. + */ + +ENTRY(thread_exception_return) +ENTRY(thread_bootstrap_return) + movl %esp,%ecx /* get kernel stack */ + or $(KERNEL_STACK_SIZE-1),%ecx + movl -3-IKS_SIZE(%ecx),%esp /* switch back to PCB stack */ + jmp _return_from_trap + +/* + * Called as a function, makes the current thread + * return from the kernel as if from a syscall. + * Takes the syscall's return code as an argument. + */ + +ENTRY(thread_syscall_return) + movl S_ARG0,%eax /* get return value */ + movl %esp,%ecx /* get kernel stack */ + or $(KERNEL_STACK_SIZE-1),%ecx + movl -3-IKS_SIZE(%ecx),%esp /* switch back to PCB stack */ + movl %eax,R_EAX(%esp) /* save return value */ + jmp _return_from_trap + +ENTRY(call_continuation) + movl S_ARG0,%eax /* get continuation */ + movl %esp,%ecx /* get kernel stack */ + or $(KERNEL_STACK_SIZE-1),%ecx + addl $(-3-IKS_SIZE),%ecx + movl %ecx,%esp /* pop the stack */ + xorl %ebp,%ebp /* zero frame pointer */ + pushl $0 /* Dummy return address */ + jmp *%eax /* goto continuation */ + + +/* IOAPIC has 24 interrupts, put spurious in the same array */ + +#define INTERRUPT(n) \ + .data 2 ;\ + .long 0f ;\ + .text ;\ + P2ALIGN(TEXT_ALIGN) ;\ +0: ;\ + pushl %eax ;\ + movl $(n),%eax ;\ + jmp EXT(all_intrs) + + .data 2 +DATA(int_entry_table) + .text +/* Legacy APIC interrupts or PIC interrupts */ +INTERRUPT(0) +INTERRUPT(1) +INTERRUPT(2) +INTERRUPT(3) +INTERRUPT(4) +INTERRUPT(5) +INTERRUPT(6) +INTERRUPT(7) +INTERRUPT(8) +INTERRUPT(9) +INTERRUPT(10) +INTERRUPT(11) +INTERRUPT(12) +INTERRUPT(13) +INTERRUPT(14) +INTERRUPT(15) +#ifdef APIC +/* APIC PCI interrupts PIRQ A-H */ +INTERRUPT(16) +INTERRUPT(17) +INTERRUPT(18) +INTERRUPT(19) +INTERRUPT(20) +INTERRUPT(21) +INTERRUPT(22) +INTERRUPT(23) +/* Possibly 8 more GSIs */ +INTERRUPT(24) +INTERRUPT(25) +INTERRUPT(26) +INTERRUPT(27) +INTERRUPT(28) +INTERRUPT(29) +INTERRUPT(30) +INTERRUPT(31) +/* ... APIC IOAPIC #2 */ +INTERRUPT(32) +INTERRUPT(33) +INTERRUPT(34) +INTERRUPT(35) +INTERRUPT(36) +INTERRUPT(37) +INTERRUPT(38) +INTERRUPT(39) +INTERRUPT(40) +INTERRUPT(41) +INTERRUPT(42) +INTERRUPT(43) +INTERRUPT(44) +INTERRUPT(45) +INTERRUPT(46) +INTERRUPT(47) +INTERRUPT(48) +INTERRUPT(49) +INTERRUPT(50) +INTERRUPT(51) +INTERRUPT(52) +INTERRUPT(53) +INTERRUPT(54) +INTERRUPT(55) +/* Possibly 8 more GSIs */ +INTERRUPT(56) +INTERRUPT(57) +INTERRUPT(58) +INTERRUPT(59) +INTERRUPT(60) +INTERRUPT(61) +INTERRUPT(62) +INTERRUPT(63) +#endif +#if NCPUS > 1 +INTERRUPT(CALL_AST_CHECK) +INTERRUPT(CALL_PMAP_UPDATE) +#endif +#ifdef APIC +/* Spurious interrupt, set irq number to vect number */ +INTERRUPT(255) +#endif + +/* XXX handle NMI - at least print a warning like Linux does. */ + +/* + * All interrupts enter here. + * old %eax on stack; interrupt number in %eax. + */ +ENTRY(all_intrs) + PUSH_REGS_ISR /* save registers */ + cld /* clear direction flag */ + + CPU_NUMBER_NO_GS(%ecx) + movl %esp,%edx /* on an interrupt stack? */ + and $(~(INTSTACK_SIZE-1)),%edx + cmpl %ss:CX(EXT(int_stack_base),%ecx),%edx + je int_from_intstack /* if not: */ + + PUSH_SEGMENTS_ISR /* save segment registers */ + SET_KERNEL_SEGMENTS(%dx) /* switch to kernel segments */ + + CPU_NUMBER(%edx) + + movl CX(EXT(int_stack_top),%edx),%ecx + + xchgl %ecx,%esp /* switch to interrupt stack */ + +#if STAT_TIME + pushl %ecx /* save pointer to old stack */ +#else + pushl %ebx /* save %ebx - out of the way */ + /* so stack looks the same */ + pushl %ecx /* save pointer to old stack */ + TIME_INT_ENTRY /* do timing */ +#endif + +#ifdef MACH_LDEBUG + incl CX(EXT(in_interrupt),%edx) +#endif + + call EXT(interrupt) /* call generic interrupt routine */ + .globl EXT(return_to_iret) /* ( label for kdb_kintr and hardclock */ +LEXT(return_to_iret) /* to find the return from calling interrupt) */ + + CPU_NUMBER(%edx) +#ifdef MACH_LDEBUG + decl CX(EXT(in_interrupt),%edx) +#endif + +#if STAT_TIME +#else + TIME_INT_EXIT /* do timing */ + movl 4(%esp),%ebx /* restore the extra reg we saved */ +#endif + + popl %esp /* switch back to old stack */ + + testl $(EFL_VM),I_EFL(%esp) /* if in V86 */ + jnz 0f /* or */ + /* Note: handling KERNEL_RING value by hand */ + testb $2,I_CS(%esp) /* user mode, */ + jz 1f /* check for ASTs */ +0: + cmpl $0,CX(EXT(need_ast),%edx) + jnz ast_from_interrupt /* take it if so */ +1: + POP_SEGMENTS_ISR /* restore segment regs */ + POP_AREGS_ISR /* restore registers */ + + iret /* return to caller */ + +int_from_intstack: + CPU_NUMBER_NO_GS(%edx) + cmpl CX(EXT(int_stack_base),%edx),%esp /* seemingly looping? */ + jb stack_overflowed /* if not: */ + call EXT(interrupt) /* call interrupt routine */ +_return_to_iret_i: /* ( label for kdb_kintr) */ + /* must have been on kernel segs */ + POP_AREGS_ISR /* restore registers */ + /* no ASTs */ + + iret + +stack_overflowed: + ud2 + +/* + * Take an AST from an interrupt. + * On PCB stack. + * sp-> gs -> edx + * fs -> ecx + * es -> eax + * ds -> trapno + * edx -> code + * ecx + * eax + * eip + * cs + * efl + * esp + * ss + */ +ast_from_interrupt: + POP_SEGMENTS_ISR /* restore all registers ... */ + POP_AREGS_ISR + pushl $0 /* zero code */ + pushl $0 /* zero trap number */ + pusha /* save general registers */ + PUSH_SEGMENTS_ISR /* save segment registers */ + SET_KERNEL_SEGMENTS(%dx) /* switch to kernel segments */ + CPU_NUMBER(%edx) + TIME_TRAP_UENTRY + + movl CX(EXT(kernel_stack),%edx),%esp + /* switch to kernel stack */ + call EXT(i386_astintr) /* take the AST */ + popl %esp /* back to PCB stack */ + jmp _return_from_trap /* return */ + +#if MACH_KDB +/* + * kdb_kintr: enter kdb from keyboard interrupt. + * Chase down the stack frames until we find one whose return + * address is the interrupt handler. At that point, we have: + * + * frame-> saved %ebp + * return address in interrupt handler + * #ifndef MACH_XEN + * 1st parameter iunit + * 2nd parameter saved SPL + * 3th parameter return address + * 4th parameter registers + * saved SPL + * saved IRQ + * #endif + * return address == return_to_iret_i + * saved %edx + * saved %ecx + * saved %eax + * saved %eip + * saved %cs + * saved %efl + * + * OR: + * frame-> saved %ebp + * return address in interrupt handler + * #ifndef MACH_XEN + * iunit + * saved SPL + * irq + * #endif + * return address == return_to_iret + * pointer to save area on old stack + * [ saved %ebx, if accurate timing ] + * + * old stack: saved %gs + * saved %fs + * saved %es + * saved %ds + * saved %edx + * saved %ecx + * saved %eax + * saved %eip + * saved %cs + * saved %efl + * + * Call kdb, passing it that register save area. + */ + +#ifdef MACH_XEN +#define RET_OFFSET 8 +#else /* MACH_XEN */ +#define RET_OFFSET 32 +#endif /* MACH_XEN */ + +ENTRY(kdb_kintr) + movl %ebp,%eax /* save caller`s frame pointer */ + movl $EXT(return_to_iret),%ecx /* interrupt return address 1 */ + movl $_return_to_iret_i,%edx /* interrupt return address 2 */ + +0: cmpl RET_OFFSET(%eax),%ecx /* does this frame return to */ + /* interrupt handler (1)? */ + je 1f + cmpl RET_OFFSET(%eax),%edx /* interrupt handler (2)? */ + je 2f /* if not: */ + movl (%eax),%eax /* try next frame */ + testl %eax,%eax + jnz 0b + ud2 /* oops, didn't find frame, fix me :/ */ + +1: movl $kdb_from_iret,RET_OFFSET(%eax) + ret /* returns to kernel/user stack */ + +2: movl $kdb_from_iret_i,RET_OFFSET(%eax) + /* returns to interrupt stack */ + ret + +/* + * On return from keyboard interrupt, we will execute + * kdb_from_iret_i + * if returning to an interrupt on the interrupt stack + * kdb_from_iret + * if returning to an interrupt on the user or kernel stack + */ +kdb_from_iret: + /* save regs in known locations */ +#if STAT_TIME + pushl %ebx /* caller`s %ebx is in reg */ +#else + movl 4(%esp),%eax /* get caller`s %ebx */ + pushl %eax /* push on stack */ +#endif + pushl %ebp + pushl %esi + pushl %edi + pushl %esp /* pass regs */ + call EXT(kdb_kentry) /* to kdb */ + addl $4,%esp /* pop parameters */ + popl %edi /* restore registers */ + popl %esi + popl %ebp +#if STAT_TIME + popl %ebx +#else + popl %eax + movl %eax,4(%esp) +#endif + jmp EXT(return_to_iret) /* normal interrupt return */ + +kdb_from_iret_i: /* on interrupt stack */ + pop %edx /* restore saved registers */ + pop %ecx + pop %eax + pushl $0 /* zero error code */ + pushl $0 /* zero trap number */ + pusha /* save general registers */ + PUSH_SEGMENTS /* save segment registers */ + pushl %esp /* pass regs, */ + pushl $0 /* code, */ + pushl $-1 /* type to kdb */ + call EXT(kdb_trap) + addl $12,%esp /* remove parameters */ + POP_SEGMENTS /* restore segment registers */ + popa /* restore general registers */ + addl $8,%esp + iret + +#endif /* MACH_KDB */ + +#if MACH_TTD +/* + * Same code as that above for the keyboard entry into kdb. + */ +ENTRY(kttd_intr) + movl %ebp,%eax /* save caller`s frame pointer */ + movl $EXT(return_to_iret),%ecx /* interrupt return address 1 */ + movl $_return_to_iret_i,%edx /* interrupt return address 2 */ + +0: cmpl 16(%eax),%ecx /* does this frame return to */ + /* interrupt handler (1)? */ + je 1f + cmpl 16(%eax),%edx /* interrupt handler (2)? */ + je 2f /* if not: */ + movl (%eax),%eax /* try next frame */ + jmp 0b + +1: movl $ttd_from_iret,16(%eax) /* returns to kernel/user stack */ + ret + +2: movl $ttd_from_iret_i,16(%eax) + /* returns to interrupt stack */ + ret + +/* + * On return from keyboard interrupt, we will execute + * ttd_from_iret_i + * if returning to an interrupt on the interrupt stack + * ttd_from_iret + * if returning to an interrupt on the user or kernel stack + */ +ttd_from_iret: + /* save regs in known locations */ +#if STAT_TIME + pushl %ebx /* caller`s %ebx is in reg */ +#else + movl 4(%esp),%eax /* get caller`s %ebx */ + pushl %eax /* push on stack */ +#endif + pushl %ebp + pushl %esi + pushl %edi + pushl %esp /* pass regs */ + call _kttd_netentry /* to kdb */ + addl $4,%esp /* pop parameters */ + popl %edi /* restore registers */ + popl %esi + popl %ebp +#if STAT_TIME + popl %ebx +#else + popl %eax + movl %eax,4(%esp) +#endif + jmp EXT(return_to_iret) /* normal interrupt return */ + +ttd_from_iret_i: /* on interrupt stack */ + pop %edx /* restore saved registers */ + pop %ecx + pop %eax + pushl $0 /* zero error code */ + pushl $0 /* zero trap number */ + pusha /* save general registers */ + PUSH_SEGMENTS_ISR /* save segment registers */ + pushl %esp /* pass regs, */ + pushl $0 /* code, */ + pushl $-1 /* type to kdb */ + call _kttd_trap + addl $12,%esp /* remove parameters */ + POP_SEGMENTS_ISR /* restore segment registers */ + popa /* restore general registers */ + addl $8,%esp + iret + +#endif /* MACH_TTD */ + +/* + * System call enters through a call gate. Flags are not saved - + * we must shuffle stack to look like trap save area. + * + * esp-> old eip + * old cs + * old esp + * old ss + * + * eax contains system call number. + */ +ENTRY(syscall) +syscall_entry: + pushf /* save flags as soon as possible */ +syscall_entry_2: + cld /* clear direction flag */ + + pushl %eax /* save system call number */ + pushl $0 /* clear trap number slot */ + + pusha /* save the general registers */ + PUSH_SEGMENTS /* and the segment registers */ + SET_KERNEL_SEGMENTS(%dx) /* switch to kernel data segment */ + +/* + * Shuffle eflags,eip,cs into proper places + */ + + movl R_EIP(%esp),%ebx /* eflags are in EIP slot */ + movl R_CS(%esp),%ecx /* eip is in CS slot */ + movl R_EFLAGS(%esp),%edx /* cs is in EFLAGS slot */ + movl %ecx,R_EIP(%esp) /* fix eip */ + movl %edx,R_CS(%esp) /* fix cs */ + movl %ebx,R_EFLAGS(%esp) /* fix eflags */ + + CPU_NUMBER(%edx) + TIME_TRAP_SENTRY + + movl CX(EXT(kernel_stack),%edx),%ebx + /* get current kernel stack */ + xchgl %ebx,%esp /* switch stacks - %ebx points to */ + /* user registers. */ + /* user regs pointer already set */ + +/* + * Check for MACH or emulated system call + */ +syscall_entry_3: + movl MY(ACTIVE_THREAD),%edx + /* point to current thread */ + movl TH_TASK(%edx),%edx /* point to task */ + movl TASK_EMUL(%edx),%edx /* get emulation vector */ + orl %edx,%edx /* if none, */ + je syscall_native /* do native system call */ + movl %eax,%ecx /* copy system call number */ + subl DISP_MIN(%edx),%ecx /* get displacement into syscall */ + /* vector table */ + jl syscall_native /* too low - native system call */ + cmpl DISP_COUNT(%edx),%ecx /* check range */ + jnl syscall_native /* too high - native system call */ + movl DISP_VECTOR(%edx,%ecx,4),%edx + /* get the emulation vector */ + orl %edx,%edx /* emulated system call if not zero */ + jnz syscall_emul + +/* + * Native system call. + */ +syscall_native: + negl %eax /* get system call number */ + jl mach_call_range /* out of range if it was positive */ + cmpl EXT(mach_trap_count),%eax /* check system call table bounds */ + jg mach_call_range /* error if out of range */ +#if 0 /* debug hack to show the syscall number on the screen */ + movb %al,%dl + shrb $4,%dl + orb $0x30,%dl + movb $0x0f,%dh + movw %dx,0xb800a + movb %al,%dl + andb $0xf,%dl + orb $0x30,%dl + movb $0xf,%dh + movw %dx,0xb800c +#endif + shll $4,%eax /* manual indexing of mach_trap_t */ + movl EXT(mach_trap_table)(%eax),%ecx + /* get number of arguments */ + jecxz mach_call_call /* skip argument copy if none */ + + movl R_UESP(%ebx),%esi /* get user stack pointer */ + lea 4(%esi,%ecx,4),%esi /* skip user return address, */ + /* and point past last argument */ + movl $USER_DS,%edx /* use user data segment for accesses */ + mov %dx,%fs + movl %esp,%edx /* save kernel ESP for error recovery */ + +0: subl $4,%esi + RECOVER(mach_call_addr_push) + pushl %fs:(%esi) /* push argument on stack */ + loop 0b /* loop for all arguments */ + +mach_call_call: + +#ifdef DEBUG + testb $0xff,EXT(syscall_trace) + jz 0f + pushl %eax + call EXT(syscall_trace_print) + /* will return with syscallofs still (or again) in eax */ + addl $4,%esp +0: +#endif /* DEBUG */ + + call *EXT(mach_trap_table)+4(%eax) + /* call procedure */ + movl %esp,%ecx /* get kernel stack */ + or $(KERNEL_STACK_SIZE-1),%ecx + movl -3-IKS_SIZE(%ecx),%esp /* switch back to PCB stack */ + movl %eax,R_EAX(%esp) /* save return value */ + jmp _return_from_trap /* return to user */ + +/* + * Address out of range. Change to page fault. + * %esi holds failing address. + */ +mach_call_addr_push: + movl %edx,%esp /* clean parameters from stack */ +mach_call_addr: + movl %esi,R_CR2(%ebx) /* set fault address */ + movl $(T_PAGE_FAULT),R_TRAPNO(%ebx) + /* set page-fault trap */ + movl $(T_PF_USER),R_ERR(%ebx) + /* set error code - read user space */ + jmp _take_trap /* treat as a trap */ + +/* + * System call out of range. Treat as invalid-instruction trap. + * (? general protection?) + */ +mach_call_range: + movl $(T_INVALID_OPCODE),R_TRAPNO(%ebx) + /* set invalid-operation trap */ + movl $0,R_ERR(%ebx) /* clear error code */ + jmp _take_trap /* treat as a trap */ + +/* + * User space emulation of system calls. + * edx - user address to handle syscall + * + * User stack will become: + * uesp-> eflags + * eip + * eax still contains syscall number. + */ +syscall_emul: + movl $USER_DS,%edi /* use user data segment for accesses */ + mov %di,%fs + +/* XXX what about write-protected pages? */ + movl R_UESP(%ebx),%edi /* get user stack pointer */ + subl $8,%edi /* push space for new arguments */ + movl R_EFLAGS(%ebx),%eax /* move flags */ + RECOVER(syscall_addr) + movl %eax,%fs:0(%edi) /* to user stack */ + movl R_EIP(%ebx),%eax /* move eip */ + RECOVER(syscall_addr) + movl %eax,%fs:4(%edi) /* to user stack */ + movl %edi,R_UESP(%ebx) /* set new user stack pointer */ + movl %edx,R_EIP(%ebx) /* change return address to trap */ + movl %ebx,%esp /* back to PCB stack */ + jmp _return_from_trap /* return to user */ + +/* + * Address error - address is in %edi. + */ +syscall_addr: + movl %edi,R_CR2(%ebx) /* set fault address */ + movl $(T_PAGE_FAULT),R_TRAPNO(%ebx) + /* set page-fault trap */ + movl $(T_PF_USER),R_ERR(%ebx) + /* set error code - read user space */ + jmp _take_trap /* treat as a trap */ + + + .data +DATA(cpu_features) +DATA(cpu_features_edx) + .long 0 +DATA(cpu_features_ecx) + .long 0 + .text + +END(syscall) + +/* Discover what kind of cpu we have; return the family number + (3, 4, 5, 6, for 386, 486, 586, 686 respectively). */ +ENTRY(discover_x86_cpu_type) + pushl %ebp /* Save frame pointer */ + movl %esp,%ebp /* Save stack pointer */ + and $~0x3,%esp /* Align stack pointer */ + +#if 0 +/* Seems to hang with kvm linux 4.3.0 */ +#ifdef MACH_HYP +#warning Assuming not Cyrix CPU +#else /* MACH_HYP */ + inb $0xe8,%al /* Enable ID flag for Cyrix CPU ... */ + andb $0x80,%al /* ... in CCR4 reg bit7 */ + outb %al,$0xe8 +#endif /* MACH_HYP */ +#endif + + pushfl /* Fetch flags ... */ + popl %eax /* ... into eax */ + movl %eax,%ecx /* Save original flags for return */ + xorl $(EFL_AC+EFL_ID),%eax /* Attempt to toggle ID and AC bits */ + pushl %eax /* Save flags... */ + popfl /* ... In EFLAGS */ + pushfl /* Fetch flags back ... */ + popl %eax /* ... into eax */ + pushl %ecx /* From ecx... */ + popfl /* ... restore original flags */ + + xorl %ecx,%eax /* See if any bits didn't change */ + testl $EFL_AC,%eax /* Test AC bit */ + jnz 0f /* Skip next bit if AC toggled */ + movl $3,%eax /* Return value is 386 */ + jmp 9f /* And RETURN */ + +0: testl $EFL_ID,%eax /* Test ID bit */ + jnz 0f /* Skip next bit if ID toggled */ + movl $4,%eax /* Return value is 486 */ + jmp 9f /* And RETURN */ + + /* We are a modern enough processor to have the CPUID instruction; + use it to find out what we are. */ +0: movl $1,%eax /* Fetch CPU type info ... */ + cpuid /* ... into eax */ + movl %ecx,cpu_features_ecx /* Keep a copy */ + movl %edx,cpu_features_edx /* Keep a copy */ + shrl $8,%eax /* Slide family bits down */ + andl $15,%eax /* And select them */ + +9: movl %ebp,%esp /* Restore stack pointer */ + popl %ebp /* Restore frame pointer */ + ret /* And return */ + + +/* */ +/* + * Utility routines. + */ + +/* + * Copy from user address space - generic version. + * arg0: user address + * arg1: kernel address + * arg2: byte count + */ +ENTRY(copyin) + pushl %esi + pushl %edi /* save registers */ + + movl 8+S_ARG0,%esi /* get user start address */ + movl 8+S_ARG1,%edi /* get kernel destination address */ + movl 8+S_ARG2,%edx /* get count */ + + movl $USER_DS,%eax /* use user data segment for accesses */ + mov %ax,%ds + + /*cld*/ /* count up: default mode in all GCC code */ + movl %edx,%ecx /* move by longwords first */ + shrl $2,%ecx + RECOVER(copyin_fail) + rep + movsl /* move longwords */ + movl %edx,%ecx /* now move remaining bytes */ + andl $3,%ecx + RECOVER(copyin_fail) + rep + movsb + xorl %eax,%eax /* return 0 for success */ + +copyin_ret: + mov %ss,%di /* restore DS to kernel segment */ + mov %di,%ds + + popl %edi /* restore registers */ + popl %esi + ret /* and return */ + +copyin_fail: + movl $1,%eax /* return 1 for failure */ + jmp copyin_ret /* pop frame and return */ + +/* + * Copy from user address space - version for copying messages. + * arg0: user address + * arg1: kernel address + * arg2: byte count - must be a multiple of four + * arg3: kernel byte count + */ +ENTRY(copyinmsg) + pushl %esi + pushl %edi /* save registers */ + + movl 8+S_ARG0,%esi /* get user start address */ + movl 8+S_ARG1,%edi /* get kernel destination address */ + movl 8+S_ARG2,%ecx /* get count */ + movl %ecx,%edx /* save count */ + + movl $USER_DS,%eax /* use user data segment for accesses */ + mov %ax,%ds + + /*cld*/ /* count up: default mode in all GCC code */ + shrl $2,%ecx + RECOVER(copyinmsg_fail) + rep + movsl /* move longwords */ + xorl %eax,%eax /* return 0 for success */ + + movl 8+S_ARG1,%edi + movl %edx,%es:MSGH_MSGH_SIZE(%edi) /* set msgh_size */ + +copyinmsg_ret: + mov %ss,%di /* restore DS to kernel segment */ + mov %di,%ds + + popl %edi /* restore registers */ + popl %esi + ret /* and return */ + +copyinmsg_fail: + movl $1,%eax /* return 1 for failure */ + jmp copyinmsg_ret /* pop frame and return */ + +/* + * Copy to user address space - generic version. + * arg0: kernel address + * arg1: user address + * arg2: byte count + */ +ENTRY(copyout) + pushl %esi + pushl %edi /* save registers */ + + movl 8+S_ARG0,%esi /* get kernel start address */ + movl 8+S_ARG1,%edi /* get user start address */ + movl 8+S_ARG2,%edx /* get count */ + + movl $USER_DS,%eax /* use user data segment for accesses */ + mov %ax,%es + +#if !defined(MACH_HYP) && !PAE + cmpl $3,machine_slot+SUB_TYPE_CPU_TYPE + jbe copyout_retry /* Use slow version on i386 */ +#endif /* !defined(MACH_HYP) && !PAE */ + + /*cld*/ /* count up: always this way in GCC code */ + movl %edx,%ecx /* move by longwords first */ + shrl $2,%ecx + RECOVER(copyout_fail) + rep + movsl + movl %edx,%ecx /* now move remaining bytes */ + andl $3,%ecx + RECOVER(copyout_fail) + rep + movsb /* move */ + xorl %eax,%eax /* return 0 for success */ + +copyout_ret: + mov %ss,%di /* restore ES to kernel segment */ + mov %di,%es + + popl %edi /* restore registers */ + popl %esi + ret /* and return */ + +copyout_fail: + movl $1,%eax /* return 1 for failure */ + jmp copyout_ret /* pop frame and return */ + +/* + * Copy to user address space - version for copying messages. + * arg0: kernel address + * arg1: user address + * arg2: byte count - must be a multiple of four + */ +ENTRY(copyoutmsg) + pushl %esi + pushl %edi /* save registers */ + + movl 8+S_ARG0,%esi /* get kernel start address */ + movl 8+S_ARG1,%edi /* get user start address */ + movl 8+S_ARG2,%ecx /* get count */ + + movl $USER_DS,%eax /* use user data segment for accesses */ + mov %ax,%es + +#if !defined(MACH_HYP) && !PAE + movl 8+S_ARG2,%edx /* copyout_retry expects count here */ + cmpl $3,machine_slot+SUB_TYPE_CPU_TYPE + jbe copyout_retry /* Use slow version on i386 */ +#endif /* !defined(MACH_HYP) && !PAE */ + + shrl $2,%ecx /* move by longwords */ + RECOVER(copyoutmsg_fail) + rep + movsl + xorl %eax,%eax /* return 0 for success */ + +copyoutmsg_ret: + mov %ss,%di /* restore ES to kernel segment */ + mov %di,%es + + popl %edi /* restore registers */ + popl %esi + ret /* and return */ + +copyoutmsg_fail: + movl $1,%eax /* return 1 for failure */ + jmp copyoutmsg_ret /* pop frame and return */ + +#if !defined(MACH_HYP) && !PAE +/* + * Check whether user address space is writable + * before writing to it - i386 hardware is broken. + */ +copyout_retry: + movl %cr3,%ecx /* point to page directory */ + movl %edi,%eax /* get page directory bits */ + shrl $(PDESHIFT),%eax /* from user address */ + movl KERNELBASE(%ecx,%eax,PTE_SIZE),%ecx + /* get page directory pointer */ + testl $(PTE_V),%ecx /* present? */ + jz 0f /* if not, fault is OK */ + andl $(PTE_PFN),%ecx /* isolate page frame address */ + movl %edi,%eax /* get page table bits */ + shrl $(PTESHIFT),%eax + andl $(PTEMASK),%eax /* from user address */ + leal KERNELBASE(%ecx,%eax,PTE_SIZE),%ecx + /* point to page table entry */ + movl (%ecx),%eax /* get it */ + testl $(PTE_V),%eax /* present? */ + jz 0f /* if not, fault is OK */ + testl $(PTE_W),%eax /* writable? */ + jnz 0f /* OK if so */ +/* + * Not writable - must fake a fault. Turn off access to the page. + */ + andl $(PTE_INVALID),(%ecx) /* turn off valid bit */ + movl %cr3,%eax /* invalidate TLB */ + movl %eax,%cr3 +0: + +/* + * Copy only what fits on the current destination page. + * Check for write-fault again on the next page. + */ + leal NBPG(%edi),%eax /* point to */ + andl $(-NBPG),%eax /* start of next page */ + subl %edi,%eax /* get number of bytes to that point */ + cmpl %edx,%eax /* bigger than count? */ + jle 1f /* if so, */ + movl %edx,%eax /* use count */ +1: + + /*cld*/ /* count up: always this way in GCC code */ + movl %eax,%ecx /* move by longwords first */ + shrl $2,%ecx + RECOVER(copyout_fail) + RETRY(copyout_retry) + rep + movsl + movl %eax,%ecx /* now move remaining bytes */ + andl $3,%ecx + RECOVER(copyout_fail) + RETRY(copyout_retry) + rep + movsb /* move */ + subl %eax,%edx /* decrement count */ + jg copyout_retry /* restart on next page if not done */ + xorl %eax,%eax /* return 0 for success */ + jmp copyout_ret +#endif /* !defined(MACH_HYP) && !PAE */ + +/* + * int inst_fetch(int eip, int cs); + * + * Fetch instruction byte. Return -1 if invalid address. + */ +ENTRY(inst_fetch) + movl S_ARG1, %eax /* get segment */ + movw %ax,%fs /* into FS */ + movl S_ARG0, %eax /* get offset */ + RETRY(EXT(inst_fetch)) /* re-load FS on retry */ + RECOVER(_inst_fetch_fault) + movzbl %fs:(%eax),%eax /* load instruction byte */ + ret + +_inst_fetch_fault: + movl $-1,%eax /* return -1 if error */ + ret + + +/* + * Done with recovery and retry tables. + */ + RECOVER_TABLE_END + RETRY_TABLE_END + + + +/* + * cpu_shutdown() + * Force reboot + */ +null_idt: + .space 8 * 32 + +null_idtr: + .word 8 * 32 - 1 + .long null_idt + +Entry(cpu_shutdown) + lidt null_idtr /* disable the interrupt handler */ + xor %ecx,%ecx /* generate a divide by zero */ + div %ecx,%eax /* reboot now */ + ret /* this will "never" be executed */ diff --git a/i386/i386/locore.h b/i386/i386/locore.h new file mode 100644 index 0000000..374c8cf --- /dev/null +++ b/i386/i386/locore.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2006, 2011 Free Software Foundation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _MACHINE_LOCORE_H_ +#define _MACHINE_LOCORE_H_ + +#include + +#include + +/* + * Fault recovery in copyin/copyout routines. + */ +struct recovery { + vm_offset_t fault_addr; + vm_offset_t recover_addr; +}; + +extern struct recovery recover_table[]; +extern struct recovery recover_table_end[]; + +/* + * Recovery from Successful fault in copyout does not + * return directly - it retries the pte check, since + * the 386 ignores write protection in kernel mode. + */ +extern struct recovery retry_table[]; +extern struct recovery retry_table_end[]; + + +extern int call_continuation (continuation_t continuation); + +extern int discover_x86_cpu_type (void); + +extern int copyin (const void *userbuf, void *kernelbuf, size_t cn); +extern int copyinmsg (const void *userbuf, void *kernelbuf, size_t cn, size_t kn); +extern int copyout (const void *kernelbuf, void *userbuf, size_t cn); +extern int copyoutmsg (const void *kernelbuf, void *userbuf, size_t cn); + +extern int inst_fetch (int eip, int cs); + +extern void cpu_shutdown (void); + +extern int syscall (void); +extern int syscall64 (void); + +extern unsigned int cpu_features[2]; + +#define CPU_FEATURE_FPU 0 +#define CPU_FEATURE_VME 1 +#define CPU_FEATURE_DE 2 +#define CPU_FEATURE_PSE 3 +#define CPU_FEATURE_TSC 4 +#define CPU_FEATURE_MSR 5 +#define CPU_FEATURE_PAE 6 +#define CPU_FEATURE_MCE 7 +#define CPU_FEATURE_CX8 8 +#define CPU_FEATURE_APIC 9 +#define CPU_FEATURE_SEP 11 +#define CPU_FEATURE_MTRR 12 +#define CPU_FEATURE_PGE 13 +#define CPU_FEATURE_MCA 14 +#define CPU_FEATURE_CMOV 15 +#define CPU_FEATURE_PAT 16 +#define CPU_FEATURE_PSE_36 17 +#define CPU_FEATURE_PSN 18 +#define CPU_FEATURE_CFLSH 19 +#define CPU_FEATURE_DS 21 +#define CPU_FEATURE_ACPI 22 +#define CPU_FEATURE_MMX 23 +#define CPU_FEATURE_FXSR 24 +#define CPU_FEATURE_SSE 25 +#define CPU_FEATURE_SSE2 26 +#define CPU_FEATURE_SS 27 +#define CPU_FEATURE_HTT 28 +#define CPU_FEATURE_TM 29 +#define CPU_FEATURE_PBE 31 +#define CPU_FEATURE_XSAVE (1*32 + 26) + +#define CPU_HAS_FEATURE(feature) (cpu_features[(feature) / 32] & (1 << ((feature) % 32))) + +#endif /* _MACHINE__LOCORE_H_ */ + diff --git a/i386/i386/loose_ends.c b/i386/i386/loose_ends.c new file mode 100644 index 0000000..7e7f943 --- /dev/null +++ b/i386/i386/loose_ends.c @@ -0,0 +1,49 @@ +/* + * 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. + */ +/* + */ + +#include + +#ifndef NDEBUG +#define MACH_ASSERT 1 +#else +#define MACH_ASSERT 0 +#endif /* NDEBUG */ + + /* + * For now we will always go to single user mode, since there is + * no way pass this request through the boot. + */ + +/* Someone with time should write code to set cpuspeed automagically */ +int cpuspeed = 4; +#define DELAY(n) { volatile int N = cpuspeed * (n); while (--N > 0); } +void +delay(int n) +{ + DELAY(n); +} diff --git a/i386/i386/loose_ends.h b/i386/i386/loose_ends.h new file mode 100644 index 0000000..c085527 --- /dev/null +++ b/i386/i386/loose_ends.h @@ -0,0 +1,33 @@ +/* + * Other useful functions? + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Barry deFreese. + */ +/* + * Other useful functions? + * + */ + +#ifndef _LOOSE_ENDS_H_ +#define _LOOSE_ENDS_H_ + +#include + +extern void delay (int n); + +#endif /* _LOOSE_ENDS_H_ */ diff --git a/i386/i386/mach_i386.srv b/i386/i386/mach_i386.srv new file mode 100644 index 0000000..48d16ba --- /dev/null +++ b/i386/i386/mach_i386.srv @@ -0,0 +1,27 @@ +/* + * Copyright (c) 1994 The University of Utah and + * the Computer Systems Laboratory at the University of Utah (CSL). + * All rights reserved. + * + * Permission to use, copy, modify and distribute this software is hereby + * granted provided that (1) source code retains these copyright, permission, + * and disclaimer notices, and (2) redistributions including binaries + * reproduce the notices in supporting documentation, and (3) all advertising + * materials mentioning features or use of this software display the following + * acknowledgement: ``This product includes software developed by the + * Computer Systems Laboratory at the University of Utah.'' + * + * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS + * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF + * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * CSL requests users of this software to return to csl-dist@cs.utah.edu any + * improvements that they make and grant CSL redistribution rights. + * + * Author: Bryan Ford, University of Utah CSL + */ +/* This is a server presentation file. */ + +#define KERNEL_SERVER 1 + +#include diff --git a/i386/i386/mach_param.h b/i386/i386/mach_param.h new file mode 100644 index 0000000..d7d4dee --- /dev/null +++ b/i386/i386/mach_param.h @@ -0,0 +1,31 @@ +/* + * 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. + */ +/* + * Machine-dependent parameters for i386. + */ + +#define HZ (100) + /* clock tick each 10 ms. */ diff --git a/i386/i386/machine_routines.h b/i386/i386/machine_routines.h new file mode 100644 index 0000000..d9dd94b --- /dev/null +++ b/i386/i386/machine_routines.h @@ -0,0 +1,38 @@ +/* + * Mach Operating System + * Copyright (c) 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. + */ + +#ifndef _I386_MACHINE_ROUTINES_H_ +#define _I386_MACHINE_ROUTINES_H_ + +/* + * The i386 has a set of machine-dependent interfaces. + */ +#define MACHINE_SERVER mach_i386_server +#define MACHINE_SERVER_HEADER "i386/i386/mach_i386.server.h" +#define MACHINE_SERVER_ROUTINE mach_i386_server_routine + +#endif /* _I386_MACHINE_ROUTINES_H_ */ + diff --git a/i386/i386/machine_task.c b/i386/i386/machine_task.c new file mode 100644 index 0000000..8bebf36 --- /dev/null +++ b/i386/i386/machine_task.c @@ -0,0 +1,80 @@ +/* Machine specific data for a task on i386. + + Copyright (C) 2002, 2007 Free Software Foundation, Inc. + + Written by Marcus Brinkmann. Glued into GNU Mach by Thomas Schwinge. + + This file is part of GNU Mach. + + GNU Mach is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any later + version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include +#include +#include +#include +#include + +#include + + +/* The cache which holds our IO permission bitmaps. */ +struct kmem_cache machine_task_iopb_cache; + + +/* Initialize the machine task module. The function is called once at + start up by task_init in kern/task.c. */ +void +machine_task_module_init (void) +{ + kmem_cache_init (&machine_task_iopb_cache, "i386_task_iopb", IOPB_BYTES, 0, + NULL, 0); +} + + +/* Initialize the machine specific part of task TASK. */ +void +machine_task_init (task_t task) +{ + task->machine.iopb_size = 0; + task->machine.iopb = 0; + simple_lock_init (&task->machine.iopb_lock); +} + + +/* Destroy the machine specific part of task TASK and release all + associated resources. */ +void +machine_task_terminate (const task_t task) +{ + if (task->machine.iopb) + kmem_cache_free (&machine_task_iopb_cache, + (vm_offset_t) task->machine.iopb); +} + + +/* Try to release as much memory from the machine specific data in + task TASK. */ +void +machine_task_collect (task_t task) +{ + simple_lock (&task->machine.iopb_lock); + if (task->machine.iopb_size == 0 && task->machine.iopb) + { + kmem_cache_free (&machine_task_iopb_cache, + (vm_offset_t) task->machine.iopb); + task->machine.iopb = 0; + } + simple_unlock (&task->machine.iopb_lock); +} diff --git a/i386/i386/machspl.h b/i386/i386/machspl.h new file mode 100644 index 0000000..bbb2675 --- /dev/null +++ b/i386/i386/machspl.h @@ -0,0 +1,29 @@ +/* + * 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. + */ + +/* XXX replaced by... */ +#include + diff --git a/i386/i386/model_dep.h b/i386/i386/model_dep.h new file mode 100644 index 0000000..5369e28 --- /dev/null +++ b/i386/i386/model_dep.h @@ -0,0 +1,68 @@ +/* + * Arch dependent functions + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Barry deFreese. + */ +/* + * Arch dependent functions. + * + */ + +#ifndef _I386AT_MODEL_DEP_H_ +#define _I386AT_MODEL_DEP_H_ + +#include + +/* + * Address to hold AP boot code, held in ASM + */ +extern phys_addr_t apboot_addr; + +/* + * Find devices. The system is alive. + */ +extern void machine_init (void); + +/* Conserve power on processor CPU. */ +extern void machine_idle (int cpu); + +extern void resettodr (void); + +extern void startrtclock (void); + +/* + * Halt a cpu. + */ +extern void halt_cpu (void) __attribute__ ((noreturn)); + +/* + * Halt the system or reboot. + */ +extern void halt_all_cpus (boolean_t reboot) __attribute__ ((noreturn)); + +/* + * Make cpu pause a bit. + */ +extern void machine_relax (void); + +/* + * C boot entrypoint - called by boot_entry in boothdr.S. + */ +extern void c_boot_entry(vm_offset_t bi); + +#endif /* _I386AT_MODEL_DEP_H_ */ diff --git a/i386/i386/mp_desc.c b/i386/i386/mp_desc.c new file mode 100644 index 0000000..61a7607 --- /dev/null +++ b/i386/i386/mp_desc.c @@ -0,0 +1,357 @@ +/* + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + * The i386 needs an interrupt stack to keep the PCB stack from being + * overrun by interrupts. All interrupt stacks MUST lie at lower addresses + * than any thread`s kernel stack. + */ + +/* + * Addresses of bottom and top of interrupt stacks. + */ +vm_offset_t int_stack_top[NCPUS]; +vm_offset_t int_stack_base[NCPUS]; + +/* + * Whether we are currently handling an interrupt. + * To catch code erroneously taking non-irq-safe locks. + */ +#ifdef MACH_LDEBUG +unsigned long in_interrupt[NCPUS]; +#endif + +/* Interrupt stack allocation */ +uint8_t solid_intstack[NCPUS*INTSTACK_SIZE] __aligned(NCPUS*INTSTACK_SIZE); + +void +interrupt_stack_alloc(void) +{ + int i; + + /* + * Set up pointers to the top of the interrupt stack. + */ + + for (i = 0; i < NCPUS; i++) { + int_stack_base[i] = (vm_offset_t) &solid_intstack[i * INTSTACK_SIZE]; + int_stack_top[i] = (vm_offset_t) &solid_intstack[(i + 1) * INTSTACK_SIZE] - 4; + } +} + +#if NCPUS > 1 +/* + * Flag to mark SMP init by BSP complete + */ +int bspdone; + +phys_addr_t apboot_addr; +extern void *apboot, *apbootend; +extern volatile ApicLocalUnit* lapic; + +/* + * Multiprocessor i386/i486 systems use a separate copy of the + * GDT, IDT, LDT, and kernel TSS per processor. The first three + * are separate to avoid lock contention: the i386 uses locked + * memory cycles to access the descriptor tables. The TSS is + * separate since each processor needs its own kernel stack, + * and since using a TSS marks it busy. + */ + +/* + * Descriptor tables. + */ +struct mp_desc_table *mp_desc_table[NCPUS] = { 0 }; + +/* + * Pointer to TSS for access in load_context. + */ +struct task_tss *mp_ktss[NCPUS] = { 0 }; + +/* + * Pointer to GDT to reset the KTSS busy bit. + */ +struct real_descriptor *mp_gdt[NCPUS] = { 0 }; + +/* + * Boot-time tables, for initialization and master processor. + */ +extern struct real_gate idt[IDTSZ]; +extern struct real_descriptor gdt[GDTSZ]; +extern struct real_descriptor ldt[LDTSZ]; + +/* + * Allocate and initialize the per-processor descriptor tables. + */ + +int +mp_desc_init(int mycpu) +{ + struct mp_desc_table *mpt; + vm_offset_t mem; + + if (mycpu == 0) { + /* + * Master CPU uses the tables built at boot time. + * Just set the TSS and GDT pointers. + */ + mp_ktss[mycpu] = (struct task_tss *) &ktss; + mp_gdt[mycpu] = gdt; + return 0; + } + else { + /* + * Allocate tables for other CPUs + */ + if (!init_alloc_aligned(sizeof(struct mp_desc_table), &mem)) + panic("not enough memory for descriptor tables"); + mpt = (struct mp_desc_table *)phystokv(mem); + + mp_desc_table[mycpu] = mpt; + mp_ktss[mycpu] = &mpt->ktss; + mp_gdt[mycpu] = mpt->gdt; + + /* + * Zero the tables + */ + memset(mpt->idt, 0, sizeof(idt)); + memset(mpt->gdt, 0, sizeof(gdt)); + memset(mpt->ldt, 0, sizeof(ldt)); + memset(&mpt->ktss, 0, sizeof(struct task_tss)); + + return mycpu; + } +} + +/* XXX should be adjusted per CPU speed */ +int simple_lock_pause_loop = 100; + +unsigned int simple_lock_pause_count = 0; /* debugging */ + +void +simple_lock_pause(void) +{ + static volatile int dummy; + int i; + + simple_lock_pause_count++; + + /* + * Used in loops that are trying to acquire locks out-of-order. + */ + + for (i = 0; i < simple_lock_pause_loop; i++) + dummy++; /* keep the compiler from optimizing the loop away */ +} + +kern_return_t +cpu_control(int cpu, const int *info, unsigned int count) +{ + printf("cpu_control(%d, %p, %d) not implemented\n", + cpu, info, count); + return KERN_FAILURE; +} + +void +interrupt_processor(int cpu) +{ + smp_pmap_update(apic_get_cpu_apic_id(cpu)); +} + +static void +paging_enable(void) +{ +#ifndef MACH_HYP + /* Turn paging on. + * TODO: Why does setting the WP bit here cause a crash? + */ +#if PAE + set_cr4(get_cr4() | CR4_PAE); +#endif + set_cr0(get_cr0() | CR0_PG /* | CR0_WP */); + set_cr0(get_cr0() & ~(CR0_CD | CR0_NW)); + if (CPU_HAS_FEATURE(CPU_FEATURE_PGE)) + set_cr4(get_cr4() | CR4_PGE); +#endif /* MACH_HYP */ +} + +void +cpu_setup(int cpu) +{ + pmap_make_temporary_mapping(); + printf("AP=(%u) tempmap done\n", cpu); + + paging_enable(); + flush_instr_queue(); + printf("AP=(%u) paging done\n", cpu); + + init_percpu(cpu); + mp_desc_init(cpu); + printf("AP=(%u) mpdesc done\n", cpu); + + ap_gdt_init(cpu); + printf("AP=(%u) gdt done\n", cpu); + + ap_idt_init(cpu); + printf("AP=(%u) idt done\n", cpu); + + ap_int_init(cpu); + printf("AP=(%u) int done\n", cpu); + + ap_ldt_init(cpu); + printf("AP=(%u) ldt done\n", cpu); + + ap_ktss_init(cpu); + printf("AP=(%u) ktss done\n", cpu); + + pmap_remove_temporary_mapping(); + printf("AP=(%u) remove tempmap done\n", cpu); + + pmap_set_page_dir(); + flush_tlb(); + printf("AP=(%u) reset page dir done\n", cpu); + + /* Initialize machine_slot fields with the cpu data */ + machine_slot[cpu].cpu_subtype = CPU_SUBTYPE_AT386; + machine_slot[cpu].cpu_type = machine_slot[0].cpu_type; + + init_fpu(); + lapic_setup(); + lapic_enable(); + cpu_launch_first_thread(THREAD_NULL); +} + +void +cpu_ap_main() +{ + int cpu = cpu_number(); + + do { + cpu_pause(); + } while (bspdone != cpu); + + __sync_synchronize(); + + cpu_setup(cpu); +} + +kern_return_t +cpu_start(int cpu) +{ + int err; + + assert(machine_slot[cpu].running != TRUE); + + uint16_t apic_id = apic_get_cpu_apic_id(cpu); + + printf("Trying to enable: %d at 0x%lx\n", apic_id, apboot_addr); + + err = smp_startup_cpu(apic_id, apboot_addr); + + if (!err) { + printf("Started cpu %d (lapic id %04x)\n", cpu, apic_id); + return KERN_SUCCESS; + } + printf("FATAL: Cannot init AP %d\n", cpu); + for (;;); +} + +void +start_other_cpus(void) +{ + int ncpus = smp_get_numcpus(); + + //Copy cpu initialization assembly routine + memcpy((void*) phystokv(apboot_addr), (void*) &apboot, + (uint32_t)&apbootend - (uint32_t)&apboot); + + unsigned cpu; + + splhigh(); + + /* Disable IOAPIC interrupts (IPIs not affected). + * Clearing this flag is similar to masking all + * IOAPIC interrupts individually. + * + * This is done to prevent IOAPIC interrupts from + * interfering with SMP startup. splhigh() may be enough for BSP, + * but I'm not sure. We cannot control the lapic + * on APs because we don't have execution on them yet. + */ + lapic_disable(); + + bspdone = 0; + for (cpu = 1; cpu < ncpus; cpu++) { + machine_slot[cpu].running = FALSE; + + //Start cpu + printf("Starting AP %d\n", cpu); + cpu_start(cpu); + + bspdone++; + do { + cpu_pause(); + } while (machine_slot[cpu].running == FALSE); + + __sync_synchronize(); + } + printf("BSP: Completed SMP init\n"); + + /* Re-enable IOAPIC interrupts as per setup */ + lapic_enable(); +} +#endif /* NCPUS > 1 */ diff --git a/i386/i386/mp_desc.h b/i386/i386/mp_desc.h new file mode 100644 index 0000000..dc3a7dc --- /dev/null +++ b/i386/i386/mp_desc.h @@ -0,0 +1,98 @@ +/* + * 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. + */ + +#ifndef _I386_MP_DESC_H_ +#define _I386_MP_DESC_H_ + +#include + +#if MULTIPROCESSOR + +/* + * Multiprocessor i386/i486 systems use a separate copy of the + * GDT, IDT, LDT, and kernel TSS per processor. The first three + * are separate to avoid lock contention: the i386 uses locked + * memory cycles to access the descriptor tables. The TSS is + * separate since each processor needs its own kernel stack, + * and since using a TSS marks it busy. + */ + +#include "seg.h" +#include "tss.h" +#include +#include "gdt.h" +#include "ldt.h" + +/* + * The descriptor tables are together in a structure + * allocated one per processor (except for the boot processor). + */ +struct mp_desc_table { + struct real_gate idt[IDTSZ]; /* IDT */ + struct real_descriptor gdt[GDTSZ]; /* GDT */ + struct real_descriptor ldt[LDTSZ]; /* LDT */ + struct task_tss ktss; +}; + +/* + * They are pointed to by a per-processor array. + */ +extern struct mp_desc_table *mp_desc_table[NCPUS]; + +/* + * The kernel TSS gets its own pointer. + */ +extern struct task_tss *mp_ktss[NCPUS]; + +/* + * So does the GDT. + */ +extern struct real_descriptor *mp_gdt[NCPUS]; + +extern uint8_t solid_intstack[]; + +extern int bspdone; + +/* + * Each CPU calls this routine to set up its descriptor tables. + */ +extern int mp_desc_init(int); + + +extern void interrupt_processor(int cpu); + + +#endif /* MULTIPROCESSOR */ + +extern void start_other_cpus(void); + +extern kern_return_t cpu_start(int cpu); + +extern kern_return_t cpu_control(int cpu, const int *info, unsigned int count); + +extern void interrupt_stack_alloc(void); + +#endif /* _I386_MP_DESC_H_ */ diff --git a/i386/i386/msr.h b/i386/i386/msr.h new file mode 100644 index 0000000..8f09b80 --- /dev/null +++ b/i386/i386/msr.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2023 Free Software Foundation + * + * This program is free software ; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation ; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY ; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the program ; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _MACHINE_MSR_H_ +#define _MACHINE_MSR_H_ + +#define MSR_REG_EFER 0xC0000080 +#define MSR_REG_STAR 0xC0000081 +#define MSR_REG_LSTAR 0xC0000082 +#define MSR_REG_CSTAR 0xC0000083 +#define MSR_REG_FMASK 0xC0000084 +#define MSR_REG_FSBASE 0xC0000100 +#define MSR_REG_GSBASE 0xC0000101 + +#define MSR_EFER_SCE 0x00000001 + +#ifndef __ASSEMBLER__ + +static inline void wrmsr(uint32_t regaddr, uint64_t value) +{ + uint32_t low = (uint32_t) value, high = ((uint32_t) (value >> 32)); + asm volatile("wrmsr" + : + : "c" (regaddr), "a" (low), "d" (high) + : "memory" /* wrmsr may cause a read from memory, so + * make the compiler flush any changes */ + ); +} + +static inline uint64_t rdmsr(uint32_t regaddr) +{ + uint32_t low, high; + asm volatile("rdmsr" + : "=a" (low), "=d" (high) + : "c" (regaddr) + ); + return ((uint64_t)high << 32) | low; +} +#endif /* __ASSEMBLER__ */ + +#endif /* _MACHINE_MSR_H_ */ diff --git a/i386/i386/pcb.c b/i386/i386/pcb.c new file mode 100644 index 0000000..e890155 --- /dev/null +++ b/i386/i386/pcb.c @@ -0,0 +1,958 @@ +/* + * 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. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include "vm_param.h" +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include "eflags.h" +#include "gdt.h" +#include "ldt.h" +#include "msr.h" +#include "ktss.h" +#include "pcb.h" + +#include + +#if NCPUS > 1 +#include +#endif + +struct kmem_cache pcb_cache; + +vm_offset_t kernel_stack[NCPUS]; /* top of active_stack */ + +/* + * stack_attach: + * + * Attach a kernel stack to a thread. + */ + +void stack_attach( + thread_t thread, + vm_offset_t stack, + void (*continuation)(thread_t)) +{ + counter(if (++c_stacks_current > c_stacks_max) + c_stacks_max = c_stacks_current); + + thread->kernel_stack = stack; + + /* + * We want to run continuation, giving it as an argument + * the return value from Load_context/Switch_context. + * Thread_continue takes care of the mismatch between + * the argument-passing/return-value conventions. + * This function will not return normally, + * so we don`t have to worry about a return address. + */ + STACK_IKS(stack)->k_eip = (long) Thread_continue; + STACK_IKS(stack)->k_ebx = (long) continuation; + STACK_IKS(stack)->k_esp = (long) STACK_IEL(stack); + STACK_IKS(stack)->k_ebp = (long) 0; + + /* + * Point top of kernel stack to user`s registers. + */ + STACK_IEL(stack)->saved_state = USER_REGS(thread); +} + +/* + * stack_detach: + * + * Detaches a kernel stack from a thread, returning the old stack. + */ + +vm_offset_t stack_detach(thread_t thread) +{ + vm_offset_t stack; + + counter(if (--c_stacks_current < c_stacks_min) + c_stacks_min = c_stacks_current); + + stack = thread->kernel_stack; + thread->kernel_stack = 0; + + return stack; +} + +#if NCPUS > 1 +#define curr_gdt(mycpu) (mp_gdt[mycpu]) +#define curr_ktss(mycpu) (mp_ktss[mycpu]) +#else +#define curr_gdt(mycpu) ((void)(mycpu), gdt) +#define curr_ktss(mycpu) ((void)(mycpu), (struct task_tss *)&ktss) +#endif + +#define gdt_desc_p(mycpu,sel) \ + ((struct real_descriptor *)&curr_gdt(mycpu)[sel_idx(sel)]) + +void switch_ktss(pcb_t pcb) +{ + int mycpu = cpu_number(); + { + vm_offset_t pcb_stack_top; + + /* + * Save a pointer to the top of the "kernel" stack - + * actually the place in the PCB where a trap into + * kernel mode will push the registers. + * The location depends on V8086 mode. If we are + * not in V8086 mode, then a trap into the kernel + * won`t save the v86 segments, so we leave room. + */ + +#if !defined(__x86_64__) || defined(USER32) + pcb_stack_top = (pcb->iss.efl & EFL_VM) + ? (long) (&pcb->iss + 1) + : (long) (&pcb->iss.v86_segs); +#else + pcb_stack_top = (vm_offset_t) (&pcb->iss + 1); +#endif + +#ifdef __x86_64__ + assert((pcb_stack_top & 0xF) == 0); +#endif + +#ifdef MACH_RING1 + /* No IO mask here */ + if (hyp_stack_switch(KERNEL_DS, pcb_stack_top)) + panic("stack_switch"); +#else /* MACH_RING1 */ +#ifdef __x86_64__ + curr_ktss(mycpu)->tss.rsp0 = pcb_stack_top; +#else /* __x86_64__ */ + curr_ktss(mycpu)->tss.esp0 = pcb_stack_top; +#endif /* __x86_64__ */ +#endif /* MACH_RING1 */ + } + + { + user_ldt_t tldt = pcb->ims.ldt; + /* + * Set the thread`s LDT. + */ + if (tldt == 0) { + /* + * Use system LDT. + */ +#ifdef MACH_PV_DESCRIPTORS + hyp_set_ldt(&ldt, LDTSZ); +#else /* MACH_PV_DESCRIPTORS */ + if (get_ldt() != KERNEL_LDT) + set_ldt(KERNEL_LDT); +#endif /* MACH_PV_DESCRIPTORS */ + } + else { + /* + * Thread has its own LDT. + */ +#ifdef MACH_PV_DESCRIPTORS + hyp_set_ldt(tldt->ldt, + (tldt->desc.limit_low|(tldt->desc.limit_high<<16)) / + sizeof(struct real_descriptor)); +#else /* MACH_PV_DESCRIPTORS */ + *gdt_desc_p(mycpu,USER_LDT) = tldt->desc; + set_ldt(USER_LDT); +#endif /* MACH_PV_DESCRIPTORS */ + } + } + +#ifdef MACH_PV_DESCRIPTORS + { + int i; + for (i=0; i < USER_GDT_SLOTS; i++) { + if (memcmp(gdt_desc_p (mycpu, USER_GDT + (i << 3)), + &pcb->ims.user_gdt[i], sizeof pcb->ims.user_gdt[i])) { + union { + struct real_descriptor real_descriptor; + uint64_t descriptor; + } user_gdt; + user_gdt.real_descriptor = pcb->ims.user_gdt[i]; + + if (hyp_do_update_descriptor(kv_to_ma(gdt_desc_p (mycpu, USER_GDT + (i << 3))), + user_gdt.descriptor)) + panic("couldn't set user gdt %d\n",i); + } + } + } +#else /* MACH_PV_DESCRIPTORS */ + + /* Copy in the per-thread GDT slots. No reloading is necessary + because just restoring the segment registers on the way back to + user mode reloads the shadow registers from the in-memory GDT. */ + memcpy (gdt_desc_p (mycpu, USER_GDT), + pcb->ims.user_gdt, sizeof pcb->ims.user_gdt); +#endif /* MACH_PV_DESCRIPTORS */ + +#if defined(__x86_64__) && !defined(USER32) + wrmsr(MSR_REG_FSBASE, pcb->ims.sbs.fsbase); + wrmsr(MSR_REG_GSBASE, pcb->ims.sbs.gsbase); +#endif + + db_load_context(pcb); + + /* + * Load the floating-point context, if necessary. + */ + fpu_load_context(pcb); + +} + +/* If NEW_IOPB is not null, the SIZE denotes the number of bytes in + the new bitmap. Expects iopb_lock to be held. */ +void +update_ktss_iopb (unsigned char *new_iopb, io_port_t size) +{ + struct task_tss *tss = curr_ktss (cpu_number ()); + + if (new_iopb && size > 0) + { + tss->tss.io_bit_map_offset + = offsetof (struct task_tss, barrier) - size; + memcpy (((char *) tss) + tss->tss.io_bit_map_offset, + new_iopb, size); + } + else + tss->tss.io_bit_map_offset = IOPB_INVAL; +} + +/* + * stack_handoff: + * + * Move the current thread's kernel stack to the new thread. + */ + +void stack_handoff( + thread_t old, + thread_t new) +{ + int mycpu = cpu_number(); + vm_offset_t stack; + + /* + * Save FP registers if in use. + */ + fpu_save_context(old); + + /* + * Switch address maps if switching tasks. + */ + { + task_t old_task, new_task; + + if ((old_task = old->task) != (new_task = new->task)) { + PMAP_DEACTIVATE_USER(vm_map_pmap(old_task->map), + old, mycpu); + PMAP_ACTIVATE_USER(vm_map_pmap(new_task->map), + new, mycpu); + + simple_lock (&new_task->machine.iopb_lock); +#if NCPUS>1 +#warning SMP support missing (avoid races with io_perm_modify). +#else + /* This optimization only works on a single processor + machine, where old_task's iopb can not change while + we are switching. */ + if (old_task->machine.iopb || new_task->machine.iopb) +#endif + update_ktss_iopb (new_task->machine.iopb, + new_task->machine.iopb_size); + simple_unlock (&new_task->machine.iopb_lock); + } + } + + /* + * Load the rest of the user state for the new thread + */ + switch_ktss(new->pcb); + + /* + * Switch to new thread + */ + stack = current_stack(); + old->kernel_stack = 0; + new->kernel_stack = stack; + percpu_assign(active_thread, new); + + /* + * Switch exception link to point to new + * user registers. + */ + + STACK_IEL(stack)->saved_state = USER_REGS(new); + +} + +/* + * Switch to the first thread on a CPU. + */ +void load_context(thread_t new) +{ + switch_ktss(new->pcb); + Load_context(new); +} + +/* + * Switch to a new thread. + * Save the old thread`s kernel state or continuation, + * and return it. + */ +thread_t switch_context( + thread_t old, + continuation_t continuation, + thread_t new) +{ + /* + * Save FP registers if in use. + */ + fpu_save_context(old); + + /* + * Switch address maps if switching tasks. + */ + { + task_t old_task, new_task; + int mycpu = cpu_number(); + + if ((old_task = old->task) != (new_task = new->task)) { + PMAP_DEACTIVATE_USER(vm_map_pmap(old_task->map), + old, mycpu); + PMAP_ACTIVATE_USER(vm_map_pmap(new_task->map), + new, mycpu); + + simple_lock (&new_task->machine.iopb_lock); +#if NCPUS>1 +#warning SMP support missing (avoid races with io_perm_modify). +#else + /* This optimization only works on a single processor + machine, where old_task's iopb can not change while + we are switching. */ + if (old_task->machine.iopb || new_task->machine.iopb) +#endif + update_ktss_iopb (new_task->machine.iopb, + new_task->machine.iopb_size); + simple_unlock (&new_task->machine.iopb_lock); + } + } + + /* + * Load the rest of the user state for the new thread + */ + switch_ktss(new->pcb); + return Switch_context(old, continuation, new); +} + +void pcb_module_init(void) +{ + kmem_cache_init(&pcb_cache, "pcb", sizeof(struct pcb), + KERNEL_STACK_ALIGN, NULL, 0); + + fpu_module_init(); +} + +void pcb_init(task_t parent_task, thread_t thread) +{ + pcb_t pcb; + + pcb = (pcb_t) kmem_cache_alloc(&pcb_cache); + if (pcb == 0) + panic("pcb_init"); + + counter(if (++c_threads_current > c_threads_max) + c_threads_max = c_threads_current); + + /* + * We can't let random values leak out to the user. + */ + memset(pcb, 0, sizeof *pcb); + simple_lock_init(&pcb->lock); + + /* + * Guarantee that the bootstrapped thread will be in user + * mode. + */ + pcb->iss.cs = USER_CS; + pcb->iss.ss = USER_DS; +#if !defined(__x86_64__) || defined(USER32) + pcb->iss.ds = USER_DS; + pcb->iss.es = USER_DS; + pcb->iss.fs = USER_DS; + pcb->iss.gs = USER_DS; +#endif + pcb->iss.efl = EFL_USER_SET; + + thread->pcb = pcb; + + /* This is a new thread for the current task, make it inherit our FPU + state. */ + if (current_thread() && parent_task == current_task()) + fpinherit(current_thread(), thread); +} + +void pcb_terminate(thread_t thread) +{ + pcb_t pcb = thread->pcb; + + counter(if (--c_threads_current < c_threads_min) + c_threads_min = c_threads_current); + + if (pcb->ims.ifps != 0) + fp_free(pcb->ims.ifps); + if (pcb->ims.ldt != 0) + user_ldt_free(pcb->ims.ldt); + kmem_cache_free(&pcb_cache, (vm_offset_t) pcb); + thread->pcb = 0; +} + +/* + * pcb_collect: + * + * Attempt to free excess pcb memory. + */ + +void pcb_collect(__attribute__((unused)) const thread_t thread) +{ +} + + +/* + * thread_setstatus: + * + * Set the status of the specified thread. + */ + +kern_return_t thread_setstatus( + thread_t thread, + int flavor, + thread_state_t tstate, + unsigned int count) +{ + switch (flavor) { + case i386_THREAD_STATE: + case i386_REGS_SEGS_STATE: + { + struct i386_thread_state *state; + struct i386_saved_state *saved_state; + + if (count < i386_THREAD_STATE_COUNT) { + return(KERN_INVALID_ARGUMENT); + } + + state = (struct i386_thread_state *) tstate; + + if (flavor == i386_REGS_SEGS_STATE) { + /* + * Code and stack selectors must not be null, + * and must have user protection levels. + * Only the low 16 bits are valid. + */ + state->cs &= 0xffff; + state->ss &= 0xffff; +#if !defined(__x86_64__) || defined(USER32) + state->ds &= 0xffff; + state->es &= 0xffff; + state->fs &= 0xffff; + state->gs &= 0xffff; +#endif + + if (state->cs == 0 || (state->cs & SEL_PL) != SEL_PL_U + || state->ss == 0 || (state->ss & SEL_PL) != SEL_PL_U) + return KERN_INVALID_ARGUMENT; + } + + saved_state = USER_REGS(thread); + + /* + * General registers + */ +#if defined(__x86_64__) && !defined(USER32) + saved_state->r8 = state->r8; + saved_state->r9 = state->r9; + saved_state->r10 = state->r10; + saved_state->r11 = state->r11; + saved_state->r12 = state->r12; + saved_state->r13 = state->r13; + saved_state->r14 = state->r14; + saved_state->r15 = state->r15; + saved_state->edi = state->rdi; + saved_state->esi = state->rsi; + saved_state->ebp = state->rbp; + saved_state->uesp = state->ursp; + saved_state->ebx = state->rbx; + saved_state->edx = state->rdx; + saved_state->ecx = state->rcx; + saved_state->eax = state->rax; + saved_state->eip = state->rip; + saved_state->efl = (state->rfl & ~EFL_USER_CLEAR) + | EFL_USER_SET; +#else + saved_state->edi = state->edi; + saved_state->esi = state->esi; + saved_state->ebp = state->ebp; + saved_state->uesp = state->uesp; + saved_state->ebx = state->ebx; + saved_state->edx = state->edx; + saved_state->ecx = state->ecx; + saved_state->eax = state->eax; + saved_state->eip = state->eip; + saved_state->efl = (state->efl & ~EFL_USER_CLEAR) + | EFL_USER_SET; +#endif /* __x86_64__ && !USER32 */ + +#if !defined(__x86_64__) || defined(USER32) + /* + * Segment registers. Set differently in V8086 mode. + */ + if (saved_state->efl & EFL_VM) { + /* + * Set V8086 mode segment registers. + */ + saved_state->cs = state->cs & 0xffff; + saved_state->ss = state->ss & 0xffff; + saved_state->v86_segs.v86_ds = state->ds & 0xffff; + saved_state->v86_segs.v86_es = state->es & 0xffff; + saved_state->v86_segs.v86_fs = state->fs & 0xffff; + saved_state->v86_segs.v86_gs = state->gs & 0xffff; + + /* + * Zero protected mode segment registers. + */ + saved_state->ds = 0; + saved_state->es = 0; + saved_state->fs = 0; + saved_state->gs = 0; + + if (thread->pcb->ims.v86s.int_table) { + /* + * Hardware assist on. + */ + thread->pcb->ims.v86s.flags = + saved_state->efl & (EFL_TF | EFL_IF); + } + } else +#endif + if (flavor == i386_THREAD_STATE) { + /* + * 386 mode. Set segment registers for flat + * 32-bit address space. + */ + saved_state->cs = USER_CS; + saved_state->ss = USER_DS; +#if !defined(__x86_64__) || defined(USER32) + saved_state->ds = USER_DS; + saved_state->es = USER_DS; + saved_state->fs = USER_DS; + saved_state->gs = USER_DS; +#endif + } + else { + /* + * User setting segment registers. + * Code and stack selectors have already been + * checked. Others will be reset by 'iret' + * if they are not valid. + */ + saved_state->cs = state->cs; + saved_state->ss = state->ss; +#if !defined(__x86_64__) || defined(USER32) + saved_state->ds = state->ds; + saved_state->es = state->es; + saved_state->fs = state->fs; + saved_state->gs = state->gs; +#endif + } + break; + } + + case i386_FLOAT_STATE: { + + if (count < i386_FLOAT_STATE_COUNT) + return(KERN_INVALID_ARGUMENT); + + return fpu_set_state(thread, + (struct i386_float_state *) tstate); + } + + /* + * Temporary - replace by i386_io_map + */ + case i386_ISA_PORT_MAP_STATE: { + //register struct i386_isa_port_map_state *state; + + if (count < i386_ISA_PORT_MAP_STATE_COUNT) + return(KERN_INVALID_ARGUMENT); + +#if 0 + /* + * If the thread has no ktss yet, + * we must allocate one. + */ + + state = (struct i386_isa_port_map_state *) tstate; + tss = thread->pcb->ims.io_tss; + if (tss == 0) { + tss = iopb_create(); + thread->pcb->ims.io_tss = tss; + } + + memcpy(tss->bitmap, + state->pm, + sizeof state->pm); +#endif + break; + } +#if !defined(__x86_64__) || defined(USER32) + case i386_V86_ASSIST_STATE: + { + struct i386_v86_assist_state *state; + vm_offset_t int_table; + int int_count; + + if (count < i386_V86_ASSIST_STATE_COUNT) + return KERN_INVALID_ARGUMENT; + + state = (struct i386_v86_assist_state *) tstate; + int_table = state->int_table; + int_count = state->int_count; + + if (int_table >= VM_MAX_USER_ADDRESS || + int_table + + int_count * sizeof(struct v86_interrupt_table) + > VM_MAX_USER_ADDRESS) + return KERN_INVALID_ARGUMENT; + + thread->pcb->ims.v86s.int_table = int_table; + thread->pcb->ims.v86s.int_count = int_count; + + thread->pcb->ims.v86s.flags = + USER_REGS(thread)->efl & (EFL_TF | EFL_IF); + break; + } +#endif + case i386_DEBUG_STATE: + { + struct i386_debug_state *state; + kern_return_t ret; + + if (count < i386_DEBUG_STATE_COUNT) + return KERN_INVALID_ARGUMENT; + + state = (struct i386_debug_state *) tstate; + ret = db_set_debug_state(thread->pcb, state); + if (ret) + return ret; + break; + } +#if defined(__x86_64__) && !defined(USER32) + case i386_FSGS_BASE_STATE: + { + struct i386_fsgs_base_state *state; + if (count < i386_FSGS_BASE_STATE_COUNT) + return KERN_INVALID_ARGUMENT; + + state = (struct i386_fsgs_base_state *) tstate; + thread->pcb->ims.sbs.fsbase = state->fs_base; + thread->pcb->ims.sbs.gsbase = state->gs_base; + if (thread == current_thread()) { + wrmsr(MSR_REG_FSBASE, state->fs_base); + wrmsr(MSR_REG_GSBASE, state->gs_base); + } + break; + } +#endif + default: + return(KERN_INVALID_ARGUMENT); + } + + return(KERN_SUCCESS); +} + +/* + * thread_getstatus: + * + * Get the status of the specified thread. + */ + +kern_return_t thread_getstatus( + thread_t thread, + int flavor, + thread_state_t tstate, /* pointer to OUT array */ + unsigned int *count) /* IN/OUT */ +{ + switch (flavor) { + case THREAD_STATE_FLAVOR_LIST: +#if !defined(__x86_64__) || defined(USER32) + unsigned int ncount = 4; +#else + unsigned int ncount = 3; +#endif + if (*count < ncount) + return (KERN_INVALID_ARGUMENT); + tstate[0] = i386_THREAD_STATE; + tstate[1] = i386_FLOAT_STATE; + tstate[2] = i386_ISA_PORT_MAP_STATE; +#if !defined(__x86_64__) || defined(USER32) + tstate[3] = i386_V86_ASSIST_STATE; +#endif + *count = ncount; + break; + + case i386_THREAD_STATE: + case i386_REGS_SEGS_STATE: + { + struct i386_thread_state *state; + struct i386_saved_state *saved_state; + + if (*count < i386_THREAD_STATE_COUNT) + return(KERN_INVALID_ARGUMENT); + + state = (struct i386_thread_state *) tstate; + saved_state = USER_REGS(thread); + + /* + * General registers. + */ +#if defined(__x86_64__) && !defined(USER32) + state->r8 = saved_state->r8; + state->r9 = saved_state->r9; + state->r10 = saved_state->r10; + state->r11 = saved_state->r11; + state->r12 = saved_state->r12; + state->r13 = saved_state->r13; + state->r14 = saved_state->r14; + state->r15 = saved_state->r15; + state->rdi = saved_state->edi; + state->rsi = saved_state->esi; + state->rbp = saved_state->ebp; + state->rbx = saved_state->ebx; + state->rdx = saved_state->edx; + state->rcx = saved_state->ecx; + state->rax = saved_state->eax; + state->rip = saved_state->eip; + state->ursp = saved_state->uesp; + state->rfl = saved_state->efl; + state->rsp = 0; /* unused */ +#else + state->edi = saved_state->edi; + state->esi = saved_state->esi; + state->ebp = saved_state->ebp; + state->ebx = saved_state->ebx; + state->edx = saved_state->edx; + state->ecx = saved_state->ecx; + state->eax = saved_state->eax; + state->eip = saved_state->eip; + state->uesp = saved_state->uesp; + state->efl = saved_state->efl; + state->esp = 0; /* unused */ +#endif /* __x86_64__ && !USER32 */ + + state->cs = saved_state->cs; + state->ss = saved_state->ss; +#if !defined(__x86_64__) || defined(USER32) + if (saved_state->efl & EFL_VM) { + /* + * V8086 mode. + */ + state->ds = saved_state->v86_segs.v86_ds & 0xffff; + state->es = saved_state->v86_segs.v86_es & 0xffff; + state->fs = saved_state->v86_segs.v86_fs & 0xffff; + state->gs = saved_state->v86_segs.v86_gs & 0xffff; + + if (thread->pcb->ims.v86s.int_table) { + /* + * Hardware assist on + */ + if ((thread->pcb->ims.v86s.flags & + (EFL_IF|V86_IF_PENDING)) + == 0) + saved_state->efl &= ~EFL_IF; + } + } else { + /* + * 386 mode. + */ + state->ds = saved_state->ds & 0xffff; + state->es = saved_state->es & 0xffff; + state->fs = saved_state->fs & 0xffff; + state->gs = saved_state->gs & 0xffff; + } +#endif + *count = i386_THREAD_STATE_COUNT; + break; + } + + case i386_FLOAT_STATE: { + + if (*count < i386_FLOAT_STATE_COUNT) + return(KERN_INVALID_ARGUMENT); + + *count = i386_FLOAT_STATE_COUNT; + return fpu_get_state(thread, + (struct i386_float_state *)tstate); + } + + /* + * Temporary - replace by i386_io_map + */ + case i386_ISA_PORT_MAP_STATE: { + struct i386_isa_port_map_state *state; + + if (*count < i386_ISA_PORT_MAP_STATE_COUNT) + return(KERN_INVALID_ARGUMENT); + + state = (struct i386_isa_port_map_state *) tstate; + + simple_lock (&thread->task->machine.iopb_lock); + if (thread->task->machine.iopb == 0) + memset (state->pm, 0xff, sizeof state->pm); + else + memcpy(state->pm, + thread->task->machine.iopb, + sizeof state->pm); + simple_unlock (&thread->task->machine.iopb_lock); + + *count = i386_ISA_PORT_MAP_STATE_COUNT; + break; + } +#if !defined(__x86_64__) || defined(USER32) + case i386_V86_ASSIST_STATE: + { + struct i386_v86_assist_state *state; + + if (*count < i386_V86_ASSIST_STATE_COUNT) + return KERN_INVALID_ARGUMENT; + + state = (struct i386_v86_assist_state *) tstate; + state->int_table = thread->pcb->ims.v86s.int_table; + state->int_count = thread->pcb->ims.v86s.int_count; + + *count = i386_V86_ASSIST_STATE_COUNT; + break; + } +#endif + case i386_DEBUG_STATE: + { + struct i386_debug_state *state; + + if (*count < i386_DEBUG_STATE_COUNT) + return KERN_INVALID_ARGUMENT; + + state = (struct i386_debug_state *) tstate; + db_get_debug_state(thread->pcb, state); + + *count = i386_DEBUG_STATE_COUNT; + break; + } +#if defined(__x86_64__) && !defined(USER32) + case i386_FSGS_BASE_STATE: + { + struct i386_fsgs_base_state *state; + if (*count < i386_FSGS_BASE_STATE_COUNT) + return KERN_INVALID_ARGUMENT; + + state = (struct i386_fsgs_base_state *) tstate; + state->fs_base = thread->pcb->ims.sbs.fsbase; + state->gs_base = thread->pcb->ims.sbs.gsbase; + *count = i386_FSGS_BASE_STATE_COUNT; + break; + } +#endif + default: + return(KERN_INVALID_ARGUMENT); + } + + return(KERN_SUCCESS); +} + +/* + * Alter the thread`s state so that a following thread_exception_return + * will make the thread return 'retval' from a syscall. + */ +void +thread_set_syscall_return( + thread_t thread, + kern_return_t retval) +{ + thread->pcb->iss.eax = retval; +} + +/* + * Return preferred address of user stack. + * Always returns low address. If stack grows up, + * the stack grows away from this address; + * if stack grows down, the stack grows towards this + * address. + */ +vm_offset_t +user_stack_low(vm_size_t stack_size) +{ + return (VM_MAX_USER_ADDRESS - stack_size); +} + +/* + * Allocate argument area and set registers for first user thread. + */ +vm_offset_t +set_user_regs(vm_offset_t stack_base, /* low address */ + vm_offset_t stack_size, + const struct exec_info *exec_info, + vm_size_t arg_size) +{ + vm_offset_t arg_addr; + struct i386_saved_state *saved_state; + + assert(P2ALIGNED(stack_size, USER_STACK_ALIGN)); + assert(P2ALIGNED(stack_base, USER_STACK_ALIGN)); + arg_size = P2ROUND(arg_size, USER_STACK_ALIGN); + arg_addr = stack_base + stack_size - arg_size; + + saved_state = USER_REGS(current_thread()); + saved_state->uesp = (rpc_vm_offset_t)arg_addr; + saved_state->eip = exec_info->entry; + + return (arg_addr); +} diff --git a/i386/i386/pcb.h b/i386/i386/pcb.h new file mode 100644 index 0000000..4d48b9f --- /dev/null +++ b/i386/i386/pcb.h @@ -0,0 +1,90 @@ +/* + * + * Copyright (C) 2006 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Barry deFreese. + */ +/* + * + * + */ + +#ifndef _I386_PCB_H_ +#define _I386_PCB_H_ + +#include +#include +#include +#include +#include + +extern void pcb_init (task_t parent_task, thread_t thread); + +extern void pcb_terminate (thread_t thread); + +extern void pcb_collect (thread_t thread); + +extern kern_return_t thread_setstatus ( + thread_t thread, + int flavor, + thread_state_t tstate, + unsigned int count); + +extern kern_return_t thread_getstatus ( + thread_t thread, + int flavor, + thread_state_t tstate, + unsigned int *count); + +extern void thread_set_syscall_return ( + thread_t thread, + kern_return_t retval); + +extern vm_offset_t user_stack_low (vm_size_t stack_size); + +extern vm_offset_t set_user_regs ( + vm_offset_t stack_base, + vm_offset_t stack_size, + const struct exec_info *exec_info, + vm_size_t arg_size); + +extern void load_context (thread_t new); + +extern void stack_attach ( + thread_t thread, + vm_offset_t stack, + void (*continuation)(thread_t)); + +extern vm_offset_t stack_detach (thread_t thread); + +extern void switch_ktss (pcb_t pcb); + +extern void update_ktss_iopb (unsigned char *new_iopb, io_port_t size); + +extern thread_t Load_context (thread_t new); + +extern thread_t Switch_context (thread_t old, continuation_t continuation, thread_t new); + +extern void switch_to_shutdown_context(thread_t thread, + void (*routine)(processor_t), + processor_t processor); + +extern void Thread_continue (void); + +extern void pcb_module_init (void); + +#endif /* _I386_PCB_H_ */ diff --git a/i386/i386/percpu.c b/i386/i386/percpu.c new file mode 100644 index 0000000..c6b728b --- /dev/null +++ b/i386/i386/percpu.c @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2023 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include +#include +#include + +struct percpu percpu_array[NCPUS] = {0}; + +#ifndef MACH_XEN +void init_percpu(int cpu) +{ + int apic_id = apic_get_current_cpu(); + + percpu_array[cpu].self = &percpu_array[cpu]; + percpu_array[cpu].apic_id = apic_id; + percpu_array[cpu].cpu_id = cpu; +} +#endif diff --git a/i386/i386/percpu.h b/i386/i386/percpu.h new file mode 100644 index 0000000..637d2ca --- /dev/null +++ b/i386/i386/percpu.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2023 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef _PERCPU_H_ +#define _PERCPU_H_ + +struct percpu; + +#if NCPUS > 1 + +#define percpu_assign(stm, val) \ + asm("mov %[src], %%gs:%c[offs]" \ + : /* No outputs */ \ + : [src] "r" (val), [offs] "e" (__builtin_offsetof(struct percpu, stm)) \ + : ); + +#define percpu_get(typ, stm) \ +MACRO_BEGIN \ + typ val_; \ + \ + asm("mov %%gs:%c[offs], %[dst]" \ + : [dst] "=r" (val_) \ + : [offs] "e" (__builtin_offsetof(struct percpu, stm)) \ + : ); \ + \ + val_; \ +MACRO_END + +#define percpu_ptr(typ, stm) \ +MACRO_BEGIN \ + typ *ptr_ = (typ *)__builtin_offsetof(struct percpu, stm); \ + \ + asm("add %%gs:0, %[pointer]" \ + : [pointer] "+r" (ptr_) \ + : /* No inputs */ \ + : ); \ + \ + ptr_; \ +MACRO_END + +#else + +#define percpu_assign(stm, val) \ +MACRO_BEGIN \ + percpu_array[0].stm = val; \ +MACRO_END +#define percpu_get(typ, stm) \ + (percpu_array[0].stm) +#define percpu_ptr(typ, stm) \ + (&percpu_array[0].stm) + +#endif + +#include +#include + +struct percpu { + struct percpu *self; + int apic_id; + int cpu_id; + struct processor processor; + thread_t active_thread; + vm_offset_t active_stack; +/* + struct machine_slot machine_slot; + struct mp_desc_table mp_desc_table; + vm_offset_t int_stack_top; + vm_offset_t int_stack_base; + ast_t need_ast; + ipc_kmsg_t ipc_kmsg_cache; + pmap_update_list cpu_update_list; + spl_t saved_ipl; + spl_t curr_ipl; + timer_data_t kernel_timer; + timer_t current_timer; + unsigned long in_interrupt; +*/ +}; + +extern struct percpu percpu_array[NCPUS]; + +void init_percpu(int cpu); + +#endif /* _PERCPU_H_ */ diff --git a/i386/i386/phys.c b/i386/i386/phys.c new file mode 100644 index 0000000..e864489 --- /dev/null +++ b/i386/i386/phys.c @@ -0,0 +1,187 @@ +/* + * 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. + */ + +#include + +#include +#include +#include +#include +#include +#include "vm_param.h" +#include +#include +#include + +#include +#include +#include + +#define INTEL_PTE_W(p) (INTEL_PTE_VALID | INTEL_PTE_WRITE | INTEL_PTE_REF | INTEL_PTE_MOD | pa_to_pte(p)) +#define INTEL_PTE_R(p) (INTEL_PTE_VALID | INTEL_PTE_REF | pa_to_pte(p)) + +/* + * pmap_zero_page zeros the specified (machine independent) page. + */ +void +pmap_zero_page(phys_addr_t p) +{ + assert(p != vm_page_fictitious_addr); + vm_offset_t v; + pmap_mapwindow_t *map; + boolean_t mapped = p >= VM_PAGE_DIRECTMAP_LIMIT; + + if (mapped) + { + map = pmap_get_mapwindow(INTEL_PTE_W(p)); + v = map->vaddr; + } + else + v = phystokv(p); + + memset((void*) v, 0, PAGE_SIZE); + + if (mapped) + pmap_put_mapwindow(map); +} + +/* + * pmap_copy_page copies the specified (machine independent) pages. + */ +void +pmap_copy_page( + phys_addr_t src, + phys_addr_t dst) +{ + vm_offset_t src_addr_v, dst_addr_v; + pmap_mapwindow_t *src_map = NULL; + pmap_mapwindow_t *dst_map; + boolean_t src_mapped = src >= VM_PAGE_DIRECTMAP_LIMIT; + boolean_t dst_mapped = dst >= VM_PAGE_DIRECTMAP_LIMIT; + assert(src != vm_page_fictitious_addr); + assert(dst != vm_page_fictitious_addr); + + if (src_mapped) + { + src_map = pmap_get_mapwindow(INTEL_PTE_R(src)); + src_addr_v = src_map->vaddr; + } + else + src_addr_v = phystokv(src); + + if (dst_mapped) + { + dst_map = pmap_get_mapwindow(INTEL_PTE_W(dst)); + dst_addr_v = dst_map->vaddr; + } + else + dst_addr_v = phystokv(dst); + + memcpy((void *) dst_addr_v, (void *) src_addr_v, PAGE_SIZE); + + if (src_mapped) + pmap_put_mapwindow(src_map); + if (dst_mapped) + pmap_put_mapwindow(dst_map); +} + +/* + * copy_to_phys(src_addr_v, dst_addr_p, count) + * + * Copy virtual memory to physical memory + */ +void +copy_to_phys( + vm_offset_t src_addr_v, + phys_addr_t dst_addr_p, + int count) +{ + vm_offset_t dst_addr_v; + pmap_mapwindow_t *dst_map; + boolean_t mapped = dst_addr_p >= VM_PAGE_DIRECTMAP_LIMIT; + assert(dst_addr_p != vm_page_fictitious_addr); + assert(pa_to_pte(dst_addr_p + count-1) == pa_to_pte(dst_addr_p)); + + if (mapped) + { + dst_map = pmap_get_mapwindow(INTEL_PTE_W(dst_addr_p)); + dst_addr_v = dst_map->vaddr + (dst_addr_p & (INTEL_PGBYTES-1)); + } + else + dst_addr_v = phystokv(dst_addr_p); + + memcpy((void *)dst_addr_v, (void *)src_addr_v, count); + + if (mapped) + pmap_put_mapwindow(dst_map); +} + +/* + * copy_from_phys(src_addr_p, dst_addr_v, count) + * + * Copy physical memory to virtual memory. The virtual memory + * is assumed to be present (e.g. the buffer pool). + */ +void +copy_from_phys( + phys_addr_t src_addr_p, + vm_offset_t dst_addr_v, + int count) +{ + vm_offset_t src_addr_v; + pmap_mapwindow_t *src_map; + boolean_t mapped = src_addr_p >= VM_PAGE_DIRECTMAP_LIMIT; + assert(src_addr_p != vm_page_fictitious_addr); + assert(pa_to_pte(src_addr_p + count-1) == pa_to_pte(src_addr_p)); + + if (mapped) + { + src_map = pmap_get_mapwindow(INTEL_PTE_R(src_addr_p)); + src_addr_v = src_map->vaddr + (src_addr_p & (INTEL_PGBYTES-1)); + } + else + src_addr_v = phystokv(src_addr_p); + + memcpy((void *)dst_addr_v, (void *)src_addr_v, count); + + if (mapped) + pmap_put_mapwindow(src_map); +} + +/* + * kvtophys(addr) + * + * Convert a kernel virtual address to a physical address + */ +phys_addr_t +kvtophys(vm_offset_t addr) +{ + pt_entry_t *pte; + + if ((pte = pmap_pte(kernel_pmap, addr)) == PT_ENTRY_NULL) + return 0; + return pte_to_pa(*pte) | (addr & INTEL_OFFMASK); +} diff --git a/i386/i386/pic.c b/i386/i386/pic.c new file mode 100644 index 0000000..66fbc04 --- /dev/null +++ b/i386/i386/pic.c @@ -0,0 +1,262 @@ +/* + * 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. + */ +/* +Copyright (c) 1988,1989 Prime Computer, Inc. Natick, MA 01760 +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 Prime +Computer, Inc. not be used in advertising or publicity +pertaining to distribution of the software without +specific, written prior permission. + +THIS SOFTWARE IS PROVIDED "AS IS", AND PRIME COMPUTER, +INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS +SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN +NO EVENT SHALL PRIME COMPUTER, INC. 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 OUR OF OR IN CONNECTION +WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/* + * Copyright (C) 1995 Shantanu Goel. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include + +spl_t curr_ipl[NCPUS] = {0}; +int curr_pic_mask; +int spl_init = 0; + +int iunit[NINTR] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + +unsigned short master_icw, master_ocw, slaves_icw, slaves_ocw; + +u_short PICM_ICW1, PICM_OCW1, PICS_ICW1, PICS_OCW1 ; +u_short PICM_ICW2, PICM_OCW2, PICS_ICW2, PICS_OCW2 ; +u_short PICM_ICW3, PICM_OCW3, PICS_ICW3, PICS_OCW3 ; +u_short PICM_ICW4, PICS_ICW4 ; + +/* +** picinit() - This routine +** * Establishes a table of interrupt vectors +** * Establishes location of PICs in the system +** * Unmasks all interrupts in the PICs +** * Initialises them +** +** At this stage the interrupt functionality of this system should be +** complete. +*/ + +/* +** Initialise the PICs , master first, then the slave. +** All the register field definitions are described in pic.h also +** the settings of these fields for the various registers are selected. +*/ + +void +picinit(void) +{ + + asm("cli"); + + /* + ** 0. Initialise the current level to match cli() + */ + int i; + + for (i = 0; i < NCPUS; i++) + curr_ipl[i] = SPLHI; + curr_pic_mask = 0; + + /* + ** 1. Generate addresses to each PIC port. + */ + + master_icw = PIC_MASTER_ICW; + master_ocw = PIC_MASTER_OCW; + slaves_icw = PIC_SLAVE_ICW; + slaves_ocw = PIC_SLAVE_OCW; + + /* + ** 2. Select options for each ICW and each OCW for each PIC. + */ + + PICM_ICW1 = + (ICW_TEMPLATE | EDGE_TRIGGER | ADDR_INTRVL8 | CASCADE_MODE | ICW4__NEEDED); + + PICS_ICW1 = + (ICW_TEMPLATE | EDGE_TRIGGER | ADDR_INTRVL8 | CASCADE_MODE | ICW4__NEEDED); + + PICM_ICW2 = PICM_VECTBASE; + PICS_ICW2 = PICS_VECTBASE; + +#ifdef AT386 + PICM_ICW3 = ( SLAVE_ON_IR2 ); + PICS_ICW3 = ( I_AM_SLAVE_2 ); +#endif /* AT386 */ + + PICM_ICW4 = + (SNF_MODE_DIS | NONBUFD_MODE | NRML_EOI_MOD | I8086_EMM_MOD); + PICS_ICW4 = + (SNF_MODE_DIS | NONBUFD_MODE | NRML_EOI_MOD | I8086_EMM_MOD); + + PICM_OCW1 = (curr_pic_mask & 0x00FF); + PICS_OCW1 = ((curr_pic_mask & 0xFF00)>>8); + + PICM_OCW2 = NON_SPEC_EOI; + PICS_OCW2 = NON_SPEC_EOI; + + PICM_OCW3 = (OCW_TEMPLATE | READ_NEXT_RD | READ_IR_ONRD ); + PICS_OCW3 = (OCW_TEMPLATE | READ_NEXT_RD | READ_IR_ONRD ); + + /* + ** 3. Initialise master - send commands to master PIC + */ + + outb ( master_icw, PICM_ICW1 ); + outb ( master_ocw, PICM_ICW2 ); + outb ( master_ocw, PICM_ICW3 ); + outb ( master_ocw, PICM_ICW4 ); + + outb ( master_ocw, PICM_MASK ); + outb ( master_icw, PICM_OCW3 ); + + /* + ** 4. Initialise slave - send commands to slave PIC + */ + + outb ( slaves_icw, PICS_ICW1 ); + outb ( slaves_ocw, PICS_ICW2 ); + outb ( slaves_ocw, PICS_ICW3 ); + outb ( slaves_ocw, PICS_ICW4 ); + + + outb ( slaves_ocw, PICS_OCW1 ); + outb ( slaves_icw, PICS_OCW3 ); + + /* + ** 5. Initialise interrupts + */ + outb ( master_ocw, PICM_OCW1 ); + +} + +void +intnull(int unit_dev) +{ + static char warned[NINTR]; + + if (unit_dev >= NINTR) + printf("Unknown interrupt %d\n", unit_dev); + else if (!warned[unit_dev]) + { + printf("intnull(%d)\n", unit_dev); + warned[unit_dev] = 1; + } + +} + +/* + * Mask a PIC IRQ. + */ +void +mask_irq (unsigned int irq_nr) +{ + int new_pic_mask = curr_pic_mask | 1 << irq_nr; + + if (curr_pic_mask != new_pic_mask) + { + curr_pic_mask = new_pic_mask; + if (irq_nr < 8) + { + outb (PIC_MASTER_OCW, curr_pic_mask & 0xff); + } + else + { + outb (PIC_SLAVE_OCW, curr_pic_mask >> 8); + } + } +} + +/* + * Unmask a PIC IRQ. + */ +void +unmask_irq (unsigned int irq_nr) +{ + int mask; + int new_pic_mask; + + mask = 1 << irq_nr; + if (irq_nr >= 8) + { + mask |= 1 << 2; + } + + new_pic_mask = curr_pic_mask & ~mask; + + if (curr_pic_mask != new_pic_mask) + { + curr_pic_mask = new_pic_mask; + if (irq_nr < 8) + { + outb (PIC_MASTER_OCW, curr_pic_mask & 0xff); + } + else + { + outb (PIC_SLAVE_OCW, curr_pic_mask >> 8); + } + } +} + diff --git a/i386/i386/pic.h b/i386/i386/pic.h new file mode 100644 index 0000000..aec0ef6 --- /dev/null +++ b/i386/i386/pic.h @@ -0,0 +1,191 @@ +/* + * 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. + */ +/* +Copyright (c) 1988,1989 Prime Computer, Inc. Natick, MA 01760 +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 Prime +Computer, Inc. not be used in advertising or publicity +pertaining to distribution of the software without +specific, written prior permission. + +THIS SOFTWARE IS PROVIDED "AS IS", AND PRIME COMPUTER, +INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS +SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN +NO EVENT SHALL PRIME COMPUTER, INC. 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 OUR OF OR IN CONNECTION +WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#ifndef _I386_PIC_H_ +#define _I386_PIC_H_ + +#ifndef APIC +#define NINTR 0x10 +#endif +#define NPICS 0x02 + +/* +** The following are definitions used to locate the PICs in the system +*/ + +#if defined(AT386) || defined(ATX86_64) +#define ADDR_PIC_BASE 0x20 +#define OFF_ICW 0x00 +#define OFF_OCW 0x01 +#define SIZE_PIC 0x80 +#endif /* defined(AT386) */ + +#define PIC_MASTER_ICW (ADDR_PIC_BASE + OFF_ICW) +#define PIC_MASTER_OCW (ADDR_PIC_BASE + OFF_OCW) +#define PIC_SLAVE_ICW (PIC_MASTER_ICW + SIZE_PIC) +#define PIC_SLAVE_OCW (PIC_MASTER_OCW + SIZE_PIC) + +/* +** The following banks of definitions ICW1, ICW2, ICW3, and ICW4 are used +** to define the fields of the various ICWs for initialisation of the PICs +*/ + +/* +** ICW1 +*/ + +#define ICW_TEMPLATE 0x10 + +#define LEVL_TRIGGER 0x08 +#define EDGE_TRIGGER 0x00 +#define ADDR_INTRVL4 0x04 +#define ADDR_INTRVL8 0x00 +#define SINGLE__MODE 0x02 +#define CASCADE_MODE 0x00 +#define ICW4__NEEDED 0x01 +#define NO_ICW4_NEED 0x00 + +/* +** ICW2 +*/ + +#if defined(AT386) || defined(ATX86_64) +#define PICM_VECTBASE 0x20 +#define PICS_VECTBASE PICM_VECTBASE + 0x08 +#endif /* defined(AT386) */ + +/* +** ICW3 +*/ + +#define SLAVE_ON_IR0 0x01 +#define SLAVE_ON_IR1 0x02 +#define SLAVE_ON_IR2 0x04 +#define SLAVE_ON_IR3 0x08 +#define SLAVE_ON_IR4 0x10 +#define SLAVE_ON_IR5 0x20 +#define SLAVE_ON_IR6 0x40 +#define SLAVE_ON_IR7 0x80 + +#define I_AM_SLAVE_0 0x00 +#define I_AM_SLAVE_1 0x01 +#define I_AM_SLAVE_2 0x02 +#define I_AM_SLAVE_3 0x03 +#define I_AM_SLAVE_4 0x04 +#define I_AM_SLAVE_5 0x05 +#define I_AM_SLAVE_6 0x06 +#define I_AM_SLAVE_7 0x07 + +/* +** ICW4 +*/ + +#define SNF_MODE_ENA 0x10 +#define SNF_MODE_DIS 0x00 +#define BUFFERD_MODE 0x08 +#define NONBUFD_MODE 0x00 +#define AUTO_EOI_MOD 0x02 +#define NRML_EOI_MOD 0x00 +#define I8086_EMM_MOD 0x01 +#define SET_MCS_MODE 0x00 + +/* +** OCW1 +*/ +#define PICM_MASK 0xFF +#define PICS_MASK 0xFF +/* +** OCW2 +*/ + +#define NON_SPEC_EOI 0x20 +#define SPECIFIC_EOI 0x60 +#define ROT_NON_SPEC 0xA0 +#define SET_ROT_AEOI 0x80 +#define RSET_ROTAEOI 0x00 +#define ROT_SPEC_EOI 0xE0 +#define SET_PRIORITY 0xC0 +#define NO_OPERATION 0x40 + +#define SEND_EOI_IR0 0x00 +#define SEND_EOI_IR1 0x01 +#define SEND_EOI_IR2 0x02 +#define SEND_EOI_IR3 0x03 +#define SEND_EOI_IR4 0x04 +#define SEND_EOI_IR5 0x05 +#define SEND_EOI_IR6 0x06 +#define SEND_EOI_IR7 0x07 + +/* +** OCW3 +*/ + +#define OCW_TEMPLATE 0x08 +#define SPECIAL_MASK 0x40 +#define MASK_MDE_SET 0x20 +#define MASK_MDE_RST 0x00 +#define POLL_COMMAND 0x04 +#define NO_POLL_CMND 0x00 +#define READ_NEXT_RD 0x02 +#define READ_IR_ONRD 0x00 +#define READ_IS_ONRD 0x01 + +#define PIC_MASK_ZERO 0x00 + +#if !defined(__ASSEMBLER__) && !defined(APIC) +extern void picinit (void); +extern int curr_pic_mask; +extern void intnull(int unit); +extern void mask_irq (unsigned int irq_nr); +extern void unmask_irq (unsigned int irq_nr); +#endif /* __ASSEMBLER__ */ + +#endif /* _I386_PIC_H_ */ diff --git a/i386/i386/pio.h b/i386/i386/pio.h new file mode 100644 index 0000000..c488fbb --- /dev/null +++ b/i386/i386/pio.h @@ -0,0 +1,61 @@ +/* + * 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. + */ + +#ifndef _I386_PIO_H_ +#define _I386_PIO_H_ + +#ifndef __GNUC__ +#error You do not stand a chance. This file is gcc only. +#endif /* __GNUC__ */ + +#define inl(y) \ +({ unsigned int _tmp__; \ + asm volatile("inl %1, %0" : "=a" (_tmp__) : "dN" ((unsigned short)(y))); \ + _tmp__; }) + +#define inw(y) \ +({ unsigned short _tmp__; \ + asm volatile("inw %1, %0" : "=a" (_tmp__) : "dN" ((unsigned short)(y))); \ + _tmp__; }) + +#define inb(y) \ +({ unsigned char _tmp__; \ + asm volatile("inb %1, %0" : "=a" (_tmp__) : "dN" ((unsigned short)(y))); \ + _tmp__; }) + + +#define outl(x, y) \ +{ asm volatile("outl %0, %1" : : "a" ((unsigned int)(y)) , "dN" ((unsigned short)(x))); } + + +#define outw(x, y) \ +{ asm volatile("outw %0, %1" : : "a" ((unsigned short)(y)) , "dN" ((unsigned short)(x))); } + + +#define outb(x, y) \ +{ asm volatile("outb %0, %1" : : "a" ((unsigned char)(y)) , "dN" ((unsigned short)(x))); } + +#endif /* _I386_PIO_H_ */ diff --git a/i386/i386/pit.c b/i386/i386/pit.c new file mode 100644 index 0000000..6c006a9 --- /dev/null +++ b/i386/i386/pit.c @@ -0,0 +1,140 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University + * Copyright (c) 1991 IBM Corporation + * 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, + * and that the name IBM not be used in advertising or publicity + * pertaining to distribution of the software without specific, written + * prior permission. + * + * CARNEGIE MELLON AND IBM ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON AND IBM DISCLAIM 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. + */ +/* + Copyright 1988, 1989 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 +#include +#include +#include +#include +#include + +int pitctl_port = PITCTL_PORT; /* For 386/20 Board */ +int pitctr0_port = PITCTR0_PORT; /* For 386/20 Board */ +/* We want PIT 0 in square wave mode */ + +int pit0_mode = PIT_C0|PIT_SQUAREMODE|PIT_READMODE ; + + +unsigned int clknumb = CLKNUM; /* interrupt interval for timer 0 */ + +void +pit_prepare_sleep(int persec) +{ + /* Prepare to sleep for 1/persec seconds */ + uint32_t val = 0; + uint8_t lsb, msb; + + val = inb(PITAUX_PORT); + val &= ~PITAUX_OUT2; + val |= PITAUX_GATE2; + outb (PITAUX_PORT, val); + outb (PITCTL_PORT, PIT_C2 | PIT_LOADMODE | PIT_ONESHOTMODE); + val = CLKNUM / persec; + lsb = val & 0xff; + msb = val >> 8; + outb (PITCTR2_PORT, lsb); + val = inb(POST_PORT); /* ~1us i/o delay */ + outb (PITCTR2_PORT, msb); +} + +void +pit_sleep(void) +{ + uint8_t val; + + /* Start counting down */ + val = inb(PITAUX_PORT); + val &= ~PITAUX_GATE2; + outb (PITAUX_PORT, val); /* Gate low */ + val |= PITAUX_GATE2; + outb (PITAUX_PORT, val); /* Gate high */ + + /* Wait until counter reaches zero */ + while ((inb(PITAUX_PORT) & PITAUX_VAL) == 0); +} + +void +pit_udelay(int usec) +{ + pit_prepare_sleep(1000000 / usec); + pit_sleep(); +} + +void +pit_mdelay(int msec) +{ + pit_prepare_sleep(1000 / msec); + pit_sleep(); +} + +void +clkstart(void) +{ + if (cpu_number() != 0) + /* Only one PIT initialization is needed */ + return; + unsigned char byte; + unsigned long s; + + s = sploff(); /* disable interrupts */ + + /* Since we use only timer 0, we program that. + * 8254 Manual specifically says you do not need to program + * timers you do not use + */ + outb(pitctl_port, pit0_mode); + clknumb = (CLKNUM + hz / 2) / hz; + byte = clknumb; + outb(pitctr0_port, byte); + byte = clknumb>>8; + outb(pitctr0_port, byte); + splon(s); /* restore interrupt state */ +} diff --git a/i386/i386/pit.h b/i386/i386/pit.h new file mode 100644 index 0000000..49e1051 --- /dev/null +++ b/i386/i386/pit.h @@ -0,0 +1,98 @@ +/* + * 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. + */ +/* + Copyright 1988, 1989 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. +*/ + +#ifndef _I386_PIT_H_ +#define _I386_PIT_H_ + +#if defined(AT386) || defined(ATX86_64) +/* Definitions for 8254 Programmable Interrupt Timer ports on AT 386 */ +#define PITCTR0_PORT 0x40 /* counter 0 port */ +#define PITCTR1_PORT 0x41 /* counter 1 port */ +#define PITCTR2_PORT 0x42 /* counter 2 port */ +#define PITCTL_PORT 0x43 /* PIT control port */ +#define PITAUX_PORT 0x61 /* PIT auxiliary port */ +/* bits used in auxiliary control port for timer 2 */ +#define PITAUX_GATE2 0x01 /* aux port, PIT gate 2 input */ +#define PITAUX_OUT2 0x02 /* aux port, PIT clock out 2 enable */ +#define PITAUX_VAL 0x20 /* aux port, output */ +#endif /* defined(AT386) */ + +/* Following are used for Timer 0 */ +#define PIT_C0 0x00 /* select counter 0 */ +#define PIT_LOADMODE 0x30 /* load least significant byte followed + * by most significant byte */ +#define PIT_NDIVMODE 0x04 /*divide by N counter */ + +/* Used for Timer 1. Used for delay calculations in countdown mode */ +#define PIT_C1 0x40 /* select counter 1 */ +#define PIT_READMODE 0x30 /* read or load least significant byte + * followed by most significant byte */ + +#define PIT_SQUAREMODE 0x06 /* square-wave mode */ +#define PIT_RATEMODE 0x04 /* rate generator mode */ +#define PIT_ONESHOTMODE 0x02 /* one-shot mode */ + +/* Used for Timer 2. */ +#define PIT_C2 0x80 /* select counter 2 */ + +#define POST_PORT 0x80 /* used for tiny i/o delay */ + +/* + * Clock speed for the timer in hz divided by the constant HZ + * (defined in param.h) + */ +#if defined(AT386) || defined(ATX86_64) +#define CLKNUM 1193182 +#endif /* AT386 */ + +extern void clkstart(void); +extern void pit_prepare_sleep(int hz); +extern void pit_sleep(void); +extern void pit_udelay(int usec); +extern void pit_mdelay(int msec); + +#endif /* _I386_PIT_H_ */ diff --git a/i386/i386/pmap.h b/i386/i386/pmap.h new file mode 100644 index 0000000..a989923 --- /dev/null +++ b/i386/i386/pmap.h @@ -0,0 +1,27 @@ +/* + * 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. + */ + +#include diff --git a/i386/i386/proc_reg.h b/i386/i386/proc_reg.h new file mode 100644 index 0000000..704676c --- /dev/null +++ b/i386/i386/proc_reg.h @@ -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 "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. + */ +/* + * Processor registers for i386 and i486. + */ +#ifndef _I386_PROC_REG_H_ +#define _I386_PROC_REG_H_ + +/* + * CR0 + */ +#define CR0_PG 0x80000000 /* enable paging */ +#define CR0_CD 0x40000000 /* i486: cache disable */ +#define CR0_NW 0x20000000 /* i486: no write-through */ +#define CR0_AM 0x00040000 /* i486: alignment check mask */ +#define CR0_WP 0x00010000 /* i486: write-protect kernel access */ +#define CR0_NE 0x00000020 /* i486: handle numeric exceptions */ +#define CR0_ET 0x00000010 /* extension type is 80387 */ + /* (not official) */ +#define CR0_TS 0x00000008 /* task switch */ +#define CR0_EM 0x00000004 /* emulate coprocessor */ +#define CR0_MP 0x00000002 /* monitor coprocessor */ +#define CR0_PE 0x00000001 /* enable protected mode */ + +/* + * CR3 + */ +#define CR3_PCD 0x0010 /* Page-level Cache Disable */ +#define CR3_PWT 0x0008 /* Page-level Writes Transparent */ + +/* + * CR4 + */ +#define CR4_VME 0x0001 /* Virtual-8086 Mode Extensions */ +#define CR4_PVI 0x0002 /* Protected-Mode Virtual Interrupts */ +#define CR4_TSD 0x0004 /* Time Stamp Disable */ +#define CR4_DE 0x0008 /* Debugging Extensions */ +#define CR4_PSE 0x0010 /* Page Size Extensions */ +#define CR4_PAE 0x0020 /* Physical Address Extension */ +#define CR4_MCE 0x0040 /* Machine-Check Enable */ +#define CR4_PGE 0x0080 /* Page Global Enable */ +#define CR4_PCE 0x0100 /* Performance-Monitoring Counter + * Enable */ +#define CR4_OSFXSR 0x0200 /* Operating System Support for FXSAVE + * and FXRSTOR instructions */ +#define CR4_OSXMMEXCPT 0x0400 /* Operating System Support for Unmasked + * SIMD Floating-Point Exceptions */ +#define CR4_OSXSAVE 0x40000 /* Operating System Support for XSAVE + * and XRSTOR instructions */ + +#ifndef __ASSEMBLER__ +#ifdef __GNUC__ + +#ifndef MACH_HYP +#include +#include +#endif /* MACH_HYP */ + +static inline unsigned long +get_eflags(void) +{ + unsigned long eflags; +#ifdef __x86_64__ + asm("pushfq; popq %0" : "=r" (eflags)); +#else + asm("pushfl; popl %0" : "=r" (eflags)); +#endif + return eflags; +} + +static inline void +set_eflags(unsigned long eflags) +{ +#ifdef __x86_64__ + asm volatile("pushq %0; popfq" : : "r" (eflags)); +#else + asm volatile("pushl %0; popfl" : : "r" (eflags)); +#endif +} + +#define get_esp() \ + ({ \ + register unsigned long _temp__ asm("esp"); \ + _temp__; \ + }) + +#ifdef __x86_64__ +#define get_eflags() \ + ({ \ + register unsigned long _temp__; \ + asm("pushfq; popq %0" : "=r" (_temp__)); \ + _temp__; \ + }) +#else +#define get_eflags() \ + ({ \ + register unsigned long _temp__; \ + asm("pushfl; popl %0" : "=r" (_temp__)); \ + _temp__; \ + }) +#endif + +#define get_cr0() \ + ({ \ + register unsigned long _temp__; \ + asm volatile("mov %%cr0, %0" : "=r" (_temp__)); \ + _temp__; \ + }) + +#define set_cr0(value) \ + ({ \ + register unsigned long _temp__ = (value); \ + asm volatile("mov %0, %%cr0" : : "r" (_temp__)); \ + }) + +#define get_cr2() \ + ({ \ + register unsigned long _temp__; \ + asm volatile("mov %%cr2, %0" : "=r" (_temp__)); \ + _temp__; \ + }) + +#ifdef MACH_PV_PAGETABLES +extern unsigned long cr3; +#define get_cr3() (cr3) +#define set_cr3(value) \ + ({ \ + cr3 = (value); \ + if (!hyp_set_cr3(value)) \ + panic("set_cr3"); \ + }) +#else /* MACH_PV_PAGETABLES */ +#define get_cr3() \ + ({ \ + register unsigned long _temp__; \ + asm volatile("mov %%cr3, %0" : "=r" (_temp__)); \ + _temp__; \ + }) + +#define set_cr3(value) \ + ({ \ + register unsigned long _temp__ = (value); \ + asm volatile("mov %0, %%cr3" : : "r" (_temp__) : "memory"); \ + }) +#endif /* MACH_PV_PAGETABLES */ + +#define flush_tlb() set_cr3(get_cr3()) + +#ifndef MACH_PV_PAGETABLES +#define invlpg(addr) \ + ({ \ + asm volatile("invlpg (%0)" : : "r" (addr)); \ + }) + +#define invlpg_linear(start) \ + ({ \ + asm volatile( \ + "movw %w1,%%es\n" \ + "\tinvlpg %%es:(%0)\n" \ + "\tmovw %w2,%%es" \ + :: "r" (start), "q" (LINEAR_DS), "q" (KERNEL_DS)); \ + }) + +#define invlpg_linear_range(start, end) \ + ({ \ + register unsigned long var = trunc_page(start); \ + asm volatile( \ + "movw %w2,%%es\n" \ + "1:\tinvlpg %%es:(%0)\n" \ + "\taddl %c4,%0\n" \ + "\tcmpl %0,%1\n" \ + "\tjb 1b\n" \ + "\tmovw %w3,%%es" \ + : "+r" (var) : "r" (end), \ + "q" (LINEAR_DS), "q" (KERNEL_DS), "i" (PAGE_SIZE)); \ + }) +#endif /* MACH_PV_PAGETABLES */ + +#define get_cr4() \ + ({ \ + register unsigned long _temp__; \ + asm volatile("mov %%cr4, %0" : "=r" (_temp__)); \ + _temp__; \ + }) + +#define set_cr4(value) \ + ({ \ + register unsigned long _temp__ = (value); \ + asm volatile("mov %0, %%cr4" : : "r" (_temp__)); \ + }) + + +#ifdef MACH_RING1 +#define set_ts() \ + hyp_fpu_taskswitch(1) +#define clear_ts() \ + hyp_fpu_taskswitch(0) +#else /* MACH_RING1 */ +#define set_ts() \ + set_cr0(get_cr0() | CR0_TS) + +#define clear_ts() \ + asm volatile("clts") +#endif /* MACH_RING1 */ + +#define get_tr() \ + ({ \ + unsigned short _seg__; \ + asm volatile("str %0" : "=rm" (_seg__) ); \ + _seg__; \ + }) + +#define set_tr(seg) \ + asm volatile("ltr %0" : : "rm" ((unsigned short)(seg)) ) + +#define get_ldt() \ + ({ \ + unsigned short _seg__; \ + asm volatile("sldt %0" : "=rm" (_seg__) ); \ + _seg__; \ + }) + +#define set_ldt(seg) \ + asm volatile("lldt %0" : : "rm" ((unsigned short)(seg)) ) + +/* This doesn't set a processor register, + but it's often used immediately after setting one, + to flush the instruction queue. */ +#define flush_instr_queue() \ + asm("jmp 0f\n" \ + "0:\n") + +#ifdef MACH_RING1 +#define get_dr0() hyp_get_debugreg(0) +#else +#define get_dr0() \ + ({ \ + register unsigned long _temp__; \ + asm volatile("mov %%dr0, %0" : "=r" (_temp__)); \ + _temp__; \ + }) +#endif + +#ifdef MACH_RING1 +#define set_dr0(value) hyp_set_debugreg(0, value) +#else +#define set_dr0(value) \ + ({ \ + register unsigned long _temp__ = (value); \ + asm volatile("mov %0,%%dr0" : : "r" (_temp__)); \ + }) +#endif + +#ifdef MACH_RING1 +#define get_dr1() hyp_get_debugreg(1) +#else +#define get_dr1() \ + ({ \ + register unsigned long _temp__; \ + asm volatile("mov %%dr1, %0" : "=r" (_temp__)); \ + _temp__; \ + }) +#endif + +#ifdef MACH_RING1 +#define set_dr1(value) hyp_set_debugreg(1, value) +#else +#define set_dr1(value) \ + ({ \ + register unsigned long _temp__ = (value); \ + asm volatile("mov %0,%%dr1" : : "r" (_temp__)); \ + }) +#endif + +#ifdef MACH_RING1 +#define get_dr2() hyp_get_debugreg(2) +#else +#define get_dr2() \ + ({ \ + register unsigned long _temp__; \ + asm volatile("mov %%dr2, %0" : "=r" (_temp__)); \ + _temp__; \ + }) +#endif + +#ifdef MACH_RING1 +#define set_dr2(value) hyp_set_debugreg(2, value) +#else +#define set_dr2(value) \ + ({ \ + register unsigned long _temp__ = (value); \ + asm volatile("mov %0,%%dr2" : : "r" (_temp__)); \ + }) +#endif + +#ifdef MACH_RING1 +#define get_dr3() hyp_get_debugreg(3) +#else +#define get_dr3() \ + ({ \ + register unsigned long _temp__; \ + asm volatile("mov %%dr3, %0" : "=r" (_temp__)); \ + _temp__; \ + }) +#endif + +#ifdef MACH_RING1 +#define set_dr3(value) hyp_set_debugreg(3, value) +#else +#define set_dr3(value) \ + ({ \ + register unsigned long _temp__ = (value); \ + asm volatile("mov %0,%%dr3" : : "r" (_temp__)); \ + }) +#endif + +#ifdef MACH_RING1 +#define get_dr6() hyp_get_debugreg(6) +#else +#define get_dr6() \ + ({ \ + register unsigned long _temp__; \ + asm volatile("mov %%dr6, %0" : "=r" (_temp__)); \ + _temp__; \ + }) +#endif + +#ifdef MACH_RING1 +#define set_dr6(value) hyp_set_debugreg(6, value) +#else +#define set_dr6(value) \ + ({ \ + register unsigned long _temp__ = (value); \ + asm volatile("mov %0,%%dr6" : : "r" (_temp__)); \ + }) +#endif + +#ifdef MACH_RING1 +#define get_dr7() hyp_get_debugreg(7) +#else +#define get_dr7() \ + ({ \ + register unsigned long _temp__; \ + asm volatile("mov %%dr7, %0" : "=r" (_temp__)); \ + _temp__; \ + }) +#endif + +#ifdef MACH_RING1 +#define set_dr7(value) hyp_set_debugreg(7, value) +#else +#define set_dr7(value) \ + ({ \ + register unsigned long _temp__ = (value); \ + asm volatile("mov %0,%%dr7" : : "r" (_temp__)); \ + }) +#endif + +/* Note: gcc might want to use bx or the stack for %1 addressing, so we can't + * use them :/ */ +#ifdef __x86_64__ +#define cpuid(eax, ebx, ecx, edx) \ +{ \ + uint64_t sav_rbx; \ + asm( "mov %%rbx,%2\n\t" \ + "cpuid\n\t" \ + "xchg %2,%%rbx\n\t" \ + "movl %k2,%1\n\t" \ + : "+a" (eax), "=m" (ebx), "=&r" (sav_rbx), "+c" (ecx), "=&d" (edx)); \ +} +#else +#define cpuid(eax, ebx, ecx, edx) \ +{ \ + asm ( "mov %%ebx,%1\n\t" \ + "cpuid\n\t" \ + "xchg %%ebx,%1\n\t" \ + : "+a" (eax), "=&SD" (ebx), "+c" (ecx), "=&d" (edx)); \ +} +#endif + +#endif /* __GNUC__ */ +#endif /* __ASSEMBLER__ */ + +#endif /* _I386_PROC_REG_H_ */ diff --git a/i386/i386/sched_param.h b/i386/i386/sched_param.h new file mode 100644 index 0000000..c93ed8a --- /dev/null +++ b/i386/i386/sched_param.h @@ -0,0 +1,40 @@ +/* + * Mach Operating System + * Copyright (c) 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. + */ +/* + * Scheduler parameters. + */ + +#ifndef _I386_SCHED_PARAM_H_ +#define _I386_SCHED_PARAM_H_ + +/* + * Sequent requires a right shift of 17 bits to convert + * microseconds to priorities. + */ + +#define PRI_SHIFT 17 + +#endif /* _I386_SCHED_PARAM_H_ */ diff --git a/i386/i386/seg.h b/i386/i386/seg.h new file mode 100644 index 0000000..673d1d9 --- /dev/null +++ b/i386/i386/seg.h @@ -0,0 +1,264 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990 Carnegie Mellon University + * Copyright (c) 1991 IBM Corporation + * 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, + * and that the name IBM not be used in advertising or publicity + * pertaining to distribution of the software without specific, written + * prior permission. + * + * CARNEGIE MELLON AND IBM ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON AND IBM DISCLAIM 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. + */ + +#ifndef _I386_SEG_H_ +#define _I386_SEG_H_ + +#include +#include + +/* + * i386 segmentation. + */ + +/* Note: the value of KERNEL_RING is handled by hand in locore.S */ +#ifdef MACH_RING1 +#define KERNEL_RING 1 +#else /* MACH_RING1 */ +#define KERNEL_RING 0 +#endif /* MACH_RING1 */ + +#ifndef __ASSEMBLER__ + +/* + * Real segment descriptor. + */ +struct real_descriptor { + unsigned int limit_low:16, /* limit 0..15 */ + base_low:16, /* base 0..15 */ + base_med:8, /* base 16..23 */ + access:8, /* access byte */ + limit_high:4, /* limit 16..19 */ + granularity:4, /* granularity */ + base_high:8; /* base 24..31 */ +}; +typedef struct real_descriptor real_descriptor_t; +typedef real_descriptor_t *real_descriptor_list_t; +typedef const real_descriptor_list_t const_real_descriptor_list_t; + +#ifdef __x86_64__ +struct real_descriptor64 { + unsigned int limit_low:16, /* limit 0..15 */ + base_low:16, /* base 0..15 */ + base_med:8, /* base 16..23 */ + access:8, /* access byte */ + limit_high:4, /* limit 16..19 */ + granularity:4, /* granularity */ + base_high:8, /* base 24..31 */ + base_ext:32, /* base 32..63 */ + reserved1:8, + zero:5, + reserved2:19; +}; +#endif + +struct real_gate { + unsigned int offset_low:16, /* offset 0..15 */ + selector:16, + word_count:8, + access:8, + offset_high:16; /* offset 16..31 */ +#ifdef __x86_64__ + unsigned int offset_ext:32, /* offset 32..63 */ + reserved:32; +#endif +}; + +#endif /* !__ASSEMBLER__ */ + +#define SZ_64 0x2 /* 64-bit segment */ +#define SZ_32 0x4 /* 32-bit segment */ +#define SZ_16 0x0 /* 16-bit segment */ +#define SZ_G 0x8 /* 4K limit field */ + +#define ACC_A 0x01 /* accessed */ +#define ACC_TYPE 0x1e /* type field: */ + +#define ACC_TYPE_SYSTEM 0x00 /* system descriptors: */ + +#define ACC_LDT 0x02 /* LDT */ +#define ACC_CALL_GATE_16 0x04 /* 16-bit call gate */ +#define ACC_TASK_GATE 0x05 /* task gate */ +#define ACC_TSS 0x09 /* task segment */ +#define ACC_CALL_GATE 0x0c /* call gate */ +#define ACC_INTR_GATE 0x0e /* interrupt gate */ +#define ACC_TRAP_GATE 0x0f /* trap gate */ + +#define ACC_TSS_BUSY 0x02 /* task busy */ + +#define ACC_TYPE_USER 0x10 /* user descriptors */ + +#define ACC_DATA 0x10 /* data */ +#define ACC_DATA_W 0x12 /* data, writable */ +#define ACC_DATA_E 0x14 /* data, expand-down */ +#define ACC_DATA_EW 0x16 /* data, expand-down, + writable */ +#define ACC_CODE 0x18 /* code */ +#define ACC_CODE_R 0x1a /* code, readable */ +#define ACC_CODE_C 0x1c /* code, conforming */ +#define ACC_CODE_CR 0x1e /* code, conforming, + readable */ +#define ACC_PL 0x60 /* access rights: */ +#define ACC_PL_K (KERNEL_RING << 5) /* kernel access only */ +#define ACC_PL_U 0x60 /* user access */ +#define ACC_P 0x80 /* segment present */ + +/* + * Components of a selector + */ +#define SEL_LDT 0x04 /* local selector */ +#define SEL_PL 0x03 /* privilege level: */ +#define SEL_PL_K KERNEL_RING /* kernel selector */ +#define SEL_PL_U 0x03 /* user selector */ + +/* + * Convert selector to descriptor table index. + */ +#define sel_idx(sel) ((sel)>>3) + + +#ifndef __ASSEMBLER__ + +#include +#include + + +/* Format of a "pseudo-descriptor", used for loading the IDT and GDT. */ +struct pseudo_descriptor +{ + unsigned short limit; + unsigned long linear_base; + short pad; +} __attribute__((packed)); + + +/* Load the processor's IDT, GDT, or LDT pointers. */ +static inline void lgdt(struct pseudo_descriptor *pdesc) +{ + __asm volatile("lgdt %0" : : "m" (*pdesc)); +} +static inline void lidt(struct pseudo_descriptor *pdesc) +{ + __asm volatile("lidt %0" : : "m" (*pdesc)); +} +static inline void lldt(unsigned short ldt_selector) +{ + __asm volatile("lldt %w0" : : "r" (ldt_selector) : "memory"); +} + +#ifdef CODE16 +#define i16_lgdt lgdt +#define i16_lidt lidt +#define i16_lldt lldt +#endif + + +/* Fill a segment descriptor. */ +static inline void +fill_descriptor(struct real_descriptor *_desc, vm_offset_t base, vm_offset_t limit, + unsigned char access, unsigned char sizebits) +{ + /* TODO: when !MACH_PV_DESCRIPTORS, setting desc and just memcpy isn't simpler actually */ +#ifdef MACH_PV_DESCRIPTORS + struct real_descriptor __desc, *desc = &__desc; +#else /* MACH_PV_DESCRIPTORS */ + struct real_descriptor *desc = _desc; +#endif /* MACH_PV_DESCRIPTORS */ + if (limit > 0xfffff) + { + limit >>= 12; + sizebits |= SZ_G; + } + desc->limit_low = limit & 0xffff; + desc->base_low = base & 0xffff; + desc->base_med = (base >> 16) & 0xff; + desc->access = access | ACC_P; + desc->limit_high = limit >> 16; + desc->granularity = sizebits; + desc->base_high = base >> 24; +#ifdef MACH_PV_DESCRIPTORS + if (hyp_do_update_descriptor(kv_to_ma(_desc), *(uint64_t*)desc)) + panic("couldn't update descriptor(%zu to %08lx%08lx)\n", (vm_offset_t) kv_to_ma(_desc), *(((unsigned long*)desc)+1), *(unsigned long *)desc); +#endif /* MACH_PV_DESCRIPTORS */ +} + +#ifdef __x86_64__ +static inline void +fill_descriptor64(struct real_descriptor64 *_desc, unsigned long base, unsigned limit, + unsigned char access, unsigned char sizebits) +{ + /* TODO: when !MACH_PV_DESCRIPTORS, setting desc and just memcpy isn't simpler actually */ +#ifdef MACH_PV_DESCRIPTORS + struct real_descriptor64 __desc, *desc = &__desc; +#else /* MACH_PV_DESCRIPTORS */ + struct real_descriptor64 *desc = _desc; +#endif /* MACH_PV_DESCRIPTORS */ + if (limit > 0xfffff) + { + limit >>= 12; + sizebits |= SZ_G; + } + desc->limit_low = limit & 0xffff; + desc->base_low = base & 0xffff; + desc->base_med = (base >> 16) & 0xff; + desc->access = access | ACC_P; + desc->limit_high = limit >> 16; + desc->granularity = sizebits; + desc->base_high = base >> 24; + desc->base_ext = base >> 32; + desc->reserved1 = 0; + desc->zero = 0; + desc->reserved2 = 0; +#ifdef MACH_PV_DESCRIPTORS + if (hyp_do_update_descriptor(kv_to_ma(_desc), *(uint64_t*)desc)) + panic("couldn't update descriptor(%lu to %08lx%08lx)\n", (vm_offset_t) kv_to_ma(_desc), *(((unsigned long*)desc)+1), *(unsigned long *)desc); +#endif /* MACH_PV_DESCRIPTORS */ +} +#endif + +/* Fill a gate with particular values. */ +static inline void +fill_gate(struct real_gate *gate, unsigned long offset, unsigned short selector, + unsigned char access, unsigned char word_count) +{ + gate->offset_low = offset & 0xffff; + gate->selector = selector; + gate->word_count = word_count; + gate->access = access | ACC_P; + gate->offset_high = (offset >> 16) & 0xffff; +#ifdef __x86_64__ + gate->offset_ext = offset >> 32; + gate->reserved = 0; +#endif +} + +#endif /* !__ASSEMBLER__ */ + +#endif /* _I386_SEG_H_ */ diff --git a/i386/i386/setjmp.h b/i386/i386/setjmp.h new file mode 100644 index 0000000..eacc8e4 --- /dev/null +++ b/i386/i386/setjmp.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 "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. + */ +/* + * Setjmp/longjmp buffer for i386. + */ +#ifndef _I386_SETJMP_H_ +#define _I386_SETJMP_H_ + +typedef struct jmp_buf { +#ifdef __i386__ + int jmp_buf[6]; /* ebx, esi, edi, ebp, esp, eip */ +#else + long jmp_buf[8]; /* rbx, rbp, r12, r13, r14, r15, rsp, rip */ +#endif +} jmp_buf_t; + +extern int _setjmp(jmp_buf_t*); + +extern void _longjmp(jmp_buf_t*, int) __attribute__ ((noreturn)); + +#endif /* _I386_SETJMP_H_ */ diff --git a/i386/i386/smp.c b/i386/i386/smp.c new file mode 100644 index 0000000..05e9de6 --- /dev/null +++ b/i386/i386/smp.c @@ -0,0 +1,199 @@ +/* smp.h - i386 SMP controller for Mach + Copyright (C) 2020 Free Software Foundation, Inc. + Written by Almudena Garcia Jurado-Centurion + + This file is part of GNU Mach. + + GNU Mach is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Mach is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + * smp_data_init: initialize smp_data structure + * Must be called after smp_init(), once all APIC structures + * has been initialized + */ +static void smp_data_init(void) +{ + uint8_t numcpus = apic_get_numcpus(); + smp_set_numcpus(numcpus); + + for(int i = 0; i < numcpus; i++){ + machine_slot[i].is_cpu = TRUE; + } + +} + +static void smp_send_ipi(unsigned apic_id, unsigned vector) +{ + unsigned long flags; + + cpu_intr_save(&flags); + + apic_send_ipi(NO_SHORTHAND, FIXED, PHYSICAL, ASSERT, EDGE, vector, apic_id); + + do { + cpu_pause(); + } while(lapic->icr_low.delivery_status == SEND_PENDING); + + apic_send_ipi(NO_SHORTHAND, FIXED, PHYSICAL, DE_ASSERT, EDGE, vector, apic_id); + + do { + cpu_pause(); + } while(lapic->icr_low.delivery_status == SEND_PENDING); + + cpu_intr_restore(flags); +} + +void smp_remote_ast(unsigned apic_id) +{ + smp_send_ipi(apic_id, CALL_AST_CHECK); +} + +void smp_pmap_update(unsigned apic_id) +{ + smp_send_ipi(apic_id, CALL_PMAP_UPDATE); +} + +static void +wait_for_ipi(void) +{ + /* This could have a timeout, but if the IPI + * is never delivered, its a disaster anyway */ + while (lapic->icr_low.delivery_status == SEND_PENDING) { + cpu_pause(); + } +} + +static int +smp_send_ipi_init(int apic_id) +{ + int err; + + lapic->error_status.r = 0; + + /* Assert INIT IPI: + * + * This is EDGE triggered to match the deassert + */ + apic_send_ipi(NO_SHORTHAND, INIT, PHYSICAL, ASSERT, EDGE, 0, apic_id); + + /* Wait for delivery */ + wait_for_ipi(); + hpet_mdelay(10); + + /* Deassert INIT IPI: + * + * NB: This must be an EDGE triggered deassert signal. + * A LEVEL triggered deassert is only supported on very old hardware + * that does not support STARTUP IPIs at all, and instead jump + * via a warm reset vector. + */ + apic_send_ipi(NO_SHORTHAND, INIT, PHYSICAL, DE_ASSERT, EDGE, 0, apic_id); + + /* Wait for delivery */ + wait_for_ipi(); + + err = lapic->error_status.r; + if (err) { + printf("ESR error upon INIT 0x%x\n", err); + } + return 0; +} + +static int +smp_send_ipi_startup(int apic_id, int vector) +{ + int err; + + lapic->error_status.r = 0; + + /* StartUp IPI: + * + * Have not seen any documentation for trigger mode for this IPI + * but it seems to work with EDGE. (AMD BKDG FAM16h document specifies dont care) + */ + apic_send_ipi(NO_SHORTHAND, STARTUP, PHYSICAL, ASSERT, EDGE, vector, apic_id); + + /* Wait for delivery */ + wait_for_ipi(); + + err = lapic->error_status.r; + if (err) { + printf("ESR error upon STARTUP 0x%x\n", err); + } + return 0; +} + +/* See Intel IA32/64 Software Developer's Manual 3A Section 8.4.4.1 */ +int smp_startup_cpu(unsigned apic_id, phys_addr_t start_eip) +{ +#if 0 + /* This block goes with a legacy method of INIT that only works with + * old hardware that does not support SIPIs. + * Must use INIT DEASSERT LEVEL triggered IPI to use this block. + * (At least one AMD FCH does not support this IPI mode, + * See AMD BKDG FAM16h document # 48751 page 461). + */ + + /* Tell CMOS to warm reset through through 40:67 */ + outb(CMOS_ADDR, CMOS_SHUTDOWN); + outb(CMOS_DATA, CM_JMP_467); + + /* Set warm reset vector to point to AP startup code */ + uint16_t dword[2]; + dword[0] = 0; + dword[1] = start_eip >> 4; + memcpy((uint8_t *)phystokv(0x467), dword, 4); +#endif + + /* Local cache flush */ + asm("wbinvd":::"memory"); + + printf("Sending IPIs to APIC ID %u...\n", apic_id); + + smp_send_ipi_init(apic_id); + hpet_mdelay(10); + smp_send_ipi_startup(apic_id, start_eip >> STARTUP_VECTOR_SHIFT); + hpet_udelay(200); + smp_send_ipi_startup(apic_id, start_eip >> STARTUP_VECTOR_SHIFT); + hpet_udelay(200); + + printf("done\n"); + return 0; +} + +/* + * smp_init: initialize the SMP support, starting the cpus searching + * and enumeration. + */ +int smp_init(void) +{ + smp_data_init(); + + return 0; +} diff --git a/i386/i386/smp.h b/i386/i386/smp.h new file mode 100644 index 0000000..73d273e --- /dev/null +++ b/i386/i386/smp.h @@ -0,0 +1,34 @@ +/* smp.h - i386 SMP controller for Mach. Header file + Copyright (C) 2020 Free Software Foundation, Inc. + Written by Almudena Garcia Jurado-Centurion + + This file is part of GNU Mach. + + GNU Mach is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Mach is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#ifndef _SMP_H_ +#define _SMP_H_ + +#include + +int smp_init(void); +void smp_remote_ast(unsigned apic_id); +void smp_pmap_update(unsigned apic_id); +int smp_startup_cpu(unsigned apic_id, phys_addr_t start_eip); + +#define cpu_pause() asm volatile ("pause" : : : "memory") +#define STARTUP_VECTOR_SHIFT (20 - 8) + +#endif diff --git a/i386/i386/spl.S b/i386/i386/spl.S new file mode 100644 index 0000000..2f2c8e3 --- /dev/null +++ b/i386/i386/spl.S @@ -0,0 +1,264 @@ +/* + * Copyright (c) 1995 Shantanu Goel + * 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. + * + * THE AUTHOR ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. THE AUTHOR DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + */ + +/* + * spl routines for the i386at. + */ + +#include +#include +#include +#include +#include +#include + +#if NCPUS > 1 +#define mb lock; addl $0,(%esp) +#else +#define mb +#endif + +/* + * Program XEN evt masks from %eax. + */ +#define XEN_SETMASK() \ + pushl %ebx; \ + movl %eax,%ebx; \ + xchgl %eax,hyp_shared_info+EVTMASK; \ + notl %ebx; \ + andl %eax,%ebx; /* Get unmasked events */ \ + testl hyp_shared_info+PENDING, %ebx; \ + popl %ebx; \ + jz 9f; /* Check whether there was some pending */ \ +lock orl $1,hyp_shared_info+CPU_PENDING_SEL; /* Yes, activate it */ \ + movb $1,hyp_shared_info+CPU_PENDING; \ +9: + +ENTRY(spl0) + mb; + CPU_NUMBER(%edx) + movl CX(EXT(curr_ipl),%edx),%eax /* save current ipl */ + pushl %eax + cli /* disable interrupts */ +#ifdef LINUX_DEV + movl EXT(bh_active),%eax + /* get pending mask */ + andl EXT(bh_mask),%eax /* any pending unmasked interrupts? */ + jz 1f /* no, skip */ + call EXT(spl1) /* block further interrupts */ + incl EXT(intr_count) /* set interrupt flag */ + call EXT(linux_soft_intr) /* go handle interrupt */ + decl EXT(intr_count) /* decrement interrupt flag */ + cli /* disable interrupts */ +1: +#endif + cmpl $0,softclkpending /* softclock pending? */ + je 1f /* no, skip */ + movl $0,softclkpending /* clear flag */ + call EXT(spl1) /* block further interrupts */ +#ifdef LINUX_DEV + incl EXT(intr_count) /* set interrupt flag */ +#endif + call EXT(softclock) /* go handle interrupt */ +#ifdef LINUX_DEV + decl EXT(intr_count) /* decrement interrupt flag */ +#endif + cli /* disable interrupts */ +1: + CPU_NUMBER(%edx) + cmpl $(SPL0),CX(EXT(curr_ipl),%edx) /* are we at spl0? */ + je 1f /* yes, all done */ + movl $(SPL0),CX(EXT(curr_ipl),%edx) /* set ipl */ +#ifdef MACH_XEN + movl EXT(int_mask)+SPL0*4,%eax + /* get xen mask */ + XEN_SETMASK() /* program xen evts */ +#endif +1: + sti /* enable interrupts */ + popl %eax /* return previous mask */ + ret + + +/* + * Historically, SETIPL(level) was called + * for spl levels 1-6, now we have combined + * all the intermediate levels into the highest level + * such that interrupts are either on or off, + * since modern hardware can handle it. + * This simplifies the interrupt handling + * especially for the linux drivers. + */ +Entry(splsoftclock) +ENTRY(spl1) +ENTRY(spl2) +ENTRY(spl3) +Entry(splnet) +Entry(splhdw) +ENTRY(spl4) +Entry(splbio) +Entry(spldcm) +ENTRY(spl5) +Entry(spltty) +Entry(splimp) +Entry(splvm) +ENTRY(spl6) +Entry(splclock) +Entry(splsched) +Entry(splhigh) +Entry(splhi) +ENTRY(spl7) + mb; + /* just clear IF */ + cli + CPU_NUMBER(%edx) + movl $SPL7,%eax + xchgl CX(EXT(curr_ipl),%edx),%eax + ret + +ENTRY(splx) + movl S_ARG0,%edx /* get ipl */ + CPU_NUMBER(%eax) +#if (MACH_KDB || MACH_TTD) && !defined(MACH_XEN) + /* First make sure that if we're exitting from ipl7, IF is still cleared */ + cmpl $SPL7,CX(EXT(curr_ipl),%eax) /* from ipl7? */ + jne 0f + pushfl + popl %eax + testl $0x200,%eax /* IF? */ + jz 0f + int3 /* Oops, interrupts got enabled?! */ + +0: +#endif /* (MACH_KDB || MACH_TTD) && !MACH_XEN */ + testl %edx,%edx /* spl0? */ + jz EXT(spl0) /* yes, handle specially */ + CPU_NUMBER(%eax) + cmpl CX(EXT(curr_ipl),%eax),%edx /* same ipl as current? */ + jne spl /* no */ + cmpl $SPL7,%edx /* spl7? */ + je 1f /* to ipl7, don't enable interrupts */ + sti /* ensure interrupts are enabled */ +1: + movl %edx,%eax /* return previous ipl */ + ret + +/* + * Like splx() but returns with interrupts disabled and does + * not return the previous ipl. This should only be called + * when returning from an interrupt. + */ + .align TEXT_ALIGN + .globl splx_cli +splx_cli: + movl S_ARG0,%edx /* get ipl */ + cli /* disable interrupts */ + testl %edx,%edx /* spl0? */ + jnz 2f /* no, skip */ +#ifdef LINUX_DEV + movl EXT(bh_active),%eax + /* get pending mask */ + andl EXT(bh_mask),%eax /* any pending unmasked interrupts? */ + jz 1f /* no, skip */ + call EXT(spl1) /* block further interrupts */ + incl EXT(intr_count) /* set interrupt flag */ + call EXT(linux_soft_intr) /* go handle interrupt */ + decl EXT(intr_count) /* decrement interrupt flag */ + cli /* disable interrupts */ +1: +#endif + cmpl $0,softclkpending /* softclock pending? */ + je 1f /* no, skip */ + movl $0,softclkpending /* clear flag */ + call EXT(spl1) /* block further interrupts */ +#ifdef LINUX_DEV + incl EXT(intr_count) /* set interrupt flag */ +#endif + call EXT(softclock) /* go handle interrupt */ +#ifdef LINUX_DEV + decl EXT(intr_count) /* decrement interrupt flag */ +#endif + cli /* disable interrupts */ +1: + xorl %edx,%edx /* edx = ipl 0 */ +2: + CPU_NUMBER(%eax) + cmpl CX(EXT(curr_ipl),%eax),%edx /* same ipl as current? */ + je 1f /* yes, all done */ + movl %edx,CX(EXT(curr_ipl),%eax) /* set ipl */ +#ifdef MACH_XEN + movl EXT(int_mask)(,%edx,4),%eax + /* get int mask */ + XEN_SETMASK() /* program xen evts with new mask */ +#endif +1: + ret + +/* + * NOTE: This routine must *not* use %ecx, otherwise + * the interrupt code will break. + */ + .align TEXT_ALIGN + .globl spl +spl: + CPU_NUMBER(%eax) +#if (MACH_KDB || MACH_TTD) && !defined(MACH_XEN) + /* First make sure that if we're exitting from ipl7, IF is still cleared */ + cmpl $SPL7,CX(EXT(curr_ipl),%eax) /* from ipl7? */ + jne 0f + pushfl + popl %eax + testl $0x200,%eax /* IF? */ + jz 0f + int3 /* Oops, interrupts got enabled?! */ + +0: +#endif /* (MACH_KDB || MACH_TTD) && !MACH_XEN */ + cmpl $SPL7,%edx /* spl7? */ + je EXT(spl7) /* yes, handle specially */ +#ifdef MACH_XEN + movl EXT(int_mask)(,%edx,4),%eax + /* get int mask */ +#endif + cli /* disable interrupts */ + CPU_NUMBER(%eax) + xchgl CX(EXT(curr_ipl),%eax),%edx /* set ipl */ +#ifdef MACH_XEN + XEN_SETMASK() /* program PICs with new mask */ +#endif + sti /* enable interrupts */ + movl %edx,%eax /* return previous ipl */ + ret + +ENTRY(sploff) + pushfl + popl %eax + cli + ret + +ENTRY(splon) + pushl 4(%esp) + popfl + ret + + .data + .align DATA_ALIGN +softclkpending: + .long 0 + .text + +ENTRY(setsoftclock) + incl softclkpending + ret diff --git a/i386/i386/spl.h b/i386/i386/spl.h new file mode 100644 index 0000000..41ad225 --- /dev/null +++ b/i386/i386/spl.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 1995, 1994, 1993, 1992, 1991, 1990 + * Open Software Foundation, Inc. + * + * 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 + * FOR A PARTICULAR PURPOSE. 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 + */ +/* + * OSF Research Institute MK6.1 (unencumbered) 1/31/1995 + */ + +#ifndef _MACHINE_SPL_H_ +#define _MACHINE_SPL_H_ + +/* + * This file defines the interrupt priority levels used by + * machine-dependent code. + */ + +typedef int spl_t; + +extern spl_t (splhi)(void); + +extern spl_t (spl0)(void); + +extern spl_t (spl1)(void); +extern spl_t (splsoftclock)(void); + +extern spl_t (spl2)(void); + +extern spl_t (spl3)(void); + +extern spl_t (spl4)(void); +extern spl_t (splnet)(void); +extern spl_t (splhdw)(void); + +extern spl_t (spl5)(void); +extern spl_t (splbio)(void); +extern spl_t (spldcm)(void); + +extern spl_t (spl6)(void); +extern spl_t (spltty)(void); +extern spl_t (splimp)(void); +extern spl_t (splvm)(void); + +extern spl_t (spl7)(void); +extern spl_t (splclock)(void); +extern spl_t (splsched)(void); +#define assert_splsched() assert(splsched() == SPL7) +extern spl_t (splhigh)(void); + +extern spl_t (splx)(spl_t n); +extern spl_t (splx_cli)(spl_t n); + +extern void splon (unsigned long n); + +extern unsigned long sploff (void); + +extern void setsoftclock (void); +extern int spl_init; + +/* XXX Include each other... */ +#include + +#endif /* _MACHINE_SPL_H_ */ diff --git a/i386/i386/strings.c b/i386/i386/strings.c new file mode 100644 index 0000000..f1752de --- /dev/null +++ b/i386/i386/strings.c @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2014 Richard Braun. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include + +#define ARCH_STRING_MEMCPY +#define ARCH_STRING_MEMMOVE +#define ARCH_STRING_MEMSET +#define ARCH_STRING_MEMCMP + +#ifdef ARCH_STRING_MEMCPY +void * +memcpy(void *dest, const void *src, size_t n) +{ + void *orig_dest; + + orig_dest = dest; + asm volatile("rep movsb" + : "+D" (dest), "+S" (src), "+c" (n) + : : "memory"); + return orig_dest; +} +#endif /* ARCH_STRING_MEMCPY */ + +#ifdef ARCH_STRING_MEMMOVE +void * +memmove(void *dest, const void *src, size_t n) +{ + void *orig_dest; + + orig_dest = dest; + + if (dest <= src) + asm volatile("rep movsb" + : "+D" (dest), "+S" (src), "+c" (n) + : : "memory"); + else { + dest += n - 1; + src += n - 1; + asm volatile("std; rep movsb; cld" + : "+D" (dest), "+S" (src), "+c" (n) + : : "memory"); + } + + return orig_dest; +} +#endif /* ARCH_STRING_MEMMOVE */ + +#ifdef ARCH_STRING_MEMSET +void * +memset(void *s, int c, size_t n) +{ + void *orig_s; + + orig_s = s; + asm volatile("rep stosb" + : "+D" (s), "+c" (n) + : "a" (c) + : "memory"); + return orig_s; +} +#endif /* ARCH_STRING_MEMSET */ + +#ifdef ARCH_STRING_MEMCMP +int +memcmp(const void *s1, const void *s2, size_t n) +{ + unsigned char c1, c2; + + if (n == 0) + return 0; + + asm volatile("repe cmpsb" + : "+D" (s1), "+S" (s2), "+c" (n) + : : "memory"); + c1 = *(((const unsigned char *)s1) - 1); + c2 = *(((const unsigned char *)s2) - 1); + return (int)c1 - (int)c2; +} +#endif /* ARCH_STRING_MEMCMP */ diff --git a/i386/i386/task.h b/i386/i386/task.h new file mode 100644 index 0000000..0060ad4 --- /dev/null +++ b/i386/i386/task.h @@ -0,0 +1,61 @@ +/* Data types for machine specific parts of tasks on i386. + + Copyright (C) 2002, 2007 Free Software Foundation, Inc. + + Written by Marcus Brinkmann. Glued into GNU Mach by Thomas Schwinge. + + This file is part of GNU Mach. + + GNU Mach is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any later + version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _I386_TASK_H_ +#define _I386_TASK_H_ + +#include +#include + +/* The machine specific data of a task. */ +struct machine_task +{ + /* A lock protecting iopb_size and iopb. */ + decl_simple_lock_data (, iopb_lock); + + /* The highest I/O port number enabled. */ + int iopb_size; + + /* The I/O permission bitmap. */ + unsigned char *iopb; +}; +typedef struct machine_task machine_task_t; + + +extern struct kmem_cache machine_task_iopb_cache; + +/* Initialize the machine task module. The function is called once at + start up by task_init in kern/task.c. */ +void machine_task_module_init (void); + +/* Initialize the machine specific part of task TASK. */ +void machine_task_init (task_t); + +/* Destroy the machine specific part of task TASK and release all + associated resources. */ +void machine_task_terminate (task_t); + +/* Try to release as much memory from the machine specific data in + task TASK. */ +void machine_task_collect (task_t); + +#endif /* _I386_TASK_H_ */ diff --git a/i386/i386/thread.h b/i386/i386/thread.h new file mode 100644 index 0000000..9c88d09 --- /dev/null +++ b/i386/i386/thread.h @@ -0,0 +1,276 @@ +/* + * 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. + */ +/* + * File: machine/thread.h + * + * This file contains the structure definitions for the thread + * state as applied to I386 processors. + */ + +#ifndef _I386_THREAD_H_ +#define _I386_THREAD_H_ + +#include +#include +#include +#include + +#include + +#include "gdt.h" + +/* + * i386_saved_state: + * + * This structure corresponds to the state of user registers + * as saved upon kernel entry. It lives in the pcb. + * It is also pushed onto the stack for exceptions in the kernel. + */ + +struct i386_saved_state { +#if !defined(__x86_64__) || defined(USER32) + unsigned long gs; + unsigned long fs; + unsigned long es; + unsigned long ds; +#endif +#ifdef __x86_64__ + unsigned long r15; + unsigned long r14; + unsigned long r13; + unsigned long r12; + unsigned long r11; + unsigned long r10; + unsigned long r9; + unsigned long r8; +#endif + unsigned long edi; + unsigned long esi; + unsigned long ebp; + unsigned long cr2; /* kernel esp stored by pusha - + we save cr2 here later */ + unsigned long ebx; + unsigned long edx; + unsigned long ecx; + unsigned long eax; + unsigned long trapno; + unsigned long err; + unsigned long eip; + unsigned long cs; + unsigned long efl; + unsigned long uesp; + unsigned long ss; +#if !defined(__x86_64__) || defined(USER32) + struct v86_segs { + unsigned long v86_es; /* virtual 8086 segment registers */ + unsigned long v86_ds; + unsigned long v86_fs; + unsigned long v86_gs; + } v86_segs; +#endif +}; + +/* + * i386_exception_link: + * + * This structure lives at the high end of the kernel stack. + * It points to the current thread`s user registers. + */ +struct i386_exception_link { + struct i386_saved_state *saved_state; +}; + +/* + * i386_kernel_state: + * + * This structure corresponds to the state of kernel registers + * as saved in a context-switch. It lives at the base of the stack. + */ + +struct i386_kernel_state { + long k_ebx; /* kernel context */ + long k_esp; + long k_ebp; +#ifdef __i386__ + long k_edi; + long k_esi; +#endif + long k_eip; +#ifdef __x86_64__ + long k_r12; + long k_r13; + long k_r14; + long k_r15; +#endif +}; + +/* + * Save area for user floating-point state. + * Allocated only when necessary. + */ + +struct i386_fpsave_state { + boolean_t fp_valid; + + union { + struct { + struct i386_fp_save fp_save_state; + struct i386_fp_regs fp_regs; + }; + struct i386_xfp_save xfp_save_state; + }; +}; + +#if !defined(__x86_64__) || defined(USER32) +/* + * v86_assist_state: + * + * This structure provides data to simulate 8086 mode + * interrupts. It lives in the pcb. + */ + +struct v86_assist_state { + vm_offset_t int_table; + unsigned short int_count; + unsigned short flags; /* 8086 flag bits */ +}; +#define V86_IF_PENDING 0x8000 /* unused bit */ +#endif + +#if defined(__x86_64__) && !defined(USER32) +struct i386_segment_base_state { + unsigned long fsbase; + unsigned long gsbase; +}; +#endif + +/* + * i386_interrupt_state: + * + * This structure describes the set of registers that must + * be pushed on the current ring-0 stack by an interrupt before + * we can switch to the interrupt stack. + */ + +struct i386_interrupt_state { +#if !defined(__x86_64__) || defined(USER32) + long gs; + long fs; + long es; + long ds; +#endif +#ifdef __x86_64__ + long r11; + long r10; + long r9; + long r8; + long rdi; + long rsi; +#endif + long edx; + long ecx; + long eax; + long eip; + long cs; + long efl; +}; + +/* + * i386_machine_state: + * + * This structure corresponds to special machine state. + * It lives in the pcb. It is not saved by default. + */ + +struct i386_machine_state { + struct user_ldt * ldt; + struct i386_fpsave_state *ifps; +#if !defined(__x86_64__) || defined(USER32) + struct v86_assist_state v86s; +#endif + struct real_descriptor user_gdt[USER_GDT_SLOTS]; + struct i386_debug_state ids; +#if defined(__x86_64__) && !defined(USER32) + struct i386_segment_base_state sbs; +#endif +}; + +typedef struct pcb { + /* START of the exception stack. + * NOTE: this area is used as exception stack when switching + * CPL, and it MUST be big enough to save the thread state and + * switch to a proper stack area, even considering recursive + * exceptions, otherwise it could corrupt nearby memory */ + struct i386_interrupt_state iis[2]; /* interrupt and NMI */ +#ifdef __x86_64__ + unsigned long pad; /* ensure exception stack is aligned to 16 */ +#endif + struct i386_saved_state iss; + /* END of exception stack*/ + struct i386_machine_state ims; + decl_simple_lock_data(, lock) + unsigned short init_control; /* Initial FPU control to set */ +#ifdef LINUX_DEV + void *data; +#endif /* LINUX_DEV */ +} *pcb_t; + +/* + * On the kernel stack is: + * stack: ... + * struct i386_exception_link + * struct i386_kernel_state + * stack+KERNEL_STACK_SIZE + */ + +#define STACK_IKS(stack) \ + ((struct i386_kernel_state *)((stack) + KERNEL_STACK_SIZE) - 1) +#define STACK_IEL(stack) \ + ((struct i386_exception_link *)STACK_IKS(stack) - 1) + +#ifdef __x86_64__ +#define KERNEL_STACK_ALIGN 16 +#else +#define KERNEL_STACK_ALIGN 4 +#endif + +#if defined(__x86_64__) && !defined(USER32) +/* Follow System V AMD64 ABI guidelines. */ +#define USER_STACK_ALIGN 16 +#else +#define USER_STACK_ALIGN 4 +#endif + +#define USER_REGS(thread) (&(thread)->pcb->iss) + + +#define syscall_emulation_sync(task) /* do nothing */ + + +/* #include_next "thread.h" */ + + +#endif /* _I386_THREAD_H_ */ diff --git a/i386/i386/time_stamp.h b/i386/i386/time_stamp.h new file mode 100644 index 0000000..43bb956 --- /dev/null +++ b/i386/i386/time_stamp.h @@ -0,0 +1,30 @@ +/* + * 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. + */ +/* + * The i386 timestamp implementation uses the default, so we don't + * need to do anything here. + */ + diff --git a/i386/i386/trap.c b/i386/i386/trap.c new file mode 100644 index 0000000..db4c702 --- /dev/null +++ b/i386/i386/trap.c @@ -0,0 +1,675 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988 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. + */ +/* + * Hardware trap/fault handler. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include /* for spl_t */ +#include + +#include +#include +#include "vm_param.h" +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#if MACH_KDB +#include +#include +#include +#endif + +#include "debug.h" + +#if MACH_KDB +boolean_t debug_all_traps_with_kdb = FALSE; +extern struct db_watchpoint *db_watchpoint_list; +extern boolean_t db_watchpoints_inserted; + +void +thread_kdb_return(void) +{ + thread_t thread = current_thread(); + struct i386_saved_state *regs = USER_REGS(thread); + + if (kdb_trap(regs->trapno, regs->err, regs)) { + thread_exception_return(); + /*NOTREACHED*/ + } +} +#endif /* MACH_KDB */ + +#if MACH_TTD +extern boolean_t kttd_enabled; +boolean_t debug_all_traps_with_kttd = TRUE; +#endif /* MACH_TTD */ + +static void +user_page_fault_continue(kern_return_t kr) +{ + thread_t thread = current_thread(); + struct i386_saved_state *regs = USER_REGS(thread); + + if (kr == KERN_SUCCESS) { +#if MACH_KDB + if (db_watchpoint_list && + db_watchpoints_inserted && + (regs->err & T_PF_WRITE) && + db_find_watchpoint(thread->task->map, + (vm_offset_t)regs->cr2, + regs)) + kdb_trap(T_WATCHPOINT, 0, regs); +#endif /* MACH_KDB */ + thread_exception_return(); + /*NOTREACHED*/ + } + +#if MACH_KDB + if (debug_all_traps_with_kdb && + kdb_trap(regs->trapno, regs->err, regs)) { + thread_exception_return(); + /*NOTREACHED*/ + } +#endif /* MACH_KDB */ + + i386_exception(EXC_BAD_ACCESS, kr, regs->cr2); + /*NOTREACHED*/ +} + + +static char *trap_type[] = { + "Divide error", + "Debug trap", + "NMI", + "Breakpoint", + "Overflow", + "Bounds check", + "Invalid opcode", + "No coprocessor", + "Double fault", + "Coprocessor overrun", + "Invalid TSS", + "Segment not present", + "Stack bounds", + "General protection", + "Page fault", + "(reserved)", + "Coprocessor error" +}; +#define TRAP_TYPES (sizeof(trap_type)/sizeof(trap_type[0])) + +char *trap_name(unsigned int trapnum) +{ + return trapnum < TRAP_TYPES ? trap_type[trapnum] : "(unknown)"; +} + +/* + * Trap from kernel mode. Only page-fault errors are recoverable, + * and then only in special circumstances. All other errors are + * fatal. + */ +void kernel_trap(struct i386_saved_state *regs) +{ + unsigned long code; + unsigned long subcode; + unsigned long type; + vm_map_t map; + kern_return_t result; + thread_t thread; + extern char _start[], etext[]; + + type = regs->trapno; + code = regs->err; + thread = current_thread(); + +#if 0 +((short*)0xb8700)[0] = 0x0f00+'K'; +((short*)0xb8700)[1] = 0x0f30+(type / 10); +((short*)0xb8700)[2] = 0x0f30+(type % 10); +#endif +#if 0 +printf("kernel trap %d error %d\n", (int) type, (int) code); +dump_ss(regs); +#endif + + switch (type) { + case T_NO_FPU: + fpnoextflt(); + return; + + case T_FPU_FAULT: + fpextovrflt(); + return; + + case T_FLOATING_POINT_ERROR: + fpexterrflt(); + return; + + case T_PAGE_FAULT: + + /* Get faulting linear address */ + subcode = regs->cr2; +#if 0 + printf("kernel page fault at linear address %08x\n", subcode); +#endif + + /* If it's in the kernel linear address region, + convert it to a kernel virtual address + and use the kernel map to process the fault. */ + if (lintokv(subcode) == 0 || + subcode >= LINEAR_MIN_KERNEL_ADDRESS) { +#if 0 + printf("%08x in kernel linear address range\n", subcode); +#endif + map = kernel_map; + subcode = lintokv(subcode); +#if 0 + printf("now %08x\n", subcode); +#endif + if (trunc_page(subcode) == 0 + || (subcode >= (long)_start + && subcode < (long)etext)) { + printf("Kernel page fault at address 0x%lx, " + "eip = 0x%lx\n", + subcode, regs->eip); + goto badtrap; + } + } else { + if (thread) + map = thread->task->map; + if (!thread || map == kernel_map) { + printf("kernel page fault at %08lx:\n", subcode); + dump_ss(regs); + panic("kernel thread accessed user space!\n"); + } + } + + /* + * Since the 386 ignores write protection in + * kernel mode, always try for write permission + * first. If that fails and the fault was a + * read fault, retry with read permission. + */ + result = vm_fault(map, + trunc_page((vm_offset_t)subcode), +#if !(__i486__ || __i586__ || __i686__) + VM_PROT_READ|VM_PROT_WRITE, +#else + (code & T_PF_WRITE) + ? VM_PROT_READ|VM_PROT_WRITE + : VM_PROT_READ, +#endif + FALSE, + FALSE, + (void (*)()) 0); +#if MACH_KDB + if (result == KERN_SUCCESS) { + /* Look for watchpoints */ + if (db_watchpoint_list && + db_watchpoints_inserted && + (code & T_PF_WRITE) && + db_find_watchpoint(map, + (vm_offset_t)subcode, regs)) + kdb_trap(T_WATCHPOINT, 0, regs); + } + else +#endif /* MACH_KDB */ +#if !(__i486__ || __i586__ || __i686__) + if ((code & T_PF_WRITE) == 0 && + result == KERN_PROTECTION_FAILURE) + { + /* + * Must expand vm_fault by hand, + * so that we can ask for read-only access + * but enter a (kernel)writable mapping. + */ + result = intel_read_fault(map, + trunc_page((vm_offset_t)subcode)); + } +#else + ; +#endif + + if (result == KERN_SUCCESS) { + /* + * Certain faults require that we back up + * the EIP. + */ + struct recovery *rp; + + /* Linear searching; but the list is small enough. */ + for (rp = retry_table; rp < retry_table_end; rp++) { + if (regs->eip == rp->fault_addr) { + regs->eip = rp->recover_addr; + break; + } + } + return; + } + + /* + * If there is a failure recovery address + * for this fault, go there. + */ + { + struct recovery *rp; + + /* Linear searching; but the list is small enough. */ + for (rp = recover_table; + rp < recover_table_end; + rp++) { + if (regs->eip == rp->fault_addr) { + regs->eip = rp->recover_addr; + return; + } + } + } + + /* + * Check thread recovery address also - + * v86 assist uses it. + */ + if (thread->recover) { + regs->eip = thread->recover; + thread->recover = 0; + return; + } + + /* + * Unanticipated page-fault errors in kernel + * should not happen. + */ + /* fall through */ + + default: + badtrap: + printf("Kernel "); + if (type < TRAP_TYPES) + printf("%s trap", trap_type[type]); + else + printf("trap %ld", type); + printf(", eip 0x%lx, code %lx, cr2 %lx\n", regs->eip, code, regs->cr2); +#if MACH_TTD + if (kttd_enabled && kttd_trap(type, code, regs)) + return; +#endif /* MACH_TTD */ +#if MACH_KDB + if (kdb_trap(type, code, regs)) + return; +#endif /* MACH_KDB */ + splhigh(); + printf("kernel trap, type %ld, code = %lx\n", + type, code); + dump_ss(regs); + panic("trap"); + return; + } +} + + +/* + * Trap from user mode. + * Return TRUE if from emulated system call. + */ +int user_trap(struct i386_saved_state *regs) +{ + int exc = 0; /* Suppress gcc warning */ + unsigned long code; + unsigned long subcode; + unsigned long type; + thread_t thread = current_thread(); + +#ifdef __x86_64__ + assert(regs == &thread->pcb->iss); +#endif + + type = regs->trapno; + code = 0; + subcode = 0; + +#if 0 + ((short*)0xb8700)[3] = 0x0f00+'U'; + ((short*)0xb8700)[4] = 0x0f30+(type / 10); + ((short*)0xb8700)[5] = 0x0f30+(type % 10); +#endif +#if 0 + printf("user trap %d error %d\n", type, code); + dump_ss(regs); +#endif + + switch (type) { + + case T_DIVIDE_ERROR: + exc = EXC_ARITHMETIC; + code = EXC_I386_DIV; + break; + + case T_DEBUG: +#if MACH_TTD + if (kttd_enabled && kttd_in_single_step()) { + if (kttd_trap(type, regs->err, regs)) + return 0; + } +#endif /* MACH_TTD */ +#if MACH_KDB + if (db_in_single_step()) { + if (kdb_trap(type, regs->err, regs)) + return 0; + } +#endif /* MACH_KDB */ + /* Make the content of the debug status register (DR6) + available to user space. */ + if (thread->pcb) + thread->pcb->ims.ids.dr[6] = get_dr6() & 0x600F; + set_dr6(0); + exc = EXC_BREAKPOINT; + code = EXC_I386_SGL; + break; + + case T_INT3: +#if MACH_TTD + if (kttd_enabled && kttd_trap(type, regs->err, regs)) + return 0; + break; +#endif /* MACH_TTD */ +#if MACH_KDB + { + if (db_find_breakpoint_here( + (current_thread())? current_thread()->task: TASK_NULL, + regs->eip - 1)) { + if (kdb_trap(type, regs->err, regs)) + return 0; + } + } +#endif /* MACH_KDB */ + exc = EXC_BREAKPOINT; + code = EXC_I386_BPT; + break; + + case T_OVERFLOW: + exc = EXC_ARITHMETIC; + code = EXC_I386_INTO; + break; + + case T_OUT_OF_BOUNDS: + exc = EXC_SOFTWARE; + code = EXC_I386_BOUND; + break; + + case T_INVALID_OPCODE: + exc = EXC_BAD_INSTRUCTION; + code = EXC_I386_INVOP; + break; + + case T_NO_FPU: + case 32: /* XXX */ + fpnoextflt(); + return 0; + + case T_FPU_FAULT: + fpextovrflt(); + return 0; + + case 10: /* invalid TSS == iret with NT flag set */ + exc = EXC_BAD_INSTRUCTION; + code = EXC_I386_INVTSSFLT; + subcode = regs->err & 0xffff; + break; + + case T_SEGMENT_NOT_PRESENT: + exc = EXC_BAD_INSTRUCTION; + code = EXC_I386_SEGNPFLT; + subcode = regs->err & 0xffff; + break; + + case T_STACK_FAULT: + exc = EXC_BAD_INSTRUCTION; + code = EXC_I386_STKFLT; + subcode = regs->err & 0xffff; + break; + + case T_GENERAL_PROTECTION: + /* Check for an emulated int80 system call. + NetBSD-current and Linux use trap instead of call gate. */ + if (thread->task->eml_dispatch) { + unsigned char opcode, intno; + + opcode = inst_fetch(regs->eip, regs->cs); + intno = inst_fetch(regs->eip+1, regs->cs); + if (opcode == 0xcd && intno == 0x80) { + regs->eip += 2; + return 1; + } + } +#ifdef __x86_64__ + { + unsigned char opcode, addr[4], seg[2]; + int i; + + opcode = inst_fetch(regs->eip, regs->cs); + for (i = 0; i < 4; i++) + addr[i] = inst_fetch(regs->eip+i+1, regs->cs); + (void) addr; + for (i = 0; i < 2; i++) + seg[i] = inst_fetch(regs->eip+i+5, regs->cs); + if (opcode == 0x9a && seg[0] == 0x7 && seg[1] == 0) { + regs->eip += 7; + return 1; + } + } +#endif + exc = EXC_BAD_INSTRUCTION; + code = EXC_I386_GPFLT; + subcode = regs->err & 0xffff; + break; + + case T_PAGE_FAULT: + subcode = regs->cr2; +#if 0 + printf("user page fault at linear address %08x\n", subcode); + dump_ss (regs); + +#endif + if (subcode >= LINEAR_MIN_KERNEL_ADDRESS) + i386_exception(EXC_BAD_ACCESS, EXC_I386_PGFLT, subcode); + (void) vm_fault(thread->task->map, + trunc_page((vm_offset_t)subcode), + (regs->err & T_PF_WRITE) + ? VM_PROT_READ|VM_PROT_WRITE + : VM_PROT_READ, + FALSE, + FALSE, + user_page_fault_continue); + /*NOTREACHED*/ + break; + +#ifdef MACH_PV_PAGETABLES + case 15: + { + static unsigned count = 0; + count++; + if (!(count % 10000)) + printf("%d 4gb segments accesses\n", count); + if (count > 1000000) { + printf("A million 4gb segment accesses, stopping reporting them."); + if (hyp_vm_assist(VMASST_CMD_disable, VMASST_TYPE_4gb_segments_notify)) + panic("couldn't disable 4gb segments vm assist notify"); + } + return 0; + } +#endif /* MACH_PV_PAGETABLES */ + + case T_FLOATING_POINT_ERROR: + fpexterrflt(); + return 0; + + default: +#if MACH_TTD + if (kttd_enabled && kttd_trap(type, regs->err, regs)) + return 0; +#endif /* MACH_TTD */ +#if MACH_KDB + if (kdb_trap(type, regs->err, regs)) + return 0; +#endif /* MACH_KDB */ + splhigh(); + printf("user trap, type %ld, code = %lx\n", + type, regs->err); + dump_ss(regs); + panic("trap"); + return 0; + } + +#if MACH_TTD + if ((debug_all_traps_with_kttd || thread->task->essential) && + kttd_trap(type, regs->err, regs)) + return 0; +#endif /* MACH_TTD */ +#if MACH_KDB + if ((debug_all_traps_with_kdb || thread->task->essential) && + kdb_trap(type, regs->err, regs)) + return 0; +#endif /* MACH_KDB */ + + i386_exception(exc, code, subcode); + /*NOTREACHED*/ +} + +#define V86_IRET_PENDING 0x4000 + +/* + * Handle AST traps for i386. + * Check for delayed floating-point exception from + * AT-bus machines. + */ +void +i386_astintr(void) +{ + (void) splsched(); /* block interrupts to check reasons */ +#ifndef MACH_RING1 + int mycpu = cpu_number(); + + if (need_ast[mycpu] & AST_I386_FP) { + /* + * AST was for delayed floating-point exception - + * FP interrupt occurred while in kernel. + * Turn off this AST reason and handle the FPU error. + */ + ast_off(mycpu, AST_I386_FP); + (void) spl0(); + + fpastintr(); + } + else +#endif /* MACH_RING1 */ + { + /* + * Not an FPU trap. Handle the AST. + * Interrupts are still blocked. + */ + ast_taken(); + } +} + +/* + * Handle exceptions for i386. + * + * If we are an AT bus machine, we must turn off the AST for a + * delayed floating-point exception. + * + * If we are providing floating-point emulation, we may have + * to retrieve the real register values from the floating point + * emulator. + */ +void +i386_exception( + int exc, + int code, + long subcode) +{ + spl_t s; + + /* + * Turn off delayed FPU error handling. + */ + s = splsched(); + ast_off(cpu_number(), AST_I386_FP); + splx(s); + + exception(exc, code, subcode); + /*NOTREACHED*/ +} + +#if MACH_PCSAMPLE > 0 +/* + * return saved state for interrupted user thread + */ +unsigned +interrupted_pc(const thread_t t) +{ + struct i386_saved_state *iss; + + iss = USER_REGS(t); + return iss->eip; +} +#endif /* MACH_PCSAMPLE > 0 */ + +#if MACH_KDB + +void +db_debug_all_traps (boolean_t enable) +{ + debug_all_traps_with_kdb = enable; +} + +#endif /* MACH_KDB */ + +void handle_double_fault(struct i386_saved_state *regs) +{ + dump_ss(regs); + panic("DOUBLE FAULT! This is critical\n"); +} diff --git a/i386/i386/trap.h b/i386/i386/trap.h new file mode 100644 index 0000000..db22273 --- /dev/null +++ b/i386/i386/trap.h @@ -0,0 +1,71 @@ +/* + * 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. + */ + +#ifndef _I386_TRAP_H_ +#define _I386_TRAP_H_ + +#include + +#ifndef __ASSEMBLER__ +#include +#include + +char *trap_name(unsigned int trapnum); + +unsigned int interrupted_pc(thread_t); + +void +i386_exception( + int exc, + int code, + long subcode) __attribute__ ((noreturn)); + +extern void +thread_kdb_return(void); + +/* + * Trap from kernel mode. Only page-fault errors are recoverable, + * and then only in special circumstances. All other errors are + * fatal. + */ +void kernel_trap(struct i386_saved_state *regs); + +/* + * Trap from user mode. + * Return TRUE if from emulated system call. + */ +int user_trap(struct i386_saved_state *regs); + +/* + * Handle AST traps for i386. + * Check for delayed floating-point exception from + * AT-bus machines. + */ +void i386_astintr(void); + +#endif /* !__ASSEMBLER__ */ + +#endif /* _I386_TRAP_H_ */ diff --git a/i386/i386/tss.h b/i386/i386/tss.h new file mode 100644 index 0000000..fd7e714 --- /dev/null +++ b/i386/i386/tss.h @@ -0,0 +1,109 @@ +/* + * 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. + */ + +#ifndef _I386_TSS_H_ +#define _I386_TSS_H_ + +#include +#include + +#include + +/* + * x86 Task State Segment + */ +#ifdef __x86_64__ +struct i386_tss { + uint32_t _reserved0; + uint64_t rsp0; + uint64_t rsp1; + uint64_t rsp2; + uint64_t _reserved1; + uint64_t ist1; + uint64_t ist2; + uint64_t ist3; + uint64_t ist4; + uint64_t ist5; + uint64_t ist6; + uint64_t ist7; + uint64_t _reserved2; + uint16_t _reserved3; + uint16_t io_bit_map_offset; +} __attribute__((__packed__)); +#else /* ! __x86_64__ */ +struct i386_tss { + int back_link; /* segment number of previous task, + if nested */ + int esp0; /* initial stack pointer ... */ + int ss0; /* and segment for ring 0 */ + int esp1; /* initial stack pointer ... */ + int ss1; /* and segment for ring 1 */ + int esp2; /* initial stack pointer ... */ + int ss2; /* and segment for ring 2 */ + int cr3; /* CR3 - page table directory + physical address */ + int eip; + int eflags; + int eax; + int ecx; + int edx; + int ebx; + int esp; /* current stack pointer */ + int ebp; + int esi; + int edi; + int es; + int cs; + int ss; /* current stack segment */ + int ds; + int fs; + int gs; + int ldt; /* local descriptor table segment */ + unsigned short trace_trap; /* trap on switch to this task */ + unsigned short io_bit_map_offset; + /* offset to start of IO permission + bit map */ +}; +#endif /* __x86_64__ */ + +/* The structure extends the above TSS structure by an I/O permission bitmap + and the barrier. */ +struct task_tss + { + struct i386_tss tss; + unsigned char iopb[IOPB_BYTES]; + unsigned char barrier; +}; + + +/* Load the current task register. */ +static inline void +ltr(unsigned short segment) +{ + __asm volatile("ltr %0" : : "r" (segment) : "memory"); +} + +#endif /* _I386_TSS_H_ */ diff --git a/i386/i386/user_ldt.c b/i386/i386/user_ldt.c new file mode 100644 index 0000000..4c89bd4 --- /dev/null +++ b/i386/i386/user_ldt.c @@ -0,0 +1,451 @@ +/* + * Mach Operating System + * Copyright (c) 1994,1993,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. + */ +/* + * User LDT management. + * Each thread in a task may have its own LDT. + */ + +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include "ldt.h" +#include "vm_param.h" + +/* + * Add the descriptors to the LDT, starting with + * the descriptor for 'first_selector'. + */ +kern_return_t +i386_set_ldt( + thread_t thread, + int first_selector, + const struct descriptor *descriptor_list, + unsigned int count, + boolean_t desc_list_inline) +{ + struct real_descriptor* desc_list = (struct real_descriptor *)descriptor_list; + user_ldt_t new_ldt, old_ldt, temp; + struct real_descriptor *dp; + unsigned i; + unsigned min_selector = 0; + pcb_t pcb; + vm_size_t ldt_size_needed; + unsigned first_desc = sel_idx(first_selector); + vm_map_copy_t old_copy_object = NULL; /* Suppress gcc warning */ + + if (thread == THREAD_NULL) + return KERN_INVALID_ARGUMENT; + if (thread == current_thread()) + min_selector = LDTSZ; + if (first_desc < min_selector || first_desc > 8191) + return KERN_INVALID_ARGUMENT; + if (first_desc + count >= 8192) + return KERN_INVALID_ARGUMENT; + + /* + * If desc_list is not inline, it is in copyin form. + * We must copy it out to the kernel map, and wire + * it down (we touch it while the PCB is locked). + * + * We make a copy of the copyin object, and clear + * out the old one, so that returning KERN_INVALID_ARGUMENT + * will not try to deallocate the data twice. + */ + if (!desc_list_inline) { + kern_return_t kr; + vm_offset_t dst_addr; + + old_copy_object = (vm_map_copy_t) desc_list; + + kr = vm_map_copyout(ipc_kernel_map, &dst_addr, + vm_map_copy_copy(old_copy_object)); + if (kr != KERN_SUCCESS) + return kr; + + (void) vm_map_pageable(ipc_kernel_map, + dst_addr, + dst_addr + count * sizeof(struct real_descriptor), + VM_PROT_READ|VM_PROT_WRITE, TRUE, TRUE); + desc_list = (struct real_descriptor *)dst_addr; + } + + for (i = 0, dp = desc_list; + i < count; + i++, dp++) + { + switch (dp->access & ~ACC_A) { + case 0: + case ACC_P: + /* valid empty descriptor */ + break; + case ACC_P | ACC_CALL_GATE: + /* Mach kernel call */ + *dp = *(struct real_descriptor *) + &ldt[sel_idx(USER_SCALL)]; + break; + case ACC_P | ACC_PL_U | ACC_DATA: + case ACC_P | ACC_PL_U | ACC_DATA_W: + case ACC_P | ACC_PL_U | ACC_DATA_E: + case ACC_P | ACC_PL_U | ACC_DATA_EW: + case ACC_P | ACC_PL_U | ACC_CODE: + case ACC_P | ACC_PL_U | ACC_CODE_R: + case ACC_P | ACC_PL_U | ACC_CODE_C: + case ACC_P | ACC_PL_U | ACC_CODE_CR: + case ACC_P | ACC_PL_U | ACC_CALL_GATE_16: + case ACC_P | ACC_PL_U | ACC_CALL_GATE: + break; + default: + return KERN_INVALID_ARGUMENT; + } + } + ldt_size_needed = sizeof(struct real_descriptor) + * (first_desc + count); + + pcb = thread->pcb; + new_ldt = 0; + Retry: + simple_lock(&pcb->lock); + old_ldt = pcb->ims.ldt; + if (old_ldt == 0 || + old_ldt->desc.limit_low + 1 < ldt_size_needed) + { + /* + * No old LDT, or not big enough + */ + if (new_ldt == 0) { + simple_unlock(&pcb->lock); + +#ifdef MACH_PV_DESCRIPTORS + /* LDT needs to be aligned on a page */ + vm_offset_t alloc = kalloc(ldt_size_needed + PAGE_SIZE + offsetof(struct user_ldt, ldt)); + new_ldt = (user_ldt_t) (round_page((alloc + offsetof(struct user_ldt, ldt))) - offsetof(struct user_ldt, ldt)); + new_ldt->alloc = alloc; + +#else /* MACH_PV_DESCRIPTORS */ + new_ldt = (user_ldt_t) + kalloc(ldt_size_needed + + sizeof(struct real_descriptor)); +#endif /* MACH_PV_DESCRIPTORS */ + /* + * Build a descriptor that describes the + * LDT itself + */ + { + vm_offset_t ldt_base; + + ldt_base = kvtolin(&new_ldt->ldt[0]); + + new_ldt->desc.limit_low = ldt_size_needed - 1; + new_ldt->desc.limit_high = 0; + new_ldt->desc.base_low = ldt_base & 0xffff; + new_ldt->desc.base_med = (ldt_base >> 16) & 0xff; + new_ldt->desc.base_high = ldt_base >> 24; + new_ldt->desc.access = ACC_P | ACC_LDT; + new_ldt->desc.granularity = 0; + } + + goto Retry; + } + + /* + * Have new LDT. If there was a an old ldt, copy descriptors + * from old to new. Otherwise copy the default ldt. + */ + if (old_ldt) { + memcpy(&new_ldt->ldt[0], + &old_ldt->ldt[0], + old_ldt->desc.limit_low + 1); + } + else { + struct real_descriptor template = {0, 0, 0, ACC_P, 0, 0 ,0}; + + for (dp = &new_ldt->ldt[0], i = 0; i < first_desc; i++, dp++) { + if (i < LDTSZ) + *dp = *(struct real_descriptor *) &ldt[i]; + else + *dp = template; + } + } + + temp = old_ldt; + old_ldt = new_ldt; /* use new LDT from now on */ + new_ldt = temp; /* discard old LDT */ + + pcb->ims.ldt = old_ldt; /* set LDT for thread */ + + /* + * If we are modifying the LDT for the current thread, + * make sure it is properly set. + */ + if (thread == current_thread()) + switch_ktss(pcb); + } + + /* + * Install new descriptors. + */ + memcpy(&old_ldt->ldt[first_desc], + desc_list, + count * sizeof(struct real_descriptor)); + + simple_unlock(&pcb->lock); + + if (new_ldt) +#ifdef MACH_PV_DESCRIPTORS + { +#ifdef MACH_PV_PAGETABLES + for (i=0; i<(new_ldt->desc.limit_low + 1)/sizeof(struct real_descriptor); i+=PAGE_SIZE/sizeof(struct real_descriptor)) + pmap_set_page_readwrite(&new_ldt->ldt[i]); +#endif /* MACH_PV_PAGETABLES*/ + kfree(new_ldt->alloc, new_ldt->desc.limit_low + 1 + + PAGE_SIZE + offsetof(struct user_ldt, ldt)); + } +#else /* MACH_PV_DESCRIPTORS */ + kfree((vm_offset_t)new_ldt, + new_ldt->desc.limit_low + 1 + + sizeof(struct real_descriptor)); +#endif /* MACH_PV_DESCRIPTORS */ + + /* + * Free the descriptor list, if it was + * out-of-line. Also discard the original + * copy object for it. + */ + if (!desc_list_inline) { + (void) kmem_free(ipc_kernel_map, + (vm_offset_t) desc_list, + count * sizeof(struct real_descriptor)); + vm_map_copy_discard(old_copy_object); + } + + return KERN_SUCCESS; +} + +kern_return_t +i386_get_ldt(const thread_t thread, + int first_selector, + int selector_count, /* number wanted */ + struct descriptor **descriptor_list, /* in/out */ + unsigned int *count /* in/out */ + ) +{ + struct real_descriptor** desc_list = (struct real_descriptor **)descriptor_list; + struct user_ldt *user_ldt; + pcb_t pcb; + int first_desc = sel_idx(first_selector); + unsigned ldt_count; + vm_size_t ldt_size; + vm_size_t size, size_needed; + vm_offset_t addr; + + if (thread == THREAD_NULL) + return KERN_INVALID_ARGUMENT; + if (first_desc < 0 || first_desc > 8191) + return KERN_INVALID_ARGUMENT; + if (first_desc + selector_count >= 8192) + return KERN_INVALID_ARGUMENT; + + pcb = thread->pcb; + addr = 0; + size = 0; + + for (;;) { + simple_lock(&pcb->lock); + user_ldt = pcb->ims.ldt; + if (user_ldt == 0) { + simple_unlock(&pcb->lock); + if (addr) + kmem_free(ipc_kernel_map, addr, size); + *count = 0; + return KERN_SUCCESS; + } + + /* + * Find how many descriptors we should return. + */ + ldt_count = (user_ldt->desc.limit_low + 1) / + sizeof (struct real_descriptor); + ldt_count -= first_desc; + if (ldt_count > selector_count) + ldt_count = selector_count; + + ldt_size = ldt_count * sizeof(struct real_descriptor); + + /* + * Do we have the memory we need? + */ + if (ldt_count <= *count) + break; /* fits in-line */ + + size_needed = round_page(ldt_size); + if (size_needed <= size) + break; + + /* + * Unlock the pcb and allocate more memory + */ + simple_unlock(&pcb->lock); + + if (size != 0) + kmem_free(ipc_kernel_map, addr, size); + + size = size_needed; + + if (kmem_alloc(ipc_kernel_map, &addr, size) + != KERN_SUCCESS) + return KERN_RESOURCE_SHORTAGE; + } + + /* + * copy out the descriptors + */ + memcpy(*desc_list, + &user_ldt->ldt[first_desc], + ldt_size); + *count = ldt_count; + simple_unlock(&pcb->lock); + + if (addr) { + vm_size_t size_used, size_left; + vm_map_copy_t memory; + + /* + * Free any unused memory beyond the end of the last page used + */ + size_used = round_page(ldt_size); + if (size_used != size) + kmem_free(ipc_kernel_map, + addr + size_used, size - size_used); + + /* + * Zero the remainder of the page being returned. + */ + size_left = size_used - ldt_size; + if (size_left > 0) + memset((char *)addr + ldt_size, 0, size_left); + + /* + * Make memory into copyin form - this unwires it. + */ + (void) vm_map_copyin(ipc_kernel_map, addr, size_used, + TRUE, &memory); + *desc_list = (struct real_descriptor *)memory; + } + + return KERN_SUCCESS; +} + +void +user_ldt_free(user_ldt_t user_ldt) +{ +#ifdef MACH_PV_DESCRIPTORS + unsigned i; +#ifdef MACH_PV_PAGETABLES + for (i=0; i<(user_ldt->desc.limit_low + 1)/sizeof(struct real_descriptor); i+=PAGE_SIZE/sizeof(struct real_descriptor)) + pmap_set_page_readwrite(&user_ldt->ldt[i]); +#endif /* MACH_PV_PAGETABLES */ + kfree(user_ldt->alloc, user_ldt->desc.limit_low + 1 + + PAGE_SIZE + offsetof(struct user_ldt, ldt)); +#else /* MACH_PV_DESCRIPTORS */ + kfree((vm_offset_t)user_ldt, + user_ldt->desc.limit_low + 1 + + sizeof(struct real_descriptor)); +#endif /* MACH_PV_DESCRIPTORS */ +} + + +kern_return_t +i386_set_gdt (thread_t thread, int *selector, struct descriptor descriptor) +{ + const struct real_descriptor *desc = (struct real_descriptor *)&descriptor; + int idx; + + if (thread == THREAD_NULL) + return KERN_INVALID_ARGUMENT; + + if (*selector == -1) + { + for (idx = 0; idx < USER_GDT_SLOTS; ++idx) + if ((thread->pcb->ims.user_gdt[idx].access & ACC_P) == 0) + { + *selector = ((idx + sel_idx(USER_GDT)) << 3) | SEL_PL_U; + break; + } + if (idx == USER_GDT_SLOTS) + return KERN_NO_SPACE; /* ? */ + } + else if ((*selector & (SEL_LDT|SEL_PL)) != SEL_PL_U + || sel_idx (*selector) < sel_idx(USER_GDT) + || sel_idx (*selector) >= sel_idx(USER_GDT) + USER_GDT_SLOTS) + return KERN_INVALID_ARGUMENT; + else + idx = sel_idx (*selector) - sel_idx(USER_GDT); + + if ((desc->access & ACC_P) == 0) + memset (&thread->pcb->ims.user_gdt[idx], 0, + sizeof thread->pcb->ims.user_gdt[idx]); + else if ((desc->access & (ACC_TYPE_USER|ACC_PL)) != (ACC_TYPE_USER|ACC_PL_U) || (desc->granularity & SZ_64)) + + return KERN_INVALID_ARGUMENT; + else + memcpy (&thread->pcb->ims.user_gdt[idx], desc, sizeof (struct descriptor)); + + /* + * If we are modifying the GDT for the current thread, + * make sure it is properly set. + */ + if (thread == current_thread()) + switch_ktss(thread->pcb); + + return KERN_SUCCESS; +} + +kern_return_t +i386_get_gdt (const thread_t thread, int selector, struct descriptor *descriptor) +{ + struct real_descriptor *desc = (struct real_descriptor *)descriptor; + if (thread == THREAD_NULL) + return KERN_INVALID_ARGUMENT; + + if ((selector & (SEL_LDT|SEL_PL)) != SEL_PL_U + || sel_idx (selector) < sel_idx(USER_GDT) + || sel_idx (selector) >= sel_idx(USER_GDT) + USER_GDT_SLOTS) + return KERN_INVALID_ARGUMENT; + + *desc = thread->pcb->ims.user_gdt[sel_idx (selector) - sel_idx(USER_GDT)]; + + return KERN_SUCCESS; +} diff --git a/i386/i386/user_ldt.h b/i386/i386/user_ldt.h new file mode 100644 index 0000000..26caa27 --- /dev/null +++ b/i386/i386/user_ldt.h @@ -0,0 +1,50 @@ +/* + * Mach Operating System + * Copyright (c) 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. + */ + +#ifndef _I386_USER_LDT_H_ +#define _I386_USER_LDT_H_ + +/* + * User LDT management. + * + * Each thread in a task may have its own LDT. + */ + +#include + +struct user_ldt { +#ifdef MACH_PV_DESCRIPTORS + vm_offset_t alloc; /* allocation before alignment */ +#endif /* MACH_PV_DESCRIPTORS */ + struct real_descriptor desc; /* descriptor for self */ + struct real_descriptor ldt[1]; /* descriptor table (variable) */ +}; +typedef struct user_ldt * user_ldt_t; + +extern void +user_ldt_free(user_ldt_t user_ldt); + +#endif /* _I386_USER_LDT_H_ */ diff --git a/i386/i386/vm_param.h b/i386/i386/vm_param.h new file mode 100644 index 0000000..056aa52 --- /dev/null +++ b/i386/i386/vm_param.h @@ -0,0 +1,200 @@ +/* + * Copyright (c) 1994 The University of Utah and + * the Computer Systems Laboratory at the University of Utah (CSL). + * All rights reserved. + * + * Permission to use, copy, modify and distribute this software is hereby + * granted provided that (1) source code retains these copyright, permission, + * and disclaimer notices, and (2) redistributions including binaries + * reproduce the notices in supporting documentation, and (3) all advertising + * materials mentioning features or use of this software display the following + * acknowledgement: ``This product includes software developed by the + * Computer Systems Laboratory at the University of Utah.'' + * + * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS + * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF + * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * CSL requests users of this software to return to csl-dist@cs.utah.edu any + * improvements that they make and grant CSL redistribution rights. + * + * Author: Bryan Ford, University of Utah CSL + */ +#ifndef _I386_KERNEL_I386_VM_PARAM_ +#define _I386_KERNEL_I386_VM_PARAM_ + +#include + +/* XXX use xu/vm_param.h */ +#include +#ifdef MACH_PV_PAGETABLES +#include +#endif + +/* To avoid ambiguity in kernel code, make the name explicit */ +#define VM_MIN_USER_ADDRESS VM_MIN_ADDRESS +#define VM_MAX_USER_ADDRESS VM_MAX_ADDRESS + +/* The kernel address space is usually 1GB, usually starting at virtual address 0. */ +/* This can be changed freely to separate kernel addresses from user addresses + * for better trace support in kdb; the _START symbol has to be offset by the + * same amount. */ +#ifdef __x86_64__ +#define VM_MIN_KERNEL_ADDRESS KERNEL_MAP_BASE +#else +#define VM_MIN_KERNEL_ADDRESS 0xC0000000UL +#endif + +#if defined(MACH_XEN) || defined (__x86_64__) +/* PV kernels can be loaded directly to the target virtual address */ +#define INIT_VM_MIN_KERNEL_ADDRESS VM_MIN_KERNEL_ADDRESS +#else /* MACH_XEN */ +/* This must remain 0 */ +#define INIT_VM_MIN_KERNEL_ADDRESS 0x00000000UL +#endif /* MACH_XEN */ + +#ifdef MACH_PV_PAGETABLES +#ifdef __i386__ +#if PAE +#define HYP_VIRT_START HYPERVISOR_VIRT_START_PAE +#else /* PAE */ +#define HYP_VIRT_START HYPERVISOR_VIRT_START_NONPAE +#endif /* PAE */ +#define VM_MAX_KERNEL_ADDRESS (HYP_VIRT_START - LINEAR_MIN_KERNEL_ADDRESS + VM_MIN_KERNEL_ADDRESS) +#else +#define HYP_VIRT_START HYPERVISOR_VIRT_START +#define VM_MAX_KERNEL_ADDRESS (LINEAR_MAX_KERNEL_ADDRESS - LINEAR_MIN_KERNEL_ADDRESS + VM_MIN_KERNEL_ADDRESS) +#endif +#else /* MACH_PV_PAGETABLES */ +#define VM_MAX_KERNEL_ADDRESS (LINEAR_MAX_KERNEL_ADDRESS - LINEAR_MIN_KERNEL_ADDRESS + VM_MIN_KERNEL_ADDRESS) +#endif /* MACH_PV_PAGETABLES */ + +/* + * Reserve mapping room for the kernel map, which includes + * the device I/O map and the IPC map. + */ +#ifdef __x86_64__ +/* + * Vm structures are quite bigger on 64 bit. + * This should be well enough for 8G of physical memory; on the other hand, + * maybe not all of them need to be in directly-mapped memory, see the parts + * allocated with pmap_steal_memory(). + */ +#define VM_KERNEL_MAP_SIZE (512 * 1024 * 1024) +#else +#define VM_KERNEL_MAP_SIZE (152 * 1024 * 1024) +#endif + +/* This is the kernel address range in linear addresses. */ +#ifdef __x86_64__ +#define LINEAR_MIN_KERNEL_ADDRESS VM_MIN_KERNEL_ADDRESS +#define LINEAR_MAX_KERNEL_ADDRESS (0xffffffffffffffffUL) +#else +/* On x86, the kernel virtual address space is actually located + at high linear addresses. */ +#define LINEAR_MIN_KERNEL_ADDRESS (VM_MAX_USER_ADDRESS) +#define LINEAR_MAX_KERNEL_ADDRESS (0xffffffffUL) +#endif + +#ifdef MACH_PV_PAGETABLES +/* need room for mmu updates (2*8bytes) */ +#define KERNEL_STACK_SIZE (4*I386_PGBYTES) +#define INTSTACK_SIZE (4*I386_PGBYTES) +#else /* MACH_PV_PAGETABLES */ +#define KERNEL_STACK_SIZE (1*I386_PGBYTES) +#define INTSTACK_SIZE (1*I386_PGBYTES) +#endif /* MACH_PV_PAGETABLES */ + /* interrupt stack size */ + +/* + * Conversion between 80386 pages and VM pages + */ + +#define trunc_i386_to_vm(p) (atop(trunc_page(i386_ptob(p)))) +#define round_i386_to_vm(p) (atop(round_page(i386_ptob(p)))) +#define vm_to_i386(p) (i386_btop(ptoa(p))) + +/* + * Physical memory is direct-mapped to virtual memory + * starting at virtual address VM_MIN_KERNEL_ADDRESS. + */ +#define phystokv(a) ((vm_offset_t)(a) + VM_MIN_KERNEL_ADDRESS) +/* + * This can not be used with virtual mappings, but can be used during bootstrap + */ +#define _kvtophys(a) ((vm_offset_t)(a) - VM_MIN_KERNEL_ADDRESS) + +/* + * Kernel virtual memory is actually at 0xc0000000 in linear addresses. + */ +#define kvtolin(a) ((vm_offset_t)(a) - VM_MIN_KERNEL_ADDRESS + LINEAR_MIN_KERNEL_ADDRESS) +#define lintokv(a) ((vm_offset_t)(a) - LINEAR_MIN_KERNEL_ADDRESS + VM_MIN_KERNEL_ADDRESS) + +/* + * Physical memory properties. + */ +#define VM_PAGE_DMA_LIMIT DECL_CONST(0x1000000, UL) + +#ifdef MACH_XEN +/* TODO Completely check Xen physical/virtual layout */ +#ifdef __LP64__ +#define VM_PAGE_MAX_SEGS 4 +#define VM_PAGE_DMA32_LIMIT DECL_CONST(0x100000000, UL) +#define VM_PAGE_DIRECTMAP_LIMIT DECL_CONST(0x400000000000, UL) +#define VM_PAGE_HIGHMEM_LIMIT DECL_CONST(0x10000000000000, ULL) +#else +#define VM_PAGE_MAX_SEGS 4 +#define VM_PAGE_DMA32_LIMIT DECL_CONST(0x100000000, UL) +#define VM_PAGE_DIRECTMAP_LIMIT (VM_MAX_KERNEL_ADDRESS \ + - VM_MIN_KERNEL_ADDRESS \ + - VM_KERNEL_MAP_SIZE) +#define VM_PAGE_HIGHMEM_LIMIT DECL_CONST(0x10000000000000, ULL) +#endif +#else /* MACH_XEN */ +#ifdef __LP64__ +#define VM_PAGE_MAX_SEGS 4 +#define VM_PAGE_DMA32_LIMIT DECL_CONST(0x100000000, UL) +#define VM_PAGE_DIRECTMAP_LIMIT (VM_MAX_KERNEL_ADDRESS \ + - VM_MIN_KERNEL_ADDRESS \ + - VM_KERNEL_MAP_SIZE + 1) +#define VM_PAGE_HIGHMEM_LIMIT DECL_CONST(0x10000000000000, UL) +#else /* __LP64__ */ +#define VM_PAGE_DIRECTMAP_LIMIT (VM_MAX_KERNEL_ADDRESS \ + - VM_MIN_KERNEL_ADDRESS \ + - VM_KERNEL_MAP_SIZE + 1) +#ifdef PAE +#define VM_PAGE_MAX_SEGS 4 +#define VM_PAGE_DMA32_LIMIT DECL_CONST(0x100000000, UL) +#define VM_PAGE_HIGHMEM_LIMIT DECL_CONST(0x10000000000000, ULL) +#else /* PAE */ +#define VM_PAGE_MAX_SEGS 3 +#define VM_PAGE_HIGHMEM_LIMIT DECL_CONST(0xfffff000, UL) +#endif /* PAE */ +#endif /* __LP64__ */ +#endif /* MACH_XEN */ + +/* + * Physical segment indexes. + */ +#define VM_PAGE_SEG_DMA 0 + +#if defined(VM_PAGE_DMA32_LIMIT) && (VM_PAGE_DMA32_LIMIT != VM_PAGE_DIRECTMAP_LIMIT) + +#if VM_PAGE_DMA32_LIMIT < VM_PAGE_DIRECTMAP_LIMIT +#define VM_PAGE_SEG_DMA32 (VM_PAGE_SEG_DMA+1) +#define VM_PAGE_SEG_DIRECTMAP (VM_PAGE_SEG_DMA32+1) +#define VM_PAGE_SEG_HIGHMEM (VM_PAGE_SEG_DIRECTMAP+1) +#else /* VM_PAGE_DMA32_LIMIT > VM_PAGE_DIRECTMAP_LIMIT */ +#define VM_PAGE_SEG_DIRECTMAP (VM_PAGE_SEG_DMA+1) +#define VM_PAGE_SEG_DMA32 (VM_PAGE_SEG_DIRECTMAP+1) +#define VM_PAGE_SEG_HIGHMEM (VM_PAGE_SEG_DMA32+1) +#endif + +#else + +#define VM_PAGE_SEG_DIRECTMAP (VM_PAGE_SEG_DMA+1) +#define VM_PAGE_SEG_DMA32 VM_PAGE_SEG_DIRECTMAP /* Alias for the DIRECTMAP segment */ +#define VM_PAGE_SEG_HIGHMEM (VM_PAGE_SEG_DIRECTMAP+1) +#endif + +#endif /* _I386_KERNEL_I386_VM_PARAM_ */ diff --git a/i386/i386/xen.h b/i386/i386/xen.h new file mode 100644 index 0000000..2cd81be --- /dev/null +++ b/i386/i386/xen.h @@ -0,0 +1,412 @@ +/* + * Copyright (C) 2006-2011 Free Software Foundation + * + * This program is free software ; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation ; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY ; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the program ; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef XEN_HYPCALL_H +#define XEN_HYPCALL_H + +#ifdef MACH_XEN +#ifndef __ASSEMBLER__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* TODO: this should be moved in appropriate non-Xen place. */ +#define mb() __asm__ __volatile__("lock; addl $0,0(%%esp)":::"memory") +#define rmb() mb() +#define wmb() mb() +static inline unsigned long xchgl(volatile unsigned long *ptr, unsigned long x) +{ + __asm__ __volatile__("xchg %0, %1" + : "=r" (x) + : "m" (*(ptr)), "0" (x): "memory"); + return x; +} +#define _TOSTR(x) #x +#define TOSTR(x) _TOSTR (x) + +#ifdef __i386__ +#define _hypcall_ret "=a" +#define _hypcall_arg1 "ebx" +#define _hypcall_arg2 "ecx" +#define _hypcall_arg3 "edx" +#define _hypcall_arg4 "esi" +#define _hypcall_arg5 "edi" +#endif +#ifdef __x86_64__ +#define _hypcall_ret "=a" +#define _hypcall_arg1 "rdi" +#define _hypcall_arg2 "rsi" +#define _hypcall_arg3 "rdx" +#define _hypcall_arg4 "r10" +#define _hypcall_arg5 "r8" +#endif + + +/* x86-specific hypercall interface. */ +#define _hypcall0(type, name) \ +static inline type hyp_##name(void) \ +{ \ + unsigned long __ret; \ + asm volatile ("call hypcalls+("TOSTR(__HYPERVISOR_##name)"*32)" \ + : "=a" (__ret) \ + : : "memory"); \ + return __ret; \ +} + +#define _hypcall1(type, name, type1, arg1) \ +static inline type hyp_##name(type1 arg1) \ +{ \ + unsigned long __ret; \ + register unsigned long __arg1 asm(_hypcall_arg1) = (unsigned long) arg1; \ + asm volatile ("call hypcalls+("TOSTR(__HYPERVISOR_##name)"*32)" \ + : "=a" (__ret), \ + "+r" (__arg1) \ + : : "memory"); \ + return __ret; \ +} + +#define _hypcall2(type, name, type1, arg1, type2, arg2) \ +static inline type hyp_##name(type1 arg1, type2 arg2) \ +{ \ + unsigned long __ret; \ + register unsigned long __arg1 asm(_hypcall_arg1) = (unsigned long) arg1; \ + register unsigned long __arg2 asm(_hypcall_arg2) = (unsigned long) arg2; \ + asm volatile ("call hypcalls+("TOSTR(__HYPERVISOR_##name)"*32)" \ + : "=a" (__ret), \ + "+r" (__arg1), \ + "+r" (__arg2) \ + : : "memory"); \ + return __ret; \ +} + +#define _hypcall3(type, name, type1, arg1, type2, arg2, type3, arg3) \ +static inline type hyp_##name(type1 arg1, type2 arg2, type3 arg3) \ +{ \ + unsigned long __ret; \ + register unsigned long __arg1 asm(_hypcall_arg1) = (unsigned long) arg1; \ + register unsigned long __arg2 asm(_hypcall_arg2) = (unsigned long) arg2; \ + register unsigned long __arg3 asm(_hypcall_arg3) = (unsigned long) arg3; \ + asm volatile ("call hypcalls+("TOSTR(__HYPERVISOR_##name)"*32)" \ + : "=a" (__ret), \ + "+r" (__arg1), \ + "+r" (__arg2), \ + "+r" (__arg3) \ + : : "memory"); \ + return __ret; \ +} + +#define _hypcall4(type, name, type1, arg1, type2, arg2, type3, arg3, type4, arg4) \ +static inline type hyp_##name(type1 arg1, type2 arg2, type3 arg3, type4 arg4) \ +{ \ + unsigned long __ret; \ + register unsigned long __arg1 asm(_hypcall_arg1) = (unsigned long) arg1; \ + register unsigned long __arg2 asm(_hypcall_arg2) = (unsigned long) arg2; \ + register unsigned long __arg3 asm(_hypcall_arg3) = (unsigned long) arg3; \ + register unsigned long __arg4 asm(_hypcall_arg4) = (unsigned long) arg4; \ + asm volatile ("call hypcalls+("TOSTR(__HYPERVISOR_##name)"*32)" \ + : "=a" (__ret), \ + "+r" (__arg1), \ + "+r" (__arg2), \ + "+r" (__arg3), \ + "+r" (__arg4) \ + : : "memory"); \ + return __ret; \ +} + +#define _hypcall5(type, name, type1, arg1, type2, arg2, type3, arg3, type4, arg4, type5, arg5) \ +static inline type hyp_##name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5) \ +{ \ + unsigned long __ret; \ + register unsigned long __arg1 asm(_hypcall_arg1) = (unsigned long) arg1; \ + register unsigned long __arg2 asm(_hypcall_arg2) = (unsigned long) arg2; \ + register unsigned long __arg3 asm(_hypcall_arg3) = (unsigned long) arg3; \ + register unsigned long __arg4 asm(_hypcall_arg4) = (unsigned long) arg4; \ + register unsigned long __arg5 asm(_hypcall_arg5) = (unsigned long) arg5; \ + asm volatile ("call hypcalls+("TOSTR(__HYPERVISOR_##name)"*32)" \ + : "=a" (__ret), \ + "+r" (__arg1), \ + "+r" (__arg2), \ + "+r" (__arg3), \ + "+r" (__arg4), \ + "+r" (__arg5) \ + : : "memory"); \ + return __ret; \ +} + +/* x86 Hypercalls */ + +/* Note: since Hypervisor uses flat memory model, remember to always use + * kvtolin when giving pointers as parameters for the hypercall to read data + * at. Use kv_to_la when they may be used before GDT got set up. */ + +_hypcall1(long, set_trap_table, vm_offset_t /* struct trap_info * */, traps); + +#ifdef MACH_PV_PAGETABLES +_hypcall4(int, mmu_update, vm_offset_t /* struct mmu_update * */, req, int, count, vm_offset_t /* int * */, success_count, domid_t, domid) +static inline int hyp_mmu_update_pte(pt_entry_t pte, pt_entry_t val) +{ + struct mmu_update update = + { + .ptr = pte, + .val = val, + }; + int count; + hyp_mmu_update(kv_to_la(&update), 1, kv_to_la(&count), DOMID_SELF); + return count; +} +/* Note: make sure this fits in KERNEL_STACK_SIZE */ +#define HYP_BATCH_MMU_UPDATES 256 + +#define hyp_mmu_update_la(la, val) hyp_mmu_update_pte( \ + (kernel_page_dir[lin2pdenum_cont((vm_offset_t)(la))] & INTEL_PTE_PFN) \ + + ptenum((vm_offset_t)(la)) * sizeof(pt_entry_t), val) +#endif + +_hypcall2(long, set_gdt, vm_offset_t /* unsigned long * */, frame_list, unsigned int, entries) + +_hypcall2(long, stack_switch, unsigned long, ss, unsigned long, esp); + +#ifdef __i386__ +_hypcall4(long, set_callbacks, unsigned long, es, void *, ea, + unsigned long, fss, void *, fsa); +#endif +#ifdef __x86_64__ +_hypcall3(long, set_callbacks, void *, ea, void *, fsa, void *, sc); +#endif +_hypcall1(long, fpu_taskswitch, int, set); + +#ifdef PAE +#define hyp_high(pte) ((pte) >> 32) +#else +#define hyp_high(pte) 0 +#endif +#ifdef __i386__ +_hypcall4(long, update_descriptor, unsigned long, ma_lo, unsigned long, ma_hi, unsigned long, desc_lo, unsigned long, desc_hi); +#define hyp_do_update_descriptor(ma, desc) ({ \ + pt_entry_t __ma = (ma); \ + uint64_t __desc = (desc); \ + hyp_update_descriptor(__ma & 0xffffffffU, hyp_high(__ma), __desc & 0xffffffffU, __desc >> 32); \ +}) +#endif +#ifdef __x86_64__ +_hypcall2(long, update_descriptor, unsigned long, ma, unsigned long, desc); +#define hyp_do_update_descriptor(ma, desc) hyp_update_descriptor(ma, desc) +#endif + +#ifdef __x86_64__ +_hypcall2(long, set_segment_base, int, reg, unsigned long, value); +#endif + +#include +_hypcall2(long, memory_op, unsigned long, cmd, vm_offset_t /* void * */, arg); +static inline void hyp_free_mfn(unsigned long mfn) +{ + struct xen_memory_reservation reservation; + reservation.extent_start = (void*) kvtolin(&mfn); + reservation.nr_extents = 1; + reservation.extent_order = 0; + reservation.address_bits = 0; + reservation.domid = DOMID_SELF; + if (hyp_memory_op(XENMEM_decrease_reservation, kvtolin(&reservation)) != 1) + panic("couldn't free page %lu\n", mfn); +} + +#ifdef __i386__ +_hypcall4(int, update_va_mapping, unsigned long, va, unsigned long, val_lo, unsigned long, val_hi, unsigned long, flags); +#define hyp_do_update_va_mapping(va, val, flags) ({ \ + pt_entry_t __val = (val); \ + hyp_update_va_mapping(va, __val & 0xffffffffU, hyp_high(__val), flags); \ +}) +#endif +#ifdef __x86_64__ +_hypcall3(int, update_va_mapping, unsigned long, va, unsigned long, val, unsigned long, flags); +#define hyp_do_update_va_mapping(va, val, flags) hyp_update_va_mapping(va, val, flags) +#endif + +static inline void hyp_free_page(unsigned long pfn, void *va) +{ + /* save mfn */ + unsigned long mfn = pfn_to_mfn(pfn); + +#ifdef MACH_PV_PAGETABLES + /* remove from mappings */ + if (hyp_do_update_va_mapping(kvtolin(va), 0, UVMF_INVLPG|UVMF_ALL)) + panic("couldn't clear page %lu at %p\n", pfn, va); + +#ifdef MACH_PSEUDO_PHYS + /* drop machine page */ + mfn_list[pfn] = ~0; +#endif /* MACH_PSEUDO_PHYS */ +#endif + + /* and free from Xen */ + hyp_free_mfn(mfn); +} + +#ifdef MACH_PV_PAGETABLES +_hypcall4(int, mmuext_op, vm_offset_t /* struct mmuext_op * */, op, int, count, vm_offset_t /* int * */, success_count, domid_t, domid); +static inline int hyp_mmuext_op_void(unsigned int cmd) +{ + struct mmuext_op op = { + .cmd = cmd, + }; + int count; + hyp_mmuext_op(kv_to_la(&op), 1, kv_to_la(&count), DOMID_SELF); + return count; +} +static inline int hyp_mmuext_op_mfn(unsigned int cmd, unsigned long mfn) +{ + struct mmuext_op op = { + .cmd = cmd, + .arg1.mfn = mfn, + }; + int count; + hyp_mmuext_op(kv_to_la(&op), 1, kv_to_la(&count), DOMID_SELF); + return count; +} +static inline void hyp_set_ldt(void *ldt, unsigned long nbentries) { + struct mmuext_op op = { + .cmd = MMUEXT_SET_LDT, + .arg1.linear_addr = kvtolin(ldt), + .arg2.nr_ents = nbentries, + }; + unsigned long count; + if (((unsigned long)ldt) & PAGE_MASK) + panic("ldt %p is not aligned on a page\n", ldt); + for (count=0; count> 32); \ +}) +#endif +#ifdef __x86_64__ +_hypcall1(long, set_timer_op, unsigned long, absolute); +#define hyp_do_set_timer_op(absolute_nsec) hyp_set_timer_op(absolute_nsec) +#endif + +#include +_hypcall1(int, event_channel_op, vm_offset_t /* evtchn_op_t * */, op); +static inline int hyp_event_channel_send(evtchn_port_t port) { + evtchn_op_t op = { + .cmd = EVTCHNOP_send, + .u.send.port = port, + }; + return hyp_event_channel_op(kvtolin(&op)); +} +static inline evtchn_port_t hyp_event_channel_alloc(domid_t domid) { + evtchn_op_t op = { + .cmd = EVTCHNOP_alloc_unbound, + .u.alloc_unbound.dom = DOMID_SELF, + .u.alloc_unbound.remote_dom = domid, + }; + if (hyp_event_channel_op(kvtolin(&op))) + panic("couldn't allocate event channel"); + return op.u.alloc_unbound.port; +} +static inline evtchn_port_t hyp_event_channel_bind_virq(uint32_t virq, uint32_t vcpu) { + evtchn_op_t op = { .cmd = EVTCHNOP_bind_virq, .u.bind_virq = { .virq = virq, .vcpu = vcpu }}; + if (hyp_event_channel_op(kvtolin(&op))) + panic("can't bind virq %d\n",virq); + return op.u.bind_virq.port; +} + +_hypcall3(int, console_io, int, cmd, int, count, vm_offset_t /* const char * */, buffer); + +_hypcall3(long, grant_table_op, unsigned int, cmd, vm_offset_t /* void * */, uop, unsigned int, count); + +_hypcall2(long, vm_assist, unsigned int, cmd, unsigned int, type); + +_hypcall0(long, iret); + +#include +_hypcall2(long, sched_op, int, cmd, vm_offset_t /* void* */, arg) +#define hyp_yield() hyp_sched_op(SCHEDOP_yield, 0) +#define hyp_block() hyp_sched_op(SCHEDOP_block, 0) +static inline void __attribute__((noreturn)) hyp_crash(void) +{ + unsigned int shut = SHUTDOWN_crash; + hyp_sched_op(SCHEDOP_shutdown, kvtolin(&shut)); + /* really shouldn't return */ + printf("uh, shutdown returned?!\n"); + for(;;); +} + +static inline void __attribute__((noreturn)) hyp_halt(void) +{ + unsigned int shut = SHUTDOWN_poweroff; + hyp_sched_op(SCHEDOP_shutdown, kvtolin(&shut)); + /* really shouldn't return */ + printf("uh, shutdown returned?!\n"); + for(;;); +} + +static inline void __attribute__((noreturn)) hyp_reboot(void) +{ + unsigned int shut = SHUTDOWN_reboot; + hyp_sched_op(SCHEDOP_shutdown, kvtolin(&shut)); + /* really shouldn't return */ + printf("uh, reboot returned?!\n"); + for(;;); +} + +_hypcall2(int, set_debugreg, int, reg, unsigned long, value); +_hypcall1(unsigned long, get_debugreg, int, reg); + +/* x86-specific */ +static inline uint64_t hyp_cpu_clock(void) { + uint32_t hi, lo; + asm volatile("rdtsc" : "=d"(hi), "=a"(lo)); + return (((uint64_t) hi) << 32) | lo; +} + +#else /* __ASSEMBLER__ */ +/* TODO: SMP */ +#define cli movb $0xff,hyp_shared_info+CPU_CLI +#define sti call hyp_sti +#define iretq jmp hyp_iretq +#endif /* ASSEMBLER */ +#endif /* MACH_XEN */ + +#endif /* XEN_HYPCALL_H */ diff --git a/i386/i386/xpr.h b/i386/i386/xpr.h new file mode 100644 index 0000000..19ef026 --- /dev/null +++ b/i386/i386/xpr.h @@ -0,0 +1,32 @@ +/* + * 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. + */ +/* + * File: xpr.h + * + * Machine dependent module for the XPR tracing facility. + */ + +#define XPR_TIMESTAMP (0) diff --git a/i386/i386at/acpi_parse_apic.c b/i386/i386at/acpi_parse_apic.c new file mode 100644 index 0000000..1cfc179 --- /dev/null +++ b/i386/i386at/acpi_parse_apic.c @@ -0,0 +1,650 @@ +/* acpi_parse_apic.h - ACPI-MADT table parser. Source file + Copyright (C) 2018 Juan Bosco Garcia + Copyright (C) 2019 2020 Almudena Garcia Jurado-Centurion + Written by Juan Bosco Garcia and Almudena Garcia Jurado-Centurion + + This file is part of Min_SMP. + + Min_SMP is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + Min_SMP is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include /* memcmp, memcpy... */ + +#include /* uint16_t, uint32_t... */ + +#include /* machine_slot */ + +#include /* printf */ +#include +#include /* phystokv */ +#include /* lapic, ioapic... */ +#include +#include + +static struct acpi_apic *apic_madt = NULL; +unsigned lapic_addr; +uint32_t *hpet_addr; + +/* + * acpi_print_info: shows by screen the ACPI's rsdp and rsdt virtual address + * and the number of entries stored in RSDT table. + * + * Receives as input the references of RSDP and RSDT tables, + * and the number of entries stored in RSDT. + */ +void +acpi_print_info(phys_addr_t rsdp, void *rsdt, int acpi_rsdt_n) +{ + + printf("ACPI:\n"); + printf(" rsdp = 0x%lx\n", rsdp); + printf(" rsdt/xsdt = 0x%p (n = %d)\n", rsdt, acpi_rsdt_n); +} + +/* + * acpi_checksum: calculates the checksum of an ACPI table. + * Receives as input the virtual address of the table. + * + * Returns 0 if success, other value if error. + */ +static uint8_t +acpi_checksum(void *addr, uint32_t length) +{ + uint8_t *bytes = addr; + uint8_t checksum = 0; + unsigned int i; + + /* Sum all bytes of addr */ + for (i = 0; i < length; i++) + checksum += bytes[i]; + + return checksum; +} + +/* + * acpi_check_signature: check if a signature match with the signature of its table. + * + * Receive as parameter both signatures: table signature, the signature which needs to check, + * and real signature, the genuine signature of the table. + * + * Return 0 if success, other if error. + */ + +static int +acpi_check_signature(const uint8_t table_signature[], const char *real_signature, uint8_t length) +{ + return memcmp(table_signature, real_signature, length); +} + + +/* + * acpi_check_rsdp: + * check if the RDSP "candidate" table is the real RSDP table. + * + * Compare the table signature with the ACPI signature for this table + * and check is the checksum is correct. + * + * Receives as input the reference of RSDT table. + * + * Preconditions: RSDP pointer must not be NULL. + * + * Returns 1 if ACPI 1.0 and sets sdt_base + * Returns 2 if ACPI >= 2.0 and sets sdt_base + */ +static int8_t +acpi_check_rsdp(struct acpi_rsdp2 *rsdp, phys_addr_t *sdt_base) +{ + int is_rsdp; + uint8_t cksum; + + /* Check if rsdp signature match with the ACPI RSDP signature. */ + is_rsdp = acpi_check_signature(rsdp->v1.signature, ACPI_RSDP_SIG, 8*sizeof(uint8_t)); + + if (is_rsdp != ACPI_SUCCESS) + return ACPI_BAD_SIGNATURE; + + if (rsdp->v1.revision == 0) { + // ACPI 1.0 + *sdt_base = rsdp->v1.rsdt_addr; + printf("ACPI v1.0\n"); + cksum = acpi_checksum((void *)(&rsdp->v1), sizeof(struct acpi_rsdp)); + + if (cksum != 0) + return ACPI_BAD_CHECKSUM; + + return 1; + + } else if (rsdp->v1.revision == 2) { + // ACPI >= 2.0 + *sdt_base = rsdp->xsdt_addr; + printf("ACPI >= v2.0\n"); + cksum = acpi_checksum((void *)rsdp, sizeof(struct acpi_rsdp2)); + + if (cksum != 0) + return ACPI_BAD_CHECKSUM; + + return 2; + } + + return ACPI_NO_RSDP; +} + +/* + * acpi_check_rsdp_align: check if the RSDP address is aligned. + * Preconditions: The address must not be NULL + * + * Returns ACPI_SUCCESS (0) if success, ACPI_BAD_ALIGN if error + */ + +static int8_t +acpi_check_rsdp_align(void *addr) +{ + /* check alignment. */ + if ((uintptr_t)addr & (ACPI_RSDP_ALIGN-1)) + return ACPI_BAD_ALIGN; + + return ACPI_SUCCESS; +} + +/* + * acpi_search_rsdp: search the rsdp table in a memory range. + * + * Receives as input the initial virtual address, and the lenght + * of memory range. + * + * Preconditions: The start address (addr) must be aligned. + * + * Returns the physical address of rsdp structure if success, 0 if failure. + */ +static phys_addr_t +acpi_search_rsdp(void *addr, uint32_t length, int *is_64bit) +{ + void *end; + int version = 0; + phys_addr_t sdt_base = 0; + + /* Search RDSP in memory space between addr and addr+lenght. */ + for (end = addr+length; addr < end; addr += ACPI_RSDP_ALIGN) { + + /* Check if the current memory block stores the RDSP. */ + if ((addr != NULL) && ((version = acpi_check_rsdp(addr, &sdt_base)) > 0)) { + /* If yes, return RSDT/XSDT address */ + *is_64bit = (version == 2); + return sdt_base; + } + } + + return 0; +} + +/* + * acpi_get_rsdp: tries to find the RSDP table, + * searching It in many memory ranges, as It's written in ACPI Specification. + * + * Returns the reference to RDSP structure if success, 0 if failure. + */ +static phys_addr_t +acpi_get_rsdp(int *is_64bit) +{ + uint16_t *start = 0; + phys_addr_t base = 0; + phys_addr_t rsdp = 0; + + /* EDBA start address. */ + start = (uint16_t*) phystokv(0x040e); + base = phystokv((*start) << 4); /* address = paragraph number * 16 */ + + /* check alignment. */ + if (acpi_check_rsdp_align((void *)base) == ACPI_BAD_ALIGN) + return 0; + rsdp = acpi_search_rsdp((void *)base, 1024, is_64bit); + + if (rsdp == 0) { + /* If RSDP isn't in EDBA, search in the BIOS read-only memory space between 0E0000h and 0FFFFFh */ + rsdp = acpi_search_rsdp((void *)phystokv(0xe0000), 0x100000 - 0x0e0000, is_64bit); + } + + return rsdp; +} + +/* + * acpi_get_rsdt: Get RSDT table reference from RSDP entries. + * + * Receives as input a reference for RSDP table + * and a reference to store the number of entries of RSDT. + * + * Returns the reference to RSDT table if success, NULL if error. + */ +static struct acpi_rsdt* +acpi_get_rsdt(phys_addr_t rsdp_phys, int* acpi_rsdt_n) +{ + struct acpi_rsdt *rsdt = NULL; + int signature_check; + + rsdt = (struct acpi_rsdt*) kmem_map_aligned_table(rsdp_phys, sizeof(struct acpi_rsdt), VM_PROT_READ); + + /* Check if the RSDT mapping is fine. */ + if (rsdt == NULL) + return NULL; + + /* Check is rsdt signature is equals to ACPI RSDT signature. */ + signature_check = acpi_check_signature(rsdt->header.signature, ACPI_RSDT_SIG, + 4*sizeof(uint8_t)); + + if (signature_check != ACPI_SUCCESS) + return NULL; + + /* Calculated number of elements stored in rsdt. */ + *acpi_rsdt_n = (rsdt->header.length - sizeof(rsdt->header)) + / sizeof(rsdt->entry[0]); + + return rsdt; +} + +/* + * acpi_get_xsdt: Get XSDT table reference from RSDPv2 entries. + * + * Receives as input a reference for RSDPv2 table + * and a reference to store the number of entries of XSDT. + * + * Returns the reference to XSDT table if success, NULL if error. + */ +static struct acpi_xsdt* +acpi_get_xsdt(phys_addr_t rsdp_phys, int* acpi_xsdt_n) +{ + struct acpi_xsdt *xsdt = NULL; + int signature_check; + + xsdt = (struct acpi_xsdt*) kmem_map_aligned_table(rsdp_phys, sizeof(struct acpi_xsdt), VM_PROT_READ); + + /* Check if the RSDT mapping is fine. */ + if (xsdt == NULL) + return NULL; + + /* Check is rsdt signature is equals to ACPI RSDT signature. */ + signature_check = acpi_check_signature(xsdt->header.signature, ACPI_XSDT_SIG, + 4*sizeof(uint8_t)); + + if (signature_check != ACPI_SUCCESS) + return NULL; + + /* Calculated number of elements stored in rsdt. */ + *acpi_xsdt_n = (xsdt->header.length - sizeof(xsdt->header)) + / sizeof(xsdt->entry[0]); + + return xsdt; +} + +/* + * acpi_get_apic: get MADT/APIC table from RSDT entries. + * + * Receives as input the RSDT initial address, + * and the number of entries of RSDT table. + * + * Returns a reference to APIC/MADT table if success, NULL if failure. + * Also sets hpet_addr to base address of HPET. + */ +static struct acpi_apic* +acpi_get_apic(struct acpi_rsdt *rsdt, int acpi_rsdt_n) +{ + struct acpi_dhdr *descr_header; + struct acpi_apic *madt = NULL; + int check_signature; + uint64_t map_addr; + + /* Search APIC entries in rsdt table. */ + for (int i = 0; i < acpi_rsdt_n; i++) { + descr_header = (struct acpi_dhdr*) kmem_map_aligned_table(rsdt->entry[i], sizeof(struct acpi_dhdr), + VM_PROT_READ); + + /* Check if the entry is a MADT */ + check_signature = acpi_check_signature(descr_header->signature, ACPI_APIC_SIG, 4*sizeof(uint8_t)); + if (check_signature == ACPI_SUCCESS) + madt = (struct acpi_apic*) descr_header; + + /* Check if the entry is a HPET */ + check_signature = acpi_check_signature(descr_header->signature, ACPI_HPET_SIG, 4*sizeof(uint8_t)); + if (check_signature == ACPI_SUCCESS) { + map_addr = ((struct acpi_hpet *)descr_header)->address.addr64; + assert (map_addr != 0); + hpet_addr = (uint32_t *)kmem_map_aligned_table(map_addr, 1024, VM_PROT_READ | VM_PROT_WRITE); + printf("HPET at physical address 0x%llx\n", map_addr); + } + } + + return madt; +} + +/* + * acpi_get_apic2: get MADT/APIC table from XSDT entries. + * + * Receives as input the XSDT initial address, + * and the number of entries of XSDT table. + * + * Returns a reference to APIC/MADT table if success, NULL if failure. + * Also sets hpet_addr to base address of HPET. + */ +static struct acpi_apic* +acpi_get_apic2(struct acpi_xsdt *xsdt, int acpi_xsdt_n) +{ + struct acpi_dhdr *descr_header; + struct acpi_apic *madt = NULL; + int check_signature; + uint64_t map_addr; + + /* Search APIC entries in rsdt table. */ + for (int i = 0; i < acpi_xsdt_n; i++) { + descr_header = (struct acpi_dhdr*) kmem_map_aligned_table(xsdt->entry[i], sizeof(struct acpi_dhdr), + VM_PROT_READ); + + /* Check if the entry is an APIC. */ + check_signature = acpi_check_signature(descr_header->signature, ACPI_APIC_SIG, 4*sizeof(uint8_t)); + if (check_signature == ACPI_SUCCESS) + madt = (struct acpi_apic *)descr_header; + + /* Check if the entry is a HPET. */ + check_signature = acpi_check_signature(descr_header->signature, ACPI_HPET_SIG, 4*sizeof(uint8_t)); + if (check_signature == ACPI_SUCCESS) { + map_addr = ((struct acpi_hpet *)descr_header)->address.addr64; + assert (map_addr != 0); + hpet_addr = (uint32_t *)kmem_map_aligned_table(map_addr, 1024, VM_PROT_READ | VM_PROT_WRITE); + printf("HPET at physical address 0x%llx\n", map_addr); + } + } + + return madt; +} + +/* + * acpi_add_lapic: add a new Local APIC to cpu_to_lapic array + * and increase the number of cpus. + * + * Receives as input the Local APIC entry in MADT/APIC table. + */ +static void +acpi_apic_add_lapic(struct acpi_apic_lapic *lapic_entry) +{ + /* If cpu flag is correct */ + if (lapic_entry->flags & 0x1) { + /* Add cpu to processors' list. */ + apic_add_cpu(lapic_entry->apic_id); + } + +} + +/* + * apic_add_ioapic: add a new IOAPIC to IOAPICS array + * and increase the number of IOAPIC. + * + * Receives as input the IOAPIC entry in MADT/APIC table. + */ + +static void +acpi_apic_add_ioapic(struct acpi_apic_ioapic *ioapic_entry) +{ + IoApicData io_apic; + + /* Fill IOAPIC structure with its main fields */ + io_apic.apic_id = ioapic_entry->apic_id; + io_apic.addr = ioapic_entry->addr; + io_apic.gsi_base = ioapic_entry->gsi_base; + io_apic.ioapic = (ApicIoUnit *)kmem_map_aligned_table(ioapic_entry->addr, + sizeof(ApicIoUnit), + VM_PROT_READ | VM_PROT_WRITE); + io_apic.ioapic->select.r = APIC_IO_VERSION; + io_apic.ngsis = ((io_apic.ioapic->window.r >> APIC_IO_ENTRIES_SHIFT) & 0xff) + 1; + + /* Insert IOAPIC in the list. */ + apic_add_ioapic(io_apic); +} + + +/* + * apic_add_ioapic: add a new IOAPIC to IOAPICS list + * and increase the number of IOAPIC. + * + * Receives as input the IOAPIC entry in MADT/APIC table. + */ + +static void +acpi_apic_add_irq_override(struct acpi_apic_irq_override* irq_override) +{ + IrqOverrideData irq_over; + + /* Fills IRQ override structure with its fields */ + irq_over.bus = irq_override->bus; + irq_over.irq = irq_override->irq; + irq_over.gsi = irq_override->gsi; + irq_over.flags = irq_override->flags; + + /* Insert IRQ override in the list */ + apic_add_irq_override(irq_over); +} + + +/* + * apic_parse_table: parse the MADT/APIC table. + * + * Read the APIC/MADT table entry to entry, + * registering the APIC structures (Local APIC, IOAPIC or IRQ override) entries in their lists. + */ + +static int +acpi_apic_parse_table(struct acpi_apic *apic) +{ + struct acpi_apic_dhdr *apic_entry = NULL; + vm_offset_t end = 0; + uint8_t numcpus = 1; + + /* Get the address of first APIC entry */ + apic_entry = (struct acpi_apic_dhdr*) apic->entry; + + /* Get the end address of APIC table */ + end = (vm_offset_t) apic + apic->header.length; + + printf("APIC entry=0x%p end=0x%x\n", apic_entry, end); + + /* Initialize number of cpus */ + numcpus = apic_get_numcpus(); + + /* Search in APIC entry. */ + while ((vm_offset_t)apic_entry < end) { + struct acpi_apic_lapic *lapic_entry; + struct acpi_apic_ioapic *ioapic_entry; + struct acpi_apic_irq_override *irq_override_entry; + + printf("APIC entry=0x%p end=0x%x\n", apic_entry, end); + /* Check entry type. */ + switch(apic_entry->type) { + + /* If APIC entry is a CPU's Local APIC. */ + case ACPI_APIC_ENTRY_LAPIC: + if(numcpus < NCPUS) { + /* Store Local APIC data. */ + lapic_entry = (struct acpi_apic_lapic*) apic_entry; + acpi_apic_add_lapic(lapic_entry); + } + break; + + /* If APIC entry is an IOAPIC. */ + case ACPI_APIC_ENTRY_IOAPIC: + + /* Store IOAPIC data. */ + ioapic_entry = (struct acpi_apic_ioapic*) apic_entry; + acpi_apic_add_ioapic(ioapic_entry); + + break; + + /* If APIC entry is a IRQ Override. */ + case ACPI_APIC_ENTRY_IRQ_OVERRIDE: + + /* Store IRQ Override data. */ + irq_override_entry = (struct acpi_apic_irq_override*) apic_entry; + acpi_apic_add_irq_override(irq_override_entry); + break; + + /* FIXME: There is another unhandled case */ + default: + printf("Unhandled APIC entry type 0x%x\n", apic_entry->type); + break; + } + + /* Get next APIC entry. */ + apic_entry = (struct acpi_apic_dhdr*)((vm_offset_t) apic_entry + + apic_entry->length); + + /* Update number of cpus. */ + numcpus = apic_get_numcpus(); + } + + return ACPI_SUCCESS; +} + + +/* + * acpi_apic_setup: parses the APIC/MADT table, to find the Local APIC and IOAPIC structures + * and the common address for Local APIC. + * + * Receives as input a reference for APIC/MADT table. + * Returns 0 if success. + * + * Fills the cpu_to_lapic and ioapics array, indexed by Kernel ID + * with a relationship between Kernel ID and APIC ID, + * and map the Local APIC common address, to fill the lapic reference. + * + * Precondition: The APIC pointer must not be NULL + */ + +static int +acpi_apic_setup(struct acpi_apic *apic) +{ + ApicLocalUnit* lapic_unit; + uint8_t ncpus, nioapics; + + /* map common lapic address */ + lapic_addr = apic->lapic_addr; + lapic_unit = kmem_map_aligned_table(apic->lapic_addr, sizeof(ApicLocalUnit), + VM_PROT_READ | VM_PROT_WRITE); + + if (lapic_unit == NULL) + return ACPI_NO_LAPIC; + + apic_lapic_init(lapic_unit); + acpi_apic_parse_table(apic); + + ncpus = apic_get_numcpus(); + nioapics = apic_get_num_ioapics(); + + if (ncpus == 0 || nioapics == 0 || ncpus > NCPUS) + return ACPI_APIC_FAILURE; + + /* Refit the apic-cpu array. */ + if(ncpus < NCPUS) { + int refit = apic_refit_cpulist(); + if (refit != 0) + return ACPI_FIT_FAILURE; + } + + apic_generate_cpu_id_lut(); + + return ACPI_SUCCESS; +} + +/* + * acpi_apic_init: find the MADT/APIC table in ACPI tables + * and parses It to find Local APIC and IOAPIC structures. + * Each Local APIC stores the info and control structores for a cpu. + * The IOAPIC controls the communication of the processors with the I/O devices. + * + * Returns 0 if success, -1 if error. + */ +int +acpi_apic_init(void) +{ + phys_addr_t rsdp = 0; + struct acpi_rsdt *rsdt = 0; + struct acpi_xsdt *xsdt = 0; + int acpi_rsdt_n; + int ret_acpi_setup; + int apic_init_success = 0; + int is_64bit = 0; + uint8_t checksum; + + /* Try to get the RSDP physical address. */ + rsdp = acpi_get_rsdp(&is_64bit); + if (rsdp == 0) + return ACPI_NO_RSDP; + + if (!is_64bit) { + /* Try to get the RSDT pointer. */ + rsdt = acpi_get_rsdt(rsdp, &acpi_rsdt_n); + if (rsdt == NULL) + return ACPI_NO_RSDT; + + checksum = acpi_checksum((void *)rsdt, rsdt->header.length); + if (checksum != 0) + return ACPI_BAD_CHECKSUM; + + /* Try to get the APIC table pointer. */ + apic_madt = acpi_get_apic(rsdt, acpi_rsdt_n); + if (apic_madt == NULL) + return ACPI_NO_APIC; + + checksum = acpi_checksum((void *)apic_madt, apic_madt->header.length); + if (checksum != 0) + return ACPI_BAD_CHECKSUM; + + /* Print the ACPI tables addresses. */ + acpi_print_info(rsdp, rsdt, acpi_rsdt_n); + + } else { + /* Try to get the XSDT pointer. */ + xsdt = acpi_get_xsdt(rsdp, &acpi_rsdt_n); + if (xsdt == NULL) + return ACPI_NO_RSDT; + + checksum = acpi_checksum((void *)xsdt, xsdt->header.length); + if (checksum != 0) + return ACPI_BAD_CHECKSUM; + + /* Try to get the APIC table pointer. */ + apic_madt = acpi_get_apic2(xsdt, acpi_rsdt_n); + if (apic_madt == NULL) + return ACPI_NO_APIC; + + checksum = acpi_checksum((void *)apic_madt, apic_madt->header.length); + if (checksum != 0) + return ACPI_BAD_CHECKSUM; + + /* Print the ACPI tables addresses. */ + acpi_print_info(rsdp, xsdt, acpi_rsdt_n); + } + + apic_init_success = apic_data_init(); + if (apic_init_success != ACPI_SUCCESS) + return ACPI_APIC_FAILURE; + + /* + * Starts the parsing of APIC table, to find the APIC structures. + * and enumerate them. This function also find the common Local APIC address. + */ + ret_acpi_setup = acpi_apic_setup(apic_madt); + if (ret_acpi_setup != ACPI_SUCCESS) + return ret_acpi_setup; + + /* Prints a table with the list of each cpu and each IOAPIC with its APIC ID. */ + apic_print_info(); + + return ACPI_SUCCESS; +} diff --git a/i386/i386at/acpi_parse_apic.h b/i386/i386at/acpi_parse_apic.h new file mode 100644 index 0000000..85e0117 --- /dev/null +++ b/i386/i386at/acpi_parse_apic.h @@ -0,0 +1,201 @@ +/* acpi_parse_apic.h - ACPI-MADT table parser. Header file + Copyright (C) 2018 Juan Bosco Garcia + Copyright (C) 2019 2020 Almudena Garcia Jurado-Centurion + Written by Juan Bosco Garcia and Almudena Garcia Jurado-Centurion + + This file is part of Min_SMP. + + Min_SMP is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + Min_SMP is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#ifndef __ACPI_H__ +#define __ACPI_H__ + +#include + +enum ACPI_RETURN { + ACPI_BAD_CHECKSUM = -1, + ACPI_BAD_ALIGN = -2, + ACPI_NO_RSDP = -3, + ACPI_NO_RSDT = -4, + ACPI_BAD_SIGNATURE = -5, + ACPI_NO_APIC = -6, + ACPI_NO_LAPIC = -7, + ACPI_APIC_FAILURE = -8, + ACPI_FIT_FAILURE = -9, + ACPI_SUCCESS = 0, +}; + +#define ACPI_RSDP_ALIGN 16 +#define ACPI_RSDP_SIG "RSD PTR " + +struct acpi_rsdp { + uint8_t signature[8]; + uint8_t checksum; + uint8_t oem_id[6]; + uint8_t revision; + uint32_t rsdt_addr; +} __attribute__((__packed__)); + +struct acpi_rsdp2 { + struct acpi_rsdp v1; + uint32_t length; + uint64_t xsdt_addr; + uint8_t checksum; + uint8_t reserved[3]; +} __attribute__((__packed__)); + +/* + * RSDT Entry Header + * + * Header which stores the descriptors of tables pointed from RDSP's Entry Field + * Includes the signature of the table, to identify each table. + * + * In MADT, the signature is 'APIC'. + */ +struct acpi_dhdr { + uint8_t signature[4]; + uint32_t length; + uint8_t revision; + uint8_t checksum; + uint8_t oem_id[6]; + uint8_t oem_table_id[8]; + uint32_t oem_revision; + uint8_t creator_id[4]; + uint32_t creator_revision; +} __attribute__((__packed__)); + + +#define ACPI_RSDT_SIG "RSDT" + +struct acpi_rsdt { + struct acpi_dhdr header; + uint32_t entry[0]; +} __attribute__((__packed__)); + +#define ACPI_XSDT_SIG "XSDT" + +struct acpi_xsdt { + struct acpi_dhdr header; + uint64_t entry[0]; +} __attribute__((__packed__)); + +struct acpi_address { + uint8_t is_io; + uint8_t reg_width; + uint8_t reg_offset; + uint8_t reserved; + uint64_t addr64; +} __attribute__((__packed__)); + +/* APIC table signature. */ +#define ACPI_APIC_SIG "APIC" + +/* Types value for MADT entries: Local APIC, IOAPIC and IRQ Override. */ +enum ACPI_APIC_ENTRY_TYPE { + ACPI_APIC_ENTRY_LAPIC = 0, + ACPI_APIC_ENTRY_IOAPIC = 1, + ACPI_APIC_ENTRY_IRQ_OVERRIDE = 2, + ACPI_APIC_ENTRY_NONMASK_IRQ = 4 +}; + +/* + * APIC descriptor header + * Define the type of the structure (Local APIC, I/O APIC or others). + * Type: Local APIC (0), I/O APIC (1). + */ +struct acpi_apic_dhdr { + uint8_t type; + uint8_t length; +} __attribute__((__packed__)); + + +/* + * Multiple APIC Description Table (MADT) + * + * Describes the APIC structures which exist in the machine. + * Includes the common address where Local APIC is mapped in main memory. + * + * Entry field stores the descriptors of APIC structures. + */ +struct acpi_apic { + struct acpi_dhdr header; /* Header, which stores the descriptor for RDST's Entry field. */ + uint32_t lapic_addr; /* Local Interrupt Controller Address. */ + uint32_t flags; + struct acpi_apic_dhdr entry[0]; /* Interrupt Controller Structure */ +} __attribute__((__packed__)); + +/* + * Processor Local APIC Structure + * + * Stores information about APIC ID, flags and ACPI Processor UID + */ + +struct acpi_apic_lapic { + struct acpi_apic_dhdr header; + uint8_t processor_id; /* ACPI Processor UID */ + uint8_t apic_id; + uint32_t flags; +} __attribute__((__packed__)); + + +/* + * I/O APIC Structure + * + * Stores information about APIC ID, and I/O APIC tables + */ + +struct acpi_apic_ioapic { + struct acpi_apic_dhdr header; + uint8_t apic_id; + uint8_t reserved; + uint32_t addr; + uint32_t gsi_base; +} __attribute__((__packed__)); + +/* + * IRQ Override structure + * + * Stores information about IRQ override, with busses and IRQ. + */ + +struct acpi_apic_irq_override { + struct acpi_apic_dhdr header; + uint8_t bus; + uint8_t irq; + uint32_t gsi; + uint16_t flags; +} __attribute__((__packed__)); + + +#define ACPI_HPET_SIG "HPET" + +/* + * HPET High Precision Event Timer structure + */ +struct acpi_hpet { + struct acpi_dhdr header; + uint32_t id; + struct acpi_address address; + uint8_t sequence; + uint16_t minimum_tick; + uint8_t flags; +} __attribute__((__packed__)); + +int acpi_apic_init(void); +void acpi_print_info(phys_addr_t rsdp, void *rsdt, int acpi_rsdt_n); + +extern unsigned lapic_addr; + +#endif /* __ACPI_H__ */ diff --git a/i386/i386at/autoconf.c b/i386/i386at/autoconf.c new file mode 100644 index 0000000..5c69988 --- /dev/null +++ b/i386/i386at/autoconf.c @@ -0,0 +1,149 @@ +/* + * Mach Operating System + * Copyright (c) 1993,1992,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. + */ + +#include +#include +#include +#include +#include +#ifdef APIC +# include +#else +# include +#endif +#include + +/* initialization typecasts */ +#define SPL_FIVE (vm_offset_t)SPL5 +#define SPL_SIX (vm_offset_t)SPL6 +#define SPL_TTY (vm_offset_t)SPLTTY + + +#if NCOM > 0 +extern struct bus_driver comdriver; +#include +#endif /* NCOM */ + +#if NLPR > 0 +extern struct bus_driver lprdriver; +#include +#endif /* NLPR */ + +struct bus_ctlr bus_master_init[] = { + +/* driver name unit intr address len phys_address + adaptor alive flags spl pic */ + + {0} +}; + + +struct bus_device bus_device_init[] = { + +/* driver name unit intr address am phys_address + adaptor alive ctlr slave flags *mi *next sysdep sysdep */ + +#if NCOM > 0 + {&comdriver, "com", 0, comintr, 0x3f8, 8, 0x3f8, + '?', 0, -1, -1, 0, 0, 0, SPL_TTY, 4}, + {&comdriver, "com", 1, comintr, 0x2f8, 8, 0x2f8, + '?', 0, -1, -1, 0, 0, 0, SPL_TTY, 3}, + {&comdriver, "com", 2, comintr, 0x3e8, 8, 0x3e8, + '?', 0, -1, -1, 0, 0, 0, SPL_TTY, 5}, +#endif /* NCOM > 0 */ + +#ifdef MACH_LPR +#if NLPR > 0 + {&lprdriver, "lpr", 0, lprintr, 0x378, 3, 0x378, + '?', 0, -1, -1, 0, 0, 0, SPL_TTY, 7}, + {&lprdriver, "lpr", 0, lprintr, 0x278, 3, 0x278, + '?', 0, -1, -1, 0, 0, 0, SPL_TTY, 7}, + {&lprdriver, "lpr", 0, lprintr, 0x3bc, 3, 0x3bc, + '?', 0, -1, -1, 0, 0, 0, SPL_TTY, 7}, +#endif /* NLPR > 0 */ +#endif /* MACH_LPR */ + + {0} +}; + +/* + * probeio: + * + * Probe and subsequently attach devices out on the AT bus. + * + * + */ +void probeio(void) +{ + struct bus_device *device; + struct bus_ctlr *master; + int i = 0; + + for (master = bus_master_init; master->driver; master++) + { + if (configure_bus_master(master->name, master->address, + master->phys_address, i, "atbus")) + i++; + } + + for (device = bus_device_init; device->driver; device++) + { + /* ignore what we (should) have found already */ + if (device->alive || device->ctlr >= 0) + continue; + if (configure_bus_device(device->name, device->address, + device->phys_address, i, "atbus")) + i++; + } + +#if MACH_TTD + /* + * Initialize Remote kernel debugger. + */ + ttd_init(); +#endif /* MACH_TTD */ +} + +void take_dev_irq( + const struct bus_device *dev) +{ + int pic = (int)dev->sysdep1; + + if (ivect[pic] == intnull) { + iunit[pic] = dev->unit; + ivect[pic] = dev->intr; + } else { + printf("The device below will clobber IRQ %d (%p).\n", pic, ivect[pic]); + printf("You have two devices at the same IRQ.\n"); + printf("This won't work. Reconfigure your hardware and try again.\n"); + printf("%s%d: port = %zx, spl = %zd, pic = %d.\n", + dev->name, dev->unit, dev->address, + dev->sysdep, dev->sysdep1); + while (1); + } + + unmask_irq(pic); +} diff --git a/i386/i386at/autoconf.h b/i386/i386at/autoconf.h new file mode 100644 index 0000000..81fc5da --- /dev/null +++ b/i386/i386at/autoconf.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Barry deFreese. + */ +/* + * Device auto configuration. + * + */ + +#ifndef _AUTOCONF_H_ +#define _AUTOCONF_H_ + +#include +#include + +/* + * probeio: + * + * Probe and subsequently attach devices out on the AT bus. + * + * + */ +void probeio(void); + +void take_dev_irq( + const struct bus_device *dev); + +#endif /* _AUTOCONF_H_ */ diff --git a/i386/i386at/biosmem.c b/i386/i386at/biosmem.c new file mode 100644 index 0000000..937c0e3 --- /dev/null +++ b/i386/i386at/biosmem.c @@ -0,0 +1,1070 @@ +/* + * Copyright (c) 2010-2014 Richard Braun. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG 0 + +#define __boot +#define __bootdata +#define __init + +#define boot_memmove memmove +#define boot_panic(s) panic("%s", s) +#define boot_strlen strlen + +#define BOOT_CGAMEM phystokv(0xb8000) +#define BOOT_CGACHARS (80 * 25) +#define BOOT_CGACOLOR 0x7 + +#define BIOSMEM_MAX_BOOT_DATA 64 + +/* + * Boot data descriptor. + * + * The start and end addresses must not be page-aligned, since there + * could be more than one range inside a single page. + */ +struct biosmem_boot_data { + phys_addr_t start; + phys_addr_t end; + boolean_t temporary; +}; + +/* + * Sorted array of boot data descriptors. + */ +static struct biosmem_boot_data biosmem_boot_data_array[BIOSMEM_MAX_BOOT_DATA] + __bootdata; +static unsigned int biosmem_nr_boot_data __bootdata; + +/* + * Maximum number of entries in the BIOS memory map. + * + * Because of adjustments of overlapping ranges, the memory map can grow + * to twice this size. + */ +#define BIOSMEM_MAX_MAP_SIZE 128 + +/* + * Memory range types. + */ +#define BIOSMEM_TYPE_AVAILABLE 1 +#define BIOSMEM_TYPE_RESERVED 2 +#define BIOSMEM_TYPE_ACPI 3 +#define BIOSMEM_TYPE_NVS 4 +#define BIOSMEM_TYPE_UNUSABLE 5 +#define BIOSMEM_TYPE_DISABLED 6 + +/* + * Bitmask corresponding to memory ranges that require narrowing + * to page boundaries. + */ +#define BIOSMEM_MASK_NARROW (((1u << BIOSMEM_TYPE_AVAILABLE) | \ + (1u << BIOSMEM_TYPE_NVS) | \ + (1u << BIOSMEM_TYPE_DISABLED))) + +/* + * Helper macro to test if range type needs narrowing. + */ +#define BIOSMEM_NEEDS_NARROW(t) ((1u << t) & BIOSMEM_MASK_NARROW) + +/* + * Memory map entry. + */ +struct biosmem_map_entry { + uint64_t base_addr; + uint64_t length; + unsigned int type; +}; + +/* + * Memory map built from the information passed by the boot loader. + * + * If the boot loader didn't pass a valid memory map, a simple map is built + * based on the mem_lower and mem_upper multiboot fields. + */ +static struct biosmem_map_entry biosmem_map[BIOSMEM_MAX_MAP_SIZE * 2] + __bootdata; +static unsigned int biosmem_map_size __bootdata; + +/* + * Contiguous block of physical memory. + */ +struct biosmem_segment { + phys_addr_t start; + phys_addr_t end; +}; + +/* + * Physical segment boundaries. + */ +static struct biosmem_segment biosmem_segments[VM_PAGE_MAX_SEGS] __bootdata; + +/* + * Boundaries of the simple bootstrap heap. + * + * This heap is located above BIOS memory. + */ +static phys_addr_t biosmem_heap_start __bootdata; +static phys_addr_t biosmem_heap_bottom __bootdata; +static phys_addr_t biosmem_heap_top __bootdata; +static phys_addr_t biosmem_heap_end __bootdata; + +/* + * Boot allocation policy. + * + * Top-down allocations are normally preferred to avoid unnecessarily + * filling the DMA segment. + */ +static boolean_t biosmem_heap_topdown __bootdata; + +static char biosmem_panic_inval_boot_data[] __bootdata + = "biosmem: invalid boot data"; +static char biosmem_panic_too_many_boot_data[] __bootdata + = "biosmem: too many boot data ranges"; +static char biosmem_panic_too_big_msg[] __bootdata + = "biosmem: too many memory map entries"; +#ifndef MACH_HYP +static char biosmem_panic_setup_msg[] __bootdata + = "biosmem: unable to set up the early memory allocator"; +#endif /* MACH_HYP */ +static char biosmem_panic_noseg_msg[] __bootdata + = "biosmem: unable to find any memory segment"; +static char biosmem_panic_inval_msg[] __bootdata + = "biosmem: attempt to allocate 0 page"; +static char biosmem_panic_nomem_msg[] __bootdata + = "biosmem: unable to allocate memory"; + +void __boot +biosmem_register_boot_data(phys_addr_t start, phys_addr_t end, + boolean_t temporary) +{ + unsigned int i; + + if (start >= end) { + boot_panic(biosmem_panic_inval_boot_data); + } + + if (biosmem_nr_boot_data == ARRAY_SIZE(biosmem_boot_data_array)) { + boot_panic(biosmem_panic_too_many_boot_data); + } + + for (i = 0; i < biosmem_nr_boot_data; i++) { + /* Check if the new range overlaps */ + if ((end > biosmem_boot_data_array[i].start) + && (start < biosmem_boot_data_array[i].end)) { + + /* + * If it does, check whether it's part of another range. + * For example, this applies to debugging symbols directly + * taken from the kernel image. + */ + if ((start >= biosmem_boot_data_array[i].start) + && (end <= biosmem_boot_data_array[i].end)) { + + /* + * If it's completely included, make sure that a permanent + * range remains permanent. + * + * XXX This means that if one big range is first registered + * as temporary, and a smaller range inside of it is + * registered as permanent, the bigger range becomes + * permanent. It's not easy nor useful in practice to do + * better than that. + */ + if (biosmem_boot_data_array[i].temporary != temporary) { + biosmem_boot_data_array[i].temporary = FALSE; + } + + return; + } + + boot_panic(biosmem_panic_inval_boot_data); + } + + if (end <= biosmem_boot_data_array[i].start) { + break; + } + } + + boot_memmove(&biosmem_boot_data_array[i + 1], + &biosmem_boot_data_array[i], + (biosmem_nr_boot_data - i) * sizeof(*biosmem_boot_data_array)); + + biosmem_boot_data_array[i].start = start; + biosmem_boot_data_array[i].end = end; + biosmem_boot_data_array[i].temporary = temporary; + biosmem_nr_boot_data++; +} + +static void __init +biosmem_unregister_boot_data(phys_addr_t start, phys_addr_t end) +{ + unsigned int i; + + if (start >= end) { + panic("%s", biosmem_panic_inval_boot_data); + } + + assert(biosmem_nr_boot_data != 0); + + for (i = 0; biosmem_nr_boot_data; i++) { + if ((start == biosmem_boot_data_array[i].start) + && (end == biosmem_boot_data_array[i].end)) { + break; + } + } + + if (i == biosmem_nr_boot_data) { + return; + } + +#if DEBUG + printf("biosmem: unregister boot data: %llx:%llx\n", + (unsigned long long)biosmem_boot_data_array[i].start, + (unsigned long long)biosmem_boot_data_array[i].end); +#endif /* DEBUG */ + + biosmem_nr_boot_data--; + + boot_memmove(&biosmem_boot_data_array[i], + &biosmem_boot_data_array[i + 1], + (biosmem_nr_boot_data - i) * sizeof(*biosmem_boot_data_array)); +} + +#ifndef MACH_HYP + +static void __boot +biosmem_map_adjust_alignment(struct biosmem_map_entry *e) +{ + uint64_t end = e->base_addr + e->length; + + if (BIOSMEM_NEEDS_NARROW(e->type)) { + e->base_addr = vm_page_round (e->base_addr); + e->length = vm_page_trunc (end) - e->base_addr; + } +} + +static void __boot +biosmem_map_build(const struct multiboot_raw_info *mbi) +{ + struct multiboot_raw_mmap_entry *mb_entry, *mb_end; + struct biosmem_map_entry *start, *entry, *end; + unsigned long addr; + + addr = phystokv(mbi->mmap_addr); + mb_entry = (struct multiboot_raw_mmap_entry *)addr; + mb_end = (struct multiboot_raw_mmap_entry *)(addr + mbi->mmap_length); + start = biosmem_map; + entry = start; + end = entry + BIOSMEM_MAX_MAP_SIZE; + + while ((mb_entry < mb_end) && (entry < end)) { + entry->base_addr = mb_entry->base_addr; + entry->length = mb_entry->length; + entry->type = mb_entry->type; + + mb_entry = (void *)mb_entry + sizeof(mb_entry->size) + mb_entry->size; + + biosmem_map_adjust_alignment(entry); + entry++; + } + + biosmem_map_size = entry - start; +} + +static void __boot +biosmem_map_build_simple(const struct multiboot_raw_info *mbi) +{ + struct biosmem_map_entry *entry; + + entry = biosmem_map; + entry->base_addr = 0; + entry->length = mbi->mem_lower << 10; + entry->type = BIOSMEM_TYPE_AVAILABLE; + biosmem_map_adjust_alignment(entry); + + entry++; + entry->base_addr = BIOSMEM_END; + entry->length = mbi->mem_upper << 10; + entry->type = BIOSMEM_TYPE_AVAILABLE; + biosmem_map_adjust_alignment(entry); + + biosmem_map_size = 2; +} + +#endif /* MACH_HYP */ + +static int __boot +biosmem_map_entry_is_invalid(const struct biosmem_map_entry *entry) +{ + return (entry->base_addr + entry->length) <= entry->base_addr; +} + +static void __boot +biosmem_map_filter(void) +{ + struct biosmem_map_entry *entry; + unsigned int i; + + i = 0; + + while (i < biosmem_map_size) { + entry = &biosmem_map[i]; + + if (biosmem_map_entry_is_invalid(entry)) { + biosmem_map_size--; + boot_memmove(entry, entry + 1, + (biosmem_map_size - i) * sizeof(*entry)); + continue; + } + + i++; + } +} + +static void __boot +biosmem_map_sort(void) +{ + struct biosmem_map_entry tmp; + unsigned int i, j; + + /* + * Simple insertion sort. + */ + for (i = 1; i < biosmem_map_size; i++) { + tmp = biosmem_map[i]; + + for (j = i - 1; j < i; j--) { + if (biosmem_map[j].base_addr < tmp.base_addr) + break; + + biosmem_map[j + 1] = biosmem_map[j]; + } + + biosmem_map[j + 1] = tmp; + } +} + +static void __boot +biosmem_map_adjust(void) +{ + struct biosmem_map_entry tmp, *a, *b, *first, *second; + uint64_t a_end, b_end, last_end; + unsigned int i, j, last_type; + + biosmem_map_filter(); + + /* + * Resolve overlapping areas, giving priority to most restrictive + * (i.e. numerically higher) types. + */ + for (i = 0; i < biosmem_map_size; i++) { + a = &biosmem_map[i]; + a_end = a->base_addr + a->length; + + j = i + 1; + + while (j < biosmem_map_size) { + b = &biosmem_map[j]; + b_end = b->base_addr + b->length; + + if ((a->base_addr >= b_end) || (a_end <= b->base_addr)) { + j++; + continue; + } + + if (a->base_addr < b->base_addr) { + first = a; + second = b; + } else { + first = b; + second = a; + } + + if (a_end > b_end) { + last_end = a_end; + last_type = a->type; + } else { + last_end = b_end; + last_type = b->type; + } + + tmp.base_addr = second->base_addr; + tmp.length = MIN(a_end, b_end) - tmp.base_addr; + tmp.type = MAX(a->type, b->type); + first->length = tmp.base_addr - first->base_addr; + second->base_addr += tmp.length; + second->length = last_end - second->base_addr; + second->type = last_type; + + /* + * Filter out invalid entries. + */ + if (biosmem_map_entry_is_invalid(a) + && biosmem_map_entry_is_invalid(b)) { + *a = tmp; + biosmem_map_size--; + memmove(b, b + 1, (biosmem_map_size - j) * sizeof(*b)); + continue; + } else if (biosmem_map_entry_is_invalid(a)) { + *a = tmp; + j++; + continue; + } else if (biosmem_map_entry_is_invalid(b)) { + *b = tmp; + j++; + continue; + } + + if (tmp.type == a->type) + first = a; + else if (tmp.type == b->type) + first = b; + else { + + /* + * If the overlapping area can't be merged with one of its + * neighbors, it must be added as a new entry. + */ + + if (biosmem_map_size >= ARRAY_SIZE(biosmem_map)) + boot_panic(biosmem_panic_too_big_msg); + + biosmem_map[biosmem_map_size] = tmp; + biosmem_map_size++; + j++; + continue; + } + + if (first->base_addr > tmp.base_addr) + first->base_addr = tmp.base_addr; + + first->length += tmp.length; + j++; + } + } + + biosmem_map_sort(); +} + +/* + * Find addresses of physical memory within a given range. + * + * This function considers the memory map with the [*phys_start, *phys_end] + * range on entry, and returns the lowest address of physical memory + * in *phys_start, and the highest address of unusable memory immediately + * following physical memory in *phys_end. + * + * These addresses are normally used to establish the range of a segment. + */ +static int __boot +biosmem_map_find_avail(phys_addr_t *phys_start, phys_addr_t *phys_end) +{ + const struct biosmem_map_entry *entry, *map_end; + phys_addr_t seg_start, seg_end; + uint64_t start, end; + + seg_start = (phys_addr_t)-1; + seg_end = (phys_addr_t)-1; + map_end = biosmem_map + biosmem_map_size; + + for (entry = biosmem_map; entry < map_end; entry++) { + if (entry->type != BIOSMEM_TYPE_AVAILABLE) + continue; + + start = vm_page_round(entry->base_addr); + + if (start >= *phys_end) + break; + + end = vm_page_trunc(entry->base_addr + entry->length); + + if ((start < end) && (start < *phys_end) && (end > *phys_start)) { + if (seg_start == (phys_addr_t)-1) + seg_start = start; + + seg_end = end; + } + } + + if ((seg_start == (phys_addr_t)-1) || (seg_end == (phys_addr_t)-1)) + return -1; + + if (seg_start > *phys_start) + *phys_start = seg_start; + + if (seg_end < *phys_end) + *phys_end = seg_end; + + return 0; +} + +static void __boot +biosmem_set_segment(unsigned int seg_index, phys_addr_t start, phys_addr_t end) +{ + biosmem_segments[seg_index].start = start; + biosmem_segments[seg_index].end = end; +} + +static phys_addr_t __boot +biosmem_segment_end(unsigned int seg_index) +{ + return biosmem_segments[seg_index].end; +} + +static phys_addr_t __boot +biosmem_segment_size(unsigned int seg_index) +{ + return biosmem_segments[seg_index].end - biosmem_segments[seg_index].start; +} + +static int __boot +biosmem_find_avail_clip(phys_addr_t *avail_start, phys_addr_t *avail_end, + phys_addr_t data_start, phys_addr_t data_end) +{ + phys_addr_t orig_end; + + assert(data_start < data_end); + + orig_end = data_end; + data_start = vm_page_trunc(data_start); + data_end = vm_page_round(data_end); + + if (data_end < orig_end) { + boot_panic(biosmem_panic_inval_boot_data); + } + + if ((data_end <= *avail_start) || (data_start >= *avail_end)) { + return 0; + } + + if (data_start > *avail_start) { + *avail_end = data_start; + } else { + if (data_end >= *avail_end) { + return -1; + } + + *avail_start = data_end; + } + + return 0; +} + +/* + * Find available memory in the given range. + * + * The search starts at the given start address, up to the given end address. + * If a range is found, it is stored through the avail_startp and avail_endp + * pointers. + * + * The range boundaries are page-aligned on return. + */ +static int __boot +biosmem_find_avail(phys_addr_t start, phys_addr_t end, + phys_addr_t *avail_start, phys_addr_t *avail_end) +{ + phys_addr_t orig_start; + unsigned int i; + int error; + + assert(start <= end); + + orig_start = start; + start = vm_page_round(start); + end = vm_page_trunc(end); + + if ((start < orig_start) || (start >= end)) { + return -1; + } + + *avail_start = start; + *avail_end = end; + + for (i = 0; i < biosmem_nr_boot_data; i++) { + error = biosmem_find_avail_clip(avail_start, avail_end, + biosmem_boot_data_array[i].start, + biosmem_boot_data_array[i].end); + + if (error) { + return -1; + } + } + + return 0; +} + +#ifndef MACH_HYP + +static void __boot +biosmem_setup_allocator(const struct multiboot_raw_info *mbi) +{ + phys_addr_t heap_start, heap_end, max_heap_start, max_heap_end; + phys_addr_t start, end; + int error; + + /* + * Find some memory for the heap. Look for the largest unused area in + * upper memory, carefully avoiding all boot data. + */ + end = vm_page_trunc((mbi->mem_upper + 1024) << 10); + + if (end > VM_PAGE_DIRECTMAP_LIMIT) + end = VM_PAGE_DIRECTMAP_LIMIT; + + max_heap_start = 0; + max_heap_end = 0; + start = BIOSMEM_END; + + for (;;) { + error = biosmem_find_avail(start, end, &heap_start, &heap_end); + + if (error) { + break; + } + + if ((heap_end - heap_start) > (max_heap_end - max_heap_start)) { + max_heap_start = heap_start; + max_heap_end = heap_end; + } + + start = heap_end; + } + + if (max_heap_start >= max_heap_end) + boot_panic(biosmem_panic_setup_msg); + + biosmem_heap_start = max_heap_start; + biosmem_heap_end = max_heap_end; + biosmem_heap_bottom = biosmem_heap_start; + biosmem_heap_top = biosmem_heap_end; + biosmem_heap_topdown = TRUE; + + /* Prevent biosmem_free_usable() from releasing the heap */ + biosmem_register_boot_data(biosmem_heap_start, biosmem_heap_end, FALSE); +} + +#endif /* MACH_HYP */ + +static void __boot +biosmem_bootstrap_common(void) +{ + phys_addr_t phys_start, phys_end; + int error; + + biosmem_map_adjust(); + + phys_start = BIOSMEM_BASE; + phys_end = VM_PAGE_DMA_LIMIT; + error = biosmem_map_find_avail(&phys_start, &phys_end); + + if (error) + boot_panic(biosmem_panic_noseg_msg); + +#if !defined(MACH_HYP) && NCPUS > 1 + /* + * Grab an early page for AP boot code which needs to be below 1MB. + */ + assert (phys_start < 0x100000); + apboot_addr = phys_start; + phys_start += PAGE_SIZE; +#endif + + biosmem_set_segment(VM_PAGE_SEG_DMA, phys_start, phys_end); + + phys_start = VM_PAGE_DMA_LIMIT; + +#ifdef VM_PAGE_DMA32_LIMIT +#if VM_PAGE_DMA32_LIMIT < VM_PAGE_DIRECTMAP_LIMIT + phys_end = VM_PAGE_DMA32_LIMIT; + error = biosmem_map_find_avail(&phys_start, &phys_end); + + if (error) + return; + + biosmem_set_segment(VM_PAGE_SEG_DMA32, phys_start, phys_end); + + phys_start = VM_PAGE_DMA32_LIMIT; +#endif +#endif /* VM_PAGE_DMA32_LIMIT */ + + phys_end = VM_PAGE_DIRECTMAP_LIMIT; + error = biosmem_map_find_avail(&phys_start, &phys_end); + + if (error) + return; + + biosmem_set_segment(VM_PAGE_SEG_DIRECTMAP, phys_start, phys_end); + + phys_start = VM_PAGE_DIRECTMAP_LIMIT; + +#ifdef VM_PAGE_DMA32_LIMIT +#if VM_PAGE_DMA32_LIMIT > VM_PAGE_DIRECTMAP_LIMIT + phys_end = VM_PAGE_DMA32_LIMIT; + error = biosmem_map_find_avail(&phys_start, &phys_end); + + if (error) + return; + + biosmem_set_segment(VM_PAGE_SEG_DMA32, phys_start, phys_end); + + phys_start = VM_PAGE_DMA32_LIMIT; +#endif +#endif /* VM_PAGE_DMA32_LIMIT */ + + phys_end = VM_PAGE_HIGHMEM_LIMIT; + error = biosmem_map_find_avail(&phys_start, &phys_end); + + if (error) + return; + + biosmem_set_segment(VM_PAGE_SEG_HIGHMEM, phys_start, phys_end); +} + +#ifdef MACH_HYP + +void +biosmem_xen_bootstrap(void) +{ + struct biosmem_map_entry *entry; + + entry = biosmem_map; + entry->base_addr = 0; + entry->length = boot_info.nr_pages << PAGE_SHIFT; + entry->type = BIOSMEM_TYPE_AVAILABLE; + + biosmem_map_size = 1; + + biosmem_bootstrap_common(); + + biosmem_heap_start = _kvtophys(boot_info.pt_base) + + (boot_info.nr_pt_frames + 3) * 0x1000; + biosmem_heap_end = boot_info.nr_pages << PAGE_SHIFT; + +#ifndef __LP64__ + if (biosmem_heap_end > VM_PAGE_DIRECTMAP_LIMIT) + biosmem_heap_end = VM_PAGE_DIRECTMAP_LIMIT; +#endif /* __LP64__ */ + + biosmem_heap_bottom = biosmem_heap_start; + biosmem_heap_top = biosmem_heap_end; + + /* + * XXX Allocation on Xen are initially bottom-up : + * At the "start of day", only 512k are available after the boot + * data. The pmap module then creates a 4g mapping so all physical + * memory is available, but it uses this allocator to do so. + * Therefore, it must return pages from this small 512k regions + * first. + */ + biosmem_heap_topdown = FALSE; + + /* + * Prevent biosmem_free_usable() from releasing the Xen boot information + * and the heap. + */ + biosmem_register_boot_data(0, biosmem_heap_end, FALSE); +} + +#else /* MACH_HYP */ + +void __boot +biosmem_bootstrap(const struct multiboot_raw_info *mbi) +{ + if (mbi->flags & MULTIBOOT_LOADER_MMAP) + biosmem_map_build(mbi); + else + biosmem_map_build_simple(mbi); + + biosmem_bootstrap_common(); + biosmem_setup_allocator(mbi); +} + +#endif /* MACH_HYP */ + +unsigned long __boot +biosmem_bootalloc(unsigned int nr_pages) +{ + unsigned long addr, size; + + size = vm_page_ptoa(nr_pages); + + if (size == 0) + boot_panic(biosmem_panic_inval_msg); + + if (biosmem_heap_topdown) { + addr = biosmem_heap_top - size; + + if ((addr < biosmem_heap_start) || (addr > biosmem_heap_top)) { + boot_panic(biosmem_panic_nomem_msg); + } + + biosmem_heap_top = addr; + } else { + unsigned long end; + + addr = biosmem_heap_bottom; + end = addr + size; + + if ((end > biosmem_heap_end) || (end < biosmem_heap_bottom)) { + boot_panic(biosmem_panic_nomem_msg); + } + + biosmem_heap_bottom = end; + } + + return addr; +} + +phys_addr_t __boot +biosmem_directmap_end(void) +{ + if (biosmem_segment_size(VM_PAGE_SEG_DIRECTMAP) != 0) + return biosmem_segment_end(VM_PAGE_SEG_DIRECTMAP); +#if defined(VM_PAGE_DMA32_LIMIT) && (VM_PAGE_DMA32_LIMIT < VM_PAGE_DIRECTMAP_LIMIT) + if (biosmem_segment_size(VM_PAGE_SEG_DMA32) != 0) + return biosmem_segment_end(VM_PAGE_SEG_DMA32); +#endif + return biosmem_segment_end(VM_PAGE_SEG_DMA); +} + +static const char * __init +biosmem_type_desc(unsigned int type) +{ + switch (type) { + case BIOSMEM_TYPE_AVAILABLE: + return "available"; + case BIOSMEM_TYPE_RESERVED: + return "reserved"; + case BIOSMEM_TYPE_ACPI: + return "ACPI"; + case BIOSMEM_TYPE_NVS: + return "ACPI NVS"; + case BIOSMEM_TYPE_UNUSABLE: + return "unusable"; + default: + return "unknown (reserved)"; + } +} + +static void __init +biosmem_map_show(void) +{ + const struct biosmem_map_entry *entry, *end; + + printf("biosmem: physical memory map:\n"); + + for (entry = biosmem_map, end = entry + biosmem_map_size; + entry < end; + entry++) + printf("biosmem: %018"PRIx64":%018"PRIx64", %s\n", entry->base_addr, + entry->base_addr + entry->length, + biosmem_type_desc(entry->type)); + +#if DEBUG + printf("biosmem: heap: %llx:%llx\n", + (unsigned long long)biosmem_heap_start, + (unsigned long long)biosmem_heap_end); +#endif +} + +static void __init +biosmem_load_segment(struct biosmem_segment *seg, uint64_t max_phys_end) +{ + phys_addr_t phys_start, phys_end, avail_start, avail_end; + unsigned int seg_index; + + phys_start = seg->start; + phys_end = seg->end; + seg_index = seg - biosmem_segments; + + if (phys_end > max_phys_end) { + if (max_phys_end <= phys_start) { + printf("biosmem: warning: segment %s physically unreachable, " + "not loaded\n", vm_page_seg_name(seg_index)); + return; + } + + printf("biosmem: warning: segment %s truncated to %#"PRIx64"\n", + vm_page_seg_name(seg_index), max_phys_end); + phys_end = max_phys_end; + } + + vm_page_load(seg_index, phys_start, phys_end); + + /* + * Clip the remaining available heap to fit it into the loaded + * segment if possible. + */ + + if ((biosmem_heap_top > phys_start) && (biosmem_heap_bottom < phys_end)) { + if (biosmem_heap_bottom >= phys_start) { + avail_start = biosmem_heap_bottom; + } else { + avail_start = phys_start; + } + + if (biosmem_heap_top <= phys_end) { + avail_end = biosmem_heap_top; + } else { + avail_end = phys_end; + } + + vm_page_load_heap(seg_index, avail_start, avail_end); + } +} + +void __init +biosmem_setup(void) +{ + struct biosmem_segment *seg; + unsigned int i; + + biosmem_map_show(); + + for (i = 0; i < ARRAY_SIZE(biosmem_segments); i++) { + if (biosmem_segment_size(i) == 0) + break; + + seg = &biosmem_segments[i]; + biosmem_load_segment(seg, VM_PAGE_HIGHMEM_LIMIT); + } +} + +static void __init +biosmem_unregister_temporary_boot_data(void) +{ + struct biosmem_boot_data *data; + unsigned int i; + + for (i = 0; i < biosmem_nr_boot_data; i++) { + data = &biosmem_boot_data_array[i]; + + if (!data->temporary) { + continue; + } + + biosmem_unregister_boot_data(data->start, data->end); + i = (unsigned int)-1; + } +} + +static void __init +biosmem_free_usable_range(phys_addr_t start, phys_addr_t end) +{ + struct vm_page *page; + +#if DEBUG + printf("biosmem: release to vm_page: %llx:%llx (%lluk)\n", + (unsigned long long)start, (unsigned long long)end, + (unsigned long long)((end - start) >> 10)); +#endif + + while (start < end) { + page = vm_page_lookup_pa(start); + assert(page != NULL); + vm_page_manage(page); + start += PAGE_SIZE; + } +} + +static void __init +biosmem_free_usable_entry(phys_addr_t start, phys_addr_t end) +{ + phys_addr_t avail_start, avail_end; + int error; + + for (;;) { + error = biosmem_find_avail(start, end, &avail_start, &avail_end); + + if (error) { + break; + } + + biosmem_free_usable_range(avail_start, avail_end); + start = avail_end; + } +} + +void __init +biosmem_free_usable(void) +{ + struct biosmem_map_entry *entry; + uint64_t start, end; + unsigned int i; + + biosmem_unregister_temporary_boot_data(); + + for (i = 0; i < biosmem_map_size; i++) { + entry = &biosmem_map[i]; + + if (entry->type != BIOSMEM_TYPE_AVAILABLE) + continue; + + start = vm_page_round(entry->base_addr); + + if (start >= VM_PAGE_HIGHMEM_LIMIT) + break; + + end = vm_page_trunc(entry->base_addr + entry->length); + + if (end > VM_PAGE_HIGHMEM_LIMIT) { + end = VM_PAGE_HIGHMEM_LIMIT; + } + + if (start < BIOSMEM_BASE) + start = BIOSMEM_BASE; + + if (start >= end) { + continue; + } + + biosmem_free_usable_entry(start, end); + } +} + +boolean_t +biosmem_addr_available(phys_addr_t addr) +{ + struct biosmem_map_entry *entry; + unsigned i; + + if (addr < BIOSMEM_BASE) + return FALSE; + + for (i = 0; i < biosmem_map_size; i++) { + entry = &biosmem_map[i]; + + if (addr >= entry->base_addr && addr < entry->base_addr + entry->length) + return entry->type == BIOSMEM_TYPE_AVAILABLE; + } + return FALSE; +} diff --git a/i386/i386at/biosmem.h b/i386/i386at/biosmem.h new file mode 100644 index 0000000..76ab23a --- /dev/null +++ b/i386/i386at/biosmem.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2010-2014 Richard Braun. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef _X86_BIOSMEM_H +#define _X86_BIOSMEM_H + +#include +#include + +/* + * Address where the address of the Extended BIOS Data Area segment can be + * found. + */ +#define BIOSMEM_EBDA_PTR 0x40e + +/* + * Significant low memory addresses. + * + * The first 64 KiB are reserved for various reasons (e.g. to preserve BIOS + * data and to work around data corruption on some hardware). + */ +#define BIOSMEM_BASE 0x010000 +#define BIOSMEM_BASE_END 0x0a0000 +#define BIOSMEM_EXT_ROM 0x0e0000 +#define BIOSMEM_ROM 0x0f0000 +#define BIOSMEM_END 0x100000 + +/* + * Report reserved addresses to the biosmem module. + * + * Once all boot data have been registered, the user can set up the + * early page allocator. + * + * If the range is marked temporary, it will be unregistered when + * biosmem_free_usable() is called, so that pages that used to store + * these boot data may be released to the VM system. + */ +void biosmem_register_boot_data(phys_addr_t start, phys_addr_t end, + boolean_t temporary); + +/* + * Initialize the early page allocator. + * + * This function uses the memory map provided by the boot loader along + * with the registered boot data addresses to set up a heap of free pages + * of physical memory. + * + * Note that on Xen, this function registers all the Xen boot information + * as boot data itself. + */ +#ifdef MACH_HYP +void biosmem_xen_bootstrap(void); +#else /* MACH_HYP */ +void biosmem_bootstrap(const struct multiboot_raw_info *mbi); +#endif /* MACH_HYP */ + +/* + * Allocate contiguous physical pages during bootstrap. + * + * The pages returned are guaranteed to be part of the direct physical + * mapping when paging is enabled. + * + * This function should only be used to allocate initial page table pages. + * Those pages are later loaded into the VM system (as reserved pages) + * which means they can be freed like other regular pages. Users should + * fix up the type of those pages once the VM system is initialized. + */ +unsigned long biosmem_bootalloc(unsigned int nr_pages); + +/* + * Return the limit of physical memory that can be directly mapped. + */ +phys_addr_t biosmem_directmap_end(void); + +/* + * Set up physical memory based on the information obtained during bootstrap + * and load it in the VM system. + */ +void biosmem_setup(void); + +/* + * Free all usable memory. + * + * This function releases all pages that aren't used by boot data and have + * not already been loaded into the VM system. + */ +void biosmem_free_usable(void); + +/* + * Tell whether this address is marked as available in the biosmem and thus used + * for usable memory. + */ +boolean_t biosmem_addr_available(phys_addr_t addr); + +#endif /* _X86_BIOSMEM_H */ diff --git a/i386/i386at/boothdr.S b/i386/i386at/boothdr.S new file mode 100644 index 0000000..daaf57d --- /dev/null +++ b/i386/i386at/boothdr.S @@ -0,0 +1,179 @@ + +#include +#include +#include +#include + + /* + * This section will be put first into .text. See also i386/ldscript. + */ + .section .text.start,"ax" + + /* We should never be entered this way. */ + .globl start,_start +start: +_start: + jmp boot_entry + + /* MultiBoot header - see multiboot.h. */ +#define MULTIBOOT_MAGIC 0x1BADB002 +#ifdef __ELF__ +#define MULTIBOOT_FLAGS 0x00000003 +#else /* __ELF__ */ +#define MULTIBOOT_FLAGS 0x00010003 +#endif /* __ELF__ */ + P2ALIGN(2) +boot_hdr: + .long MULTIBOOT_MAGIC + .long MULTIBOOT_FLAGS + /* + * The next item here is the checksum. + * XX this works OK until we need at least the 30th bit. + */ + .long - (MULTIBOOT_MAGIC+MULTIBOOT_FLAGS) +#ifndef __ELF__ /* a.out kludge */ + .long boot_hdr /* header_addr */ + .long _start /* load_addr */ + .long _edata /* load_end_addr */ + .long _end /* bss_end_addr */ + .long boot_entry /* entry */ +#endif /* __ELF__ */ + +boot_entry: + movl $percpu_array - KERNELBASE, %eax + movw %ax, boot_percpu_low - KERNELBASE + shr $16, %eax + movb %al, boot_percpu_med - KERNELBASE + shr $8, %ax + movb %al, boot_percpu_high - KERNELBASE + + /* use segmentation to offset ourself. */ + lgdt boot_gdt_descr - KERNELBASE + ljmp $0x8,$0f +0: + movw $0x0,%ax + movw %ax,%ds + movw %ax,%es + movw %ax,%fs + movw %ax,%gs + movw $0x10,%ax + movw %ax,%ds + movw %ax,%es + movw %ax,%ss + movw $0x68,%ax + movw %ax,%gs + + /* Switch to our own interrupt stack. */ + movl $solid_intstack+INTSTACK_SIZE-4, %esp + andl $0xfffffff0,%esp + + /* Enable local apic in xAPIC mode */ + xorl %eax, %eax + xorl %edx, %edx + movl $APIC_MSR, %ecx + rdmsr + orl $APIC_MSR_ENABLE, %eax + orl $APIC_MSR_BSP, %eax + andl $(~APIC_MSR_X2APIC), %eax + movl $APIC_MSR, %ecx + wrmsr + + /* Reset EFLAGS to a known state. */ + pushl $0 + popf + + /* Clear uninitialized data. */ + lea _edata,%edi + lea _end,%ecx + subl %edi,%ecx + xorl %eax,%eax + rep + stosb + + /* Push the boot_info pointer to be the second argument. */ + pushl %ebx + + /* Fix ifunc entries */ + movl $__rel_iplt_start,%esi + movl $__rel_iplt_end,%edi +iplt_cont: + cmpl %edi,%esi + jae iplt_done + movl (%esi),%ebx /* r_offset */ + movb 4(%esi),%al /* info */ + cmpb $42,%al /* IRELATIVE */ + jnz iplt_next + call *(%ebx) /* call ifunc */ + movl %eax,(%ebx) /* fixed address */ +iplt_next: + addl $8,%esi + jmp iplt_cont +iplt_done: + + /* Jump into C code. */ + call EXT(c_boot_entry) + +.align 16 + .word 0 +boot_gdt_descr: + .word 14*8-1 + .long boot_gdt - KERNELBASE +.align 16 +boot_gdt: + /* 0 */ + .quad 0 + + /* boot CS = 0x08 */ + .word 0xffff + .word (-KERNELBASE) & 0xffff + .byte ((-KERNELBASE) >> 16) & 0xff + .byte ACC_PL_K | ACC_CODE_R | ACC_P + .byte ((SZ_32 | SZ_G) << 4) | 0xf + .byte ((-KERNELBASE) >> 24) & 0xff + + /* boot DS = 0x10 */ + .word 0xffff + .word (-KERNELBASE) & 0xffff + .byte ((-KERNELBASE) >> 16) & 0xff + .byte ACC_PL_K | ACC_DATA_W | ACC_P + .byte ((SZ_32 | SZ_G) << 4) | 0xf + .byte ((-KERNELBASE) >> 24) & 0xff + + /* LDT = 0x18 */ + .quad 0 + + /* TSS = 0x20 */ + .quad 0 + + /* USER_LDT = 0x28 */ + .quad 0 + + /* USER_TSS = 0x30 */ + .quad 0 + + /* LINEAR = 0x38 */ + .quad 0 + + /* FPREGS = 0x40 */ + .quad 0 + + /* USER_GDT = 0x48 and 0x50 */ + .quad 0 + .quad 0 + + /* USER_TSS64 = 0x58 */ + .quad 0 + + /* USER_TSS64 = 0x60 */ + .quad 0 + + /* boot GS = 0x68 */ + .word 0xffff +boot_percpu_low: + .word 0 +boot_percpu_med: + .byte 0 + .byte ACC_PL_K | ACC_DATA_W | ACC_P + .byte ((SZ_32 | SZ_G) << 4) | 0xf +boot_percpu_high: + .byte 0 diff --git a/i386/i386at/com.c b/i386/i386at/com.c new file mode 100644 index 0000000..bfe353c --- /dev/null +++ b/i386/i386at/com.c @@ -0,0 +1,900 @@ +/* + * Mach Operating System + * Copyright (c) 1994,1993,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. + */ + +#if NCOM > 0 + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +static void comparam(int); + +static vm_offset_t com_std[NCOM] = { 0 }; +struct bus_device *cominfo[NCOM]; +struct bus_driver comdriver = { + comprobe, 0, comattach, 0, com_std, "com", cominfo, 0, 0, 0}; + +struct tty com_tty[NCOM]; +int commodem[NCOM]; +int comcarrier[NCOM] = {0, 0,}; +boolean_t comfifo[NCOM]; +boolean_t comtimer_active; +int comtimer_state[NCOM]; + +#define RCBAUD B115200 +static int rcline = -1; +static struct bus_device *comcndev; + +/* XX */ +extern char *kernel_cmdline; + +#define ISPEED B115200 +#define IFLAGS (EVENP|ODDP|ECHO|CRMOD|XTABS|LITOUT) + +u_short divisorreg[] = { + 0, 2304, 1536, 1047, /* 0, 50, 75, 110*/ + 857, 768, 576, 384, 192, /* 134.5, 150, 200, 300, 600*/ + 96, 64, 48, /* 1200, 1800, 2000, 2400 */ + 24, 12, /* 3600, 4800, 7200, 9600 */ + 6, 3, 2, 1}; /* 19200, 38400, 56000,115200 */ + + +/* + * + * Probes are called during kernel boot: return 1 to mean that + * the relevant device is present today. + * + */ +static int +comprobe_general(struct bus_device *dev, int noisy) +{ + u_short addr = dev->address; + int unit = dev->unit; + int oldctl, oldmsb; + char *type = "8250"; + int i; + + if ((unit < 0) || (unit >= NCOM)) { + printf("com %d out of range\n", unit); + return(0); + } + oldctl = inb(LINE_CTL(addr)); /* Save old value of LINE_CTL */ + oldmsb = inb(BAUD_MSB(addr)); /* Save old value of BAUD_MSB */ + outb(LINE_CTL(addr), 0); /* Select INTR_ENAB */ + outb(BAUD_MSB(addr), 0); + if (inb(BAUD_MSB(addr)) != 0) + { + outb(LINE_CTL(addr), oldctl); + outb(BAUD_MSB(addr), oldmsb); + return 0; + } + outb(LINE_CTL(addr), iDLAB); /* Select BAUD_MSB */ + outb(BAUD_MSB(addr), 255); + if (inb(BAUD_MSB(addr)) != 255) + { + outb(LINE_CTL(addr), oldctl); + outb(BAUD_MSB(addr), oldmsb); + return 0; + } + outb(LINE_CTL(addr), 0); /* Select INTR_ENAB */ + if (inb(BAUD_MSB(addr)) != 0) /* Check that it has kept its value*/ + { + outb(LINE_CTL(addr), oldctl); + outb(BAUD_MSB(addr), oldmsb); + return 0; + } + + /* Com port found, now check what chip it has */ + + for(i = 0; i < 256; i++) /* Is there Scratch register */ + { + outb(SCR(addr), i); + if (inb(SCR(addr)) != i) + break; + } + if (i == 256) + { /* Yes == 450 or 460 */ + outb(SCR(addr), 0); + type = "82450 or 16450"; + outb(FIFO_CTL(addr), iFIFOENA | iFIFO14CH); /* Enable fifo */ + if ((inb(FIFO_CTL(addr)) & iFIFO14CH) != 0) + { /* Was it successful */ + /* if both bits are not set then broken xx550 */ + if ((inb(FIFO_CTL(addr)) & iFIFO14CH) == iFIFO14CH) + { + type = "82550 or 16550"; + comfifo[unit] = TRUE; + } + else + { + type = "82550 or 16550 with non-working FIFO"; + } + outb(INTR_ID(addr), 0x00); /* Disable fifos */ + } + } + if (noisy) + printf("com%d: %s chip.\n", unit, type); + return 1; +} + +/* + * Probe routine for use during kernel startup when it is probing + * all of bus_device_init + */ +int +comprobe(vm_offset_t port, struct bus_ctlr *dev) +{ + return comprobe_general((struct bus_device *)dev, /*noisy*/ 0); +} + +/* + * Probe routine for use by the console + */ +int +comcnprobe(struct consdev *cp) +{ + struct bus_device *b; + int maj, unit, pri; + +#define CONSOLE_PARAMETER " console=com" + u_char *console = (u_char *) strstr(kernel_cmdline, CONSOLE_PARAMETER); + + if (console) + mach_atoi(console + strlen(CONSOLE_PARAMETER), &rcline); + + if (strncmp(kernel_cmdline, CONSOLE_PARAMETER + 1, + strlen(CONSOLE_PARAMETER) - 1) == 0) + mach_atoi((u_char*)kernel_cmdline + strlen(CONSOLE_PARAMETER) - 1, + &rcline); + + maj = 0; + unit = -1; + pri = CN_DEAD; + + for (b = bus_device_init; b->driver; b++) + if (strcmp(b->name, "com") == 0 + && b->unit == rcline + && comprobe_general(b, /*quiet*/ 0)) + { + /* Found one */ + comcndev = b; + unit = b->unit; + pri = CN_REMOTE; + break; + } + + cp->cn_dev = makedev(maj, unit); + cp->cn_pri = pri; + + return 0; +} + + +/* + * + * Device Attach's are called during kernel boot, but only if the matching + * device Probe returned a 1. + * + */ +void +comattach(struct bus_device *dev) +{ + u_char unit = dev->unit; + u_short addr = dev->address; + + if (unit >= NCOM) { + printf(", disabled by NCOM configuration\n"); + return; + } + + take_dev_irq(dev); + printf(", port = %zx, spl = %zu, pic = %d. (DOS COM%d)", + dev->address, dev->sysdep, dev->sysdep1, unit+1); + +/* comcarrier[unit] = addr->flags;*/ + commodem[unit] = 0; + + outb(INTR_ENAB(addr), 0); + outb(MODEM_CTL(addr), 0); + while (!(inb(INTR_ID(addr))&1)) { + (void) inb(LINE_STAT (addr)); /* reset overrun error etc */ + (void) inb(TXRX (addr)); /* reset data-ready */ + (void) inb(MODEM_STAT(addr)); /* reset modem status reg */ + } +} + +/* + * Attach/init routine for console. This isn't called by + * configure_bus_device which sets the alive, adaptor, and minfo + * fields of the bus_device struct (comattach is), therefore we do + * that by hand. + */ +int +comcninit(struct consdev *cp) +{ + u_char unit = comcndev->unit; + u_short addr = comcndev->address; + + take_dev_irq(comcndev); + + comcndev->alive = 1; + comcndev->adaptor = 0; + cominfo[minor(cp->cn_dev)] = comcndev; + + outb(LINE_CTL(addr), iDLAB); + outb(BAUD_LSB(addr), divisorreg[RCBAUD] & 0xff); + outb(BAUD_MSB(addr), divisorreg[RCBAUD] >>8); + outb(LINE_CTL(addr), i8BITS); + outb(INTR_ENAB(addr), 0); + outb(MODEM_CTL(addr), iDTR|iRTS|iOUT2); + + { + char msg[128]; + volatile unsigned char *p = (volatile unsigned char *)phystokv(0xb8000); + int i; + + sprintf(msg, " **** using COM port %d for console ****", + unit+1); + for (i = 0; msg[i]; i++) { + p[2*i] = msg[i]; + p[2*i+1] = (0<<7) /* blink */ + | (0x0<<4) /* bg */ + | (1<<3) /* hi-intensity */ + | 0x4; /* fg */ + } + } + + return 0; +} + +/* + * Probe for COM after autoconfiguration. + * Used to handle PCMCIA modems, which may appear + * at any time. + */ +static boolean_t com_reprobe( + int unit) +{ + struct bus_device *device; + + /* + * Look for COM device in the device + * initialization list. It must not be alive + * (otherwise we would have opened it already). + */ + for (device = bus_device_init; device->driver; device++) { + if (device->driver == &comdriver && device->unit == unit && + !device->alive && device->ctlr == (char)-1) + { + /* + * Found an entry for com port . + * Probe it. + */ + if (configure_bus_device(device->name, + device->address, + device->phys_address, + 0, + "atbus")) + return TRUE; + } + } + return FALSE; +} + +io_return_t comopen( + dev_t dev, + int flag, + io_req_t ior) +{ + int unit = minor(dev); + u_short addr; + struct bus_device *isai; + struct tty *tp; + spl_t s; + io_return_t result; + + if (unit >= NCOM) + return D_NO_SUCH_DEVICE; /* no such device */ + if ((isai = cominfo[unit]) == 0 || isai->alive == 0) { + /* + * Try to probe it again + */ + if (!com_reprobe(unit)) + return D_NO_SUCH_DEVICE; + if ((isai = cominfo[unit]) == 0 || isai->alive == 0) + return D_NO_SUCH_DEVICE; + } + tp = &com_tty[unit]; + + if ((tp->t_state & (TS_ISOPEN|TS_WOPEN)) == 0) { + ttychars(tp); + tp->t_addr = (char *)isai->address; + tp->t_dev = dev; + tp->t_oproc = comstart; + tp->t_stop = comstop; + tp->t_mctl = commctl; + tp->t_getstat = comgetstat; + tp->t_setstat = comsetstat; + if (tp->t_ispeed == 0) { + tp->t_ispeed = ISPEED; + tp->t_ospeed = ISPEED; + tp->t_flags = IFLAGS; + tp->t_state &= ~TS_BUSY; + } + } +/*rvb tp->t_state |= TS_WOPEN; */ + if ((tp->t_state & TS_ISOPEN) == 0) + comparam(unit); + addr = (uintptr_t)tp->t_addr; + + s = spltty(); + if (!comcarrier[unit]) /* not originating */ + tp->t_state |= TS_CARR_ON; + else { + int modem_stat = inb(MODEM_STAT(addr)); + if (modem_stat & iRLSD) + tp->t_state |= TS_CARR_ON; + else + tp->t_state &= ~TS_CARR_ON; + fix_modem_state(unit, modem_stat); + } + splx(s); + + result = char_open(dev, tp, flag, ior); + + if (!comtimer_active) { + comtimer_active = TRUE; + comtimer(NULL); + } + + s = spltty(); + while(!(inb(INTR_ID(addr))&1)) { /* while pending interrupts */ + (void) inb(LINE_STAT (addr)); /* reset overrun error */ + (void) inb(TXRX (addr)); /* reset data-ready */ + (void) inb(MODEM_STAT(addr)); /* reset modem status */ + } + splx(s); + return result; +} + +void comclose(dev_t dev, int flag) +{ + struct tty *tp = &com_tty[minor(dev)]; + u_short addr = (uintptr_t)tp->t_addr; + + ttyclose(tp); + if (tp->t_state&TS_HUPCLS || (tp->t_state&TS_ISOPEN)==0) { + outb(INTR_ENAB(addr), 0); + outb(MODEM_CTL(addr), 0); + tp->t_state &= ~TS_BUSY; + commodem[minor(dev)] = 0; + if (comfifo[minor(dev)] != 0) + outb(INTR_ID(addr), 0x00); /* Disable fifos */ + } + return; +} + +io_return_t comread(dev_t dev, io_req_t ior) +{ + return char_read(&com_tty[minor(dev)], ior); +} + +io_return_t comwrite(dev_t dev, io_req_t ior) +{ + return char_write(&com_tty[minor(dev)], ior); +} + +io_return_t comportdeath(dev_t dev, mach_port_t port) +{ + return (tty_portdeath(&com_tty[minor(dev)], (ipc_port_t)port)); +} + +io_return_t +comgetstat( + dev_t dev, + dev_flavor_t flavor, + dev_status_t data, /* pointer to OUT array */ + mach_msg_type_number_t *count /* out */ + ) +{ + io_return_t result = D_SUCCESS; + int unit = minor(dev); + + switch (flavor) { + case TTY_MODEM: + fix_modem_state(unit, inb(MODEM_STAT(cominfo[unit]->address))); + *data = commodem[unit]; + *count = 1; + break; + default: + result = tty_get_status(&com_tty[unit], flavor, data, count); + break; + } + return (result); +} + +io_return_t +comsetstat( + dev_t dev, + dev_flavor_t flavor, + dev_status_t data, + mach_msg_type_number_t count) +{ + io_return_t result = D_SUCCESS; + int unit = minor(dev); + struct tty *tp = &com_tty[unit]; + + switch (flavor) { + case TTY_SET_BREAK: + commctl(tp, TM_BRK, DMBIS); + break; + case TTY_CLEAR_BREAK: + commctl(tp, TM_BRK, DMBIC); + break; + case TTY_MODEM: + commctl(tp, *data, DMSET); + break; + default: + result = tty_set_status(&com_tty[unit], flavor, data, count); + if (result == D_SUCCESS && flavor == TTY_STATUS) + comparam(unit); + return (result); + } + return (D_SUCCESS); +} + +void +comintr(int unit) +{ + struct tty *tp = &com_tty[unit]; + u_short addr = cominfo[unit]->address; + static char comoverrun = 0; + char c, line, intr_id; + int line_stat; + + while (! ((intr_id=(inb(INTR_ID(addr))&MASKi)) & 1)) + switch (intr_id) { + case MODi: + /* modem change */ + commodem_intr(unit, inb(MODEM_STAT(addr))); + break; + + case TRAi: + comtimer_state[unit] = 0; + tp->t_state &= ~(TS_BUSY|TS_FLUSH); + tt_write_wakeup(tp); + (void) comstart(tp); + break; + case RECi: + case CTIi: /* Character timeout indication */ + if (tp->t_state&TS_ISOPEN) { + int escape = 0; + while ((line = inb(LINE_STAT(addr))) & iDR) { + c = inb(TXRX(addr)); + + if (c == 0x1b) { + escape = 1; + continue; + } + +#if MACH_KDB + if (escape && c == 'D'-('A'-1)) + /* ctrl-alt-d pressed, + invoke debugger */ + kdb_kintr(); + else +#endif /* MACH_KDB */ + if (escape) { + ttyinput(0x1b, tp); + ttyinput(c, tp); + } + else + ttyinput(c, tp); + + escape = 0; + } + + if (escape) + /* just escape */ + ttyinput(0x1b, tp); + } else + tt_open_wakeup(tp); + break; + case LINi: + line_stat = inb(LINE_STAT(addr)); + + if ((line_stat & iPE) && + ((tp->t_flags&(EVENP|ODDP)) == EVENP || + (tp->t_flags&(EVENP|ODDP)) == ODDP)) { + /* parity error */; + } else if (line_stat&iOR && !comoverrun) { + printf("com%d: overrun\n", unit); + comoverrun = 1; + } else if (line_stat & (iFE | iBRKINTR)) { + /* framing error or break */ + ttyinput(tp->t_breakc, tp); + } + break; + } +} + +static void +comparam(int unit) +{ + struct tty *tp = &com_tty[unit]; + u_short addr = (uintptr_t)tp->t_addr; + spl_t s = spltty(); + int mode; + + if (tp->t_ispeed == B0) { + tp->t_state |= TS_HUPCLS; + outb(MODEM_CTL(addr), iOUT2); + commodem[unit] = 0; + splx(s); + return; + } + + /* Do input buffering */ + if (tp->t_ispeed >= B300) + tp->t_state |= TS_MIN; + + outb(LINE_CTL(addr), iDLAB); + outb(BAUD_LSB(addr), divisorreg[tp->t_ispeed] & 0xff); + outb(BAUD_MSB(addr), divisorreg[tp->t_ispeed] >> 8); + + if (tp->t_flags & (RAW|LITOUT|PASS8)) + mode = i8BITS; + else + mode = i7BITS | iPEN; + if (tp->t_flags & EVENP) + mode |= iEPS; + if (tp->t_ispeed == B110) + /* + * 110 baud uses two stop bits - + * all other speeds use one + */ + mode |= iSTB; + + outb(LINE_CTL(addr), mode); + + outb(INTR_ENAB(addr), iTX_ENAB|iRX_ENAB|iMODEM_ENAB|iERROR_ENAB); + if (comfifo[unit]) + outb(FIFO_CTL(addr), iFIFOENA|iFIFO14CH); + outb(MODEM_CTL(addr), iDTR|iRTS|iOUT2); + commodem[unit] |= (TM_DTR|TM_RTS); + splx(s); +} + +int comst_1, comst_2, comst_3, comst_4, comst_5 = 14; + +void +comstart(struct tty *tp) +{ + int nch; +#if 0 + int i; +#endif + + if (tp->t_state & (TS_TIMEOUT|TS_TTSTOP|TS_BUSY)) { +comst_1++; + return; + } + if ((!queue_empty(&tp->t_delayed_write)) && + (tp->t_outq.c_cc <= TTLOWAT(tp))) { +comst_2++; + tt_write_wakeup(tp); + } + if (!tp->t_outq.c_cc) { +comst_3++; + return; + } + +#if 0 + i = (comfifo[minor(tp->t_dev)]) ? /*14*/comst_5 : 1; + + tp->t_state |= TS_BUSY; + while (i-- > 0) { + nch = getc(&tp->t_outq); + if (nch == -1) break; + if ((nch & 0200) && ((tp->t_flags & LITOUT) == 0)) { + timeout(ttrstrt, (char *)tp, (nch & 0x7f) + 6); + tp->t_state |= TS_TIMEOUT; +comst_4++; + return(0); + } + outb(TXRX((uintptr_t)tp->t_addr), nch); + } +#else + nch = getc(&tp->t_outq); + if (nch == -1) + return; + if ((nch & 0200) && ((tp->t_flags & LITOUT) == 0)) { + timeout((timer_func_t *)ttrstrt, (char *)tp, (nch & 0x7f) + 6); + tp->t_state |= TS_TIMEOUT; +comst_4++; + return; + } + outb(TXRX((uintptr_t)tp->t_addr), nch); + tp->t_state |= TS_BUSY; +#endif +} + +/* Check for stuck xmitters */ +int comtimer_interval = 5; + +void +comtimer(void * param) +{ + spl_t s = spltty(); + struct tty *tp = com_tty; + int i, nch; + + for (i = 0; i < NCOM; i++, tp++) { + if ((tp->t_state & TS_ISOPEN) == 0) + continue; + if (!tp->t_outq.c_cc) + continue; + if (++comtimer_state[i] < 2) + continue; + /* Its stuck */ +printf("Tty %p was stuck\n", tp); + nch = getc(&tp->t_outq); + outb(TXRX((uintptr_t)tp->t_addr), nch); + } + + splx(s); + timeout(comtimer, 0, comtimer_interval*hz); +} + +/* + * Set receive modem state from modem status register. + */ +void +fix_modem_state( + int unit, + int modem_stat) +{ + int stat = 0; + + if (modem_stat & iCTS) + stat |= TM_CTS; /* clear to send */ + if (modem_stat & iDSR) + stat |= TM_DSR; /* data set ready */ + if (modem_stat & iRI) + stat |= TM_RNG; /* ring indicator */ + if (modem_stat & iRLSD) + stat |= TM_CAR; /* carrier? */ + + commodem[unit] = (commodem[unit] & ~(TM_CTS|TM_DSR|TM_RNG|TM_CAR)) + | stat; +} + +/* + * Modem change (input signals) + */ +void +commodem_intr( + int unit, + int stat) +{ + int changed; + + changed = commodem[unit]; + fix_modem_state(unit, stat); + stat = commodem[unit]; + + /* Assumption: if the other party can handle + modem signals then it should handle all + the necessary ones. Else fix the cable. */ + + changed ^= stat; /* what changed ? */ + + if (changed & TM_CTS) + tty_cts( &com_tty[unit], stat & TM_CTS ); + +#if 0 + if (changed & TM_CAR) + ttymodem( &com_tty[unit], stat & TM_CAR ); +#endif + +} + +/* + * Set/get modem bits + */ +int +commctl( + struct tty *tp, + int bits, + int how) +{ + spl_t s; + int unit; + vm_offset_t dev_addr; + int b = 0; /* Suppress gcc warning */ + + unit = minor(tp->t_dev); + + if (bits == TM_HUP) { /* close line (internal) */ + bits = TM_DTR | TM_RTS; + how = DMBIC; + } + + if (how == DMGET) return commodem[unit]; + + dev_addr = cominfo[unit]->address; + + s = spltty(); + + switch (how) { + case DMSET: + b = bits; break; + case DMBIS: + b = commodem[unit] | bits; break; + case DMBIC: + b = commodem[unit] & ~bits; break; + } + commodem[unit] = b; + + if (bits & TM_BRK) { + if (b & TM_BRK) { + outb(LINE_CTL(dev_addr), inb(LINE_CTL(dev_addr)) | iSETBREAK); + } else { + outb(LINE_CTL(dev_addr), inb(LINE_CTL(dev_addr)) & ~iSETBREAK); + } + } + +#if 0 + /* do I need to do something on this ? */ + if (bits & TM_LE) { /* line enable */ + } +#endif +#if 0 + /* Unsupported */ + if (bits & TM_ST) { /* secondary transmit */ + } + if (bits & TM_SR) { /* secondary receive */ + } +#endif + if (bits & (TM_DTR|TM_RTS)) { /* data terminal ready, request to send */ + how = iOUT2; + if (b & TM_DTR) how |= iDTR; + if (b & TM_RTS) how |= iRTS; + outb(MODEM_CTL(dev_addr), how); + } + + splx(s); + + /* the rest are inputs */ + return commodem[unit]; +} + +void +comstop( + struct tty *tp, + int flags) +{ + if ((tp->t_state & TS_BUSY) && (tp->t_state & TS_TTSTOP) == 0) + tp->t_state |= TS_FLUSH; +} + +/* + * + * Code to be called from debugger. + * + */ +void compr_addr(vm_offset_t addr) +{ + /* The two line_stat prints may show different values, since + * touching some of the registers constitutes changing them. + */ + printf("LINE_STAT(%zu) %x\n", + LINE_STAT(addr), inb(LINE_STAT(addr))); + + printf("TXRX(%zu) %x, INTR_ENAB(%zu) %x, INTR_ID(%zu) %x, LINE_CTL(%zu) %x,\n\ +MODEM_CTL(%zu) %x, LINE_STAT(%zu) %x, MODEM_STAT(%zu) %x\n", + TXRX(addr), inb(TXRX(addr)), + INTR_ENAB(addr), inb(INTR_ENAB(addr)), + INTR_ID(addr), inb(INTR_ID(addr)), + LINE_CTL(addr), inb(LINE_CTL(addr)), + MODEM_CTL(addr), inb(MODEM_CTL(addr)), + LINE_STAT(addr), inb(LINE_STAT(addr)), + MODEM_STAT(addr),inb(MODEM_STAT(addr))); +} + +int compr(int unit) +{ + compr_addr(cominfo[unit]->address); + return(0); +} + +int +comgetc(int unit) +{ + u_short addr = (u_short)(cominfo[unit]->address); + spl_t s = spltty(); + int c; + + while((inb(LINE_STAT(addr)) & iDR) == 0) ; + + c = inb(TXRX(addr)); + splx(s); + return c; +} + +/* + * Routines for the console + */ +int +comcnputc(dev_t dev, int c) +{ + u_short addr = (u_short)(cominfo[minor(dev)]->address); + + /* Wait for transmitter to empty */ + while((inb(LINE_STAT(addr)) & iTHRE) == 0) + continue; + + /* send the char */ + if (c == '\n') + comcnputc(dev, '\r'); + outb(addr, c); + + return 0; +} + +int +comcngetc(dev_t dev, int wait) +{ + u_short addr = (u_short)(cominfo[minor(dev)]->address); + int c; + + while((inb(LINE_STAT(addr)) & iDR) == 0) + if (! wait) + return 0; + + c = inb(TXRX(addr)); + return c & 0x7f; +} + +#endif /* NCOM */ diff --git a/i386/i386at/com.h b/i386/i386at/com.h new file mode 100644 index 0000000..3be2930 --- /dev/null +++ b/i386/i386at/com.h @@ -0,0 +1,86 @@ +/* + * Communication functions + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Barry deFreese. + */ +/* + * Communication functions. + * + */ + +#ifndef _COM_H_ +#define _COM_H_ + +#include +#include +#include +#include + +/* + * Set receive modem state from modem status register. + */ +extern void fix_modem_state(int unit, int modem_stat); + +extern void comtimer(void * param); + +/* + * Modem change (input signals) + */ +extern void commodem_intr(int unit, int stat); + +extern int comgetc(int unit); + +extern int comcnprobe(struct consdev *cp); +extern int comcninit(struct consdev *cp); +extern int comcngetc(dev_t dev, int wait); +extern int comcnputc(dev_t dev, int c); +extern void comintr(int unit); + +int comprobe(vm_offset_t port, struct bus_ctlr *dev); +int commctl(struct tty *tp, int bits, int how); +void comstart(struct tty *tp); +void comstop(struct tty *tp, int flags); +void comattach(struct bus_device *dev); + +extern io_return_t +comgetstat( + dev_t dev, + dev_flavor_t flavor, + dev_status_t data, + mach_msg_type_number_t *count); + +extern io_return_t +comsetstat( + dev_t dev, + dev_flavor_t flavor, + dev_status_t data, + mach_msg_type_number_t count); + +#if MACH_KDB +extern void kdb_kintr(void); +extern void compr_addr(vm_offset_t addr); +extern int compr(int unit); +#endif /* MACH_KDB */ + +extern io_return_t comopen(dev_t dev, int flag, io_req_t ior); +extern void comclose(dev_t dev, int flag); +extern io_return_t comread(dev_t dev, io_req_t ior); +extern io_return_t comwrite(dev_t dev, io_req_t ior); +extern io_return_t comportdeath(dev_t dev, mach_port_t port); + +#endif /* _COM_H_ */ diff --git a/i386/i386at/comreg.h b/i386/i386at/comreg.h new file mode 100644 index 0000000..7356574 --- /dev/null +++ b/i386/i386at/comreg.h @@ -0,0 +1,139 @@ +/* + * 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. + */ +/* + * Olivetti serial port driver v1.0 + * Copyright Ing. C. Olivetti & C. S.p.A. 1988, 1989 + * All rights reserved. + * + */ +/* + Copyright 1988, 1989 by Olivetti Advanced Technology Center, Inc., +Cupertino, 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 Olivetti +not be used in advertising or publicity pertaining to distribution +of the software without specific, written prior permission. + + OLIVETTI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, +IN NO EVENT SHALL OLIVETTI 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 OUR OF OR IN CONNECTION +WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#ifndef _COMREG_H_ +#define _COMREG_H_ + +#define TXRX(addr) (addr + 0) +#define BAUD_LSB(addr) (addr + 0) +#define BAUD_MSB(addr) (addr + 1) +#define INTR_ENAB(addr) (addr + 1) +#define INTR_ID(addr) (addr + 2) +#define FIFO_CTL(addr) (addr + 2) +#define LINE_CTL(addr) (addr + 3) +#define MODEM_CTL(addr) (addr + 4) +#define LINE_STAT(addr) (addr + 5) +#define MODEM_STAT(addr)(addr + 6) +#define SCR(addr) (addr + 7) + +#define MODi 0 +#define TRAi 2 +#define RECi 4 +#define LINi 6 +#define CTIi 0xc +#define MASKi 0xf + +/* line control register */ +#define iWLS0 0x01 /*word length select bit 0 */ +#define iWLS1 0x02 /*word length select bit 2 */ +#define iSTB 0x04 /* number of stop bits */ +#define iPEN 0x08 /* parity enable */ +#define iEPS 0x10 /* even parity select */ +#define iSP 0x20 /* stick parity */ +#define iSETBREAK 0x40 /* break key */ +#define iDLAB 0x80 /* divisor latch access bit */ +#define i5BITS 0x00 /* 5 bits per char */ +#define i6BITS 0x01 /* 6 bits per char */ +#define i7BITS 0x02 /* 7 bits per char */ +#define i8BITS 0x03 /* 8 bits per char */ + +/* line status register */ +#define iDR 0x01 /* data ready */ +#define iOR 0x02 /* overrun error */ +#define iPE 0x04 /* parity error */ +#define iFE 0x08 /* framing error */ +#define iBRKINTR 0x10 /* a break has arrived */ +#define iTHRE 0x20 /* tx hold reg is now empty */ +#define iTSRE 0x40 /* tx shift reg is now empty */ + +/* interrupt id regisger */ +#define iMODEM_INTR 0x01 +#define iTX_INTR 0x02 +#define iRX_INTR 0x04 +#define iERROR_INTR 0x08 + +/* interrupt enable register */ +#define iRX_ENAB 0x01 +#define iTX_ENAB 0x02 +#define iERROR_ENAB 0x04 +#define iMODEM_ENAB 0x08 + +/* modem control register */ +#define iDTR 0x01 /* data terminal ready */ +#define iRTS 0x02 /* request to send */ +#define iOUT1 0x04 /* COM aux line -not used */ +#define iOUT2 0x08 /* turns intr to 386 on/off */ +#define iLOOP 0x10 /* loopback for diagnostics */ + +/* modem status register */ +#define iDCTS 0x01 /* delta clear to send */ +#define iDDSR 0x02 /* delta data set ready */ +#define iTERI 0x04 /* trail edge ring indicator */ +#define iDRLSD 0x08 /* delta rx line sig detect */ +#define iCTS 0x10 /* clear to send */ +#define iDSR 0x20 /* data set ready */ +#define iRI 0x40 /* ring indicator */ +#define iRLSD 0x80 /* rx line sig detect */ + +/* fifo control register (only in 16550) */ +#define iFIFOENA 0x01 /* Enable fifos */ +#define iCLRRCVRFIFO 0x02 /* Clear receive fifo */ +#define iCLRXMITFIFO 0x04 /* Clear transmit fifo */ +#define iDMAMODE 0x08 /* DMA transfer enable */ +#define iFIFO1CH 0x00 /* Receive fifo trigger level 1 char */ +#define iFIFO4CH 0x40 /* Receive fifo trigger level 4 chars*/ +#define iFIFO8CH 0x80 /* Receive fifo trigger level 8 chars*/ +#define iFIFO14CH 0xc0 /* Receive fifo trigger level 14 chars*/ + +#endif /* _COMREG_H_ */ diff --git a/i386/i386at/conf.c b/i386/i386at/conf.c new file mode 100644 index 0000000..ecbf1e4 --- /dev/null +++ b/i386/i386at/conf.c @@ -0,0 +1,172 @@ +/* + * Mach Operating System + * Copyright (c) 1993-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. + */ +/* + * Device switch for i386 AT bus. + */ + +#include +#include +#include +#include + +#define timename "time" + +#ifndef MACH_HYP +#include +#define kdname "kd" + +#if NCOM > 0 +#include +#define comname "com" +#endif /* NCOM > 0 */ + +#if NLPR > 0 +#include +#define lprname "lpr" +#endif /* NLPR > 0 */ +#endif /* MACH_HYP */ + +#include +#define kbdname "kbd" + +#ifndef MACH_HYP +#include +#define mousename "mouse" + +#include +#define memname "mem" +#endif /* MACH_HYP */ + +#include +#define kmsgname "kmsg" + +#ifdef MACH_HYP +#include +#define hypcnname "hyp" +#endif /* MACH_HYP */ + +#include +#define irqname "irq" + +/* + * List of devices - console must be at slot 0 + */ +struct dev_ops dev_name_list[] = +{ + /*name, open, close, read, + write, getstat, setstat, mmap, + async_in, reset, port_death, subdev, + dev_info */ + + /* We don't assign a console here, when we find one via + cninit() we stick something appropriate here through the + indirect list */ + { "cn", nulldev_open, nulldev_close, nulldev_read, + nulldev_write, nulldev_getstat, nulldev_setstat, nomap, + nodev_async_in, nulldev_reset, nulldev_portdeath, 0, + nodev_info}, + +#ifndef MACH_HYP +#if ENABLE_IMMEDIATE_CONSOLE + { "immc", nulldev_open, nulldev_close, nulldev_read, + nulldev_write, nulldev_getstat, nulldev_setstat, + nomap, nodev_async_in, nulldev_reset, nulldev_portdeath, 0, + nodev_info }, +#endif /* ENABLE_IMMEDIATE_CONSOLE */ + { kdname, kdopen, kdclose, kdread, + kdwrite, kdgetstat, kdsetstat, kdmmap, + nodev_async_in, nulldev_reset, kdportdeath, 0, + nodev_info }, +#endif /* MACH_HYP */ + + { timename, timeopen, timeclose, nulldev_read, + nulldev_write, nulldev_getstat, nulldev_setstat, timemmap, + nodev_async_in, nulldev_reset, nulldev_portdeath, 0, + nodev_info }, + +#ifndef MACH_HYP +#if NCOM > 0 + { comname, comopen, comclose, comread, + comwrite, comgetstat, comsetstat, nomap, + nodev_async_in, nulldev_reset, comportdeath, 0, + nodev_info }, +#endif + +#ifdef MACH_LPR + { lprname, lpropen, lprclose, lprread, + lprwrite, lprgetstat, lprsetstat, nomap, + nodev_async_in, nulldev_reset, lprportdeath, 0, + nodev_info }, +#endif + + { mousename, mouseopen, mouseclose, mouseread, + nulldev_write, mousegetstat, nulldev_setstat, nomap, + nodev_async_in, nulldev_reset, nulldev_portdeath, 0, + nodev_info }, + + { kbdname, kbdopen, kbdclose, kbdread, + nulldev_write, kbdgetstat, kbdsetstat, nomap, + nodev_async_in, nulldev_reset, nulldev_portdeath, 0, + nodev_info }, + + { memname, nulldev_open, nulldev_close, nulldev_read, + nulldev_write, nulldev_getstat, nulldev_setstat, memmmap, + nodev_async_in, nulldev_reset, nulldev_portdeath, 0, + nodev_info }, +#endif /* MACH_HYP */ + +#ifdef MACH_KMSG + { kmsgname, kmsgopen, kmsgclose, kmsgread, + nulldev_write, kmsggetstat, nulldev_setstat, nomap, + nodev_async_in, nulldev_reset, nulldev_portdeath, 0, + nodev_info }, +#endif + +#ifdef MACH_HYP + { hypcnname, hypcnopen, hypcnclose, hypcnread, + hypcnwrite, hypcngetstat, hypcnsetstat, nomap, + nodev_async_in, nulldev_reset, hypcnportdeath, 0, + nodev_info }, +#endif /* MACH_HYP */ + + { irqname, nulldev_open, nulldev_close, nulldev_read, + nulldev_write,nulldev_getstat,nulldev_setstat, nomap, + nodev_async_in, nulldev_reset, nulldev_portdeath,0, + nodev_info }, + +}; +int dev_name_count = sizeof(dev_name_list)/sizeof(dev_name_list[0]); + +/* + * Indirect list. + */ +struct dev_indirect dev_indirect_list[] = { + + /* console */ + { "console", &dev_name_list[0], 0 } +}; +int dev_indirect_count = sizeof(dev_indirect_list) + / sizeof(dev_indirect_list[0]); diff --git a/i386/i386at/cons_conf.c b/i386/i386at/cons_conf.c new file mode 100644 index 0000000..1d7dd38 --- /dev/null +++ b/i386/i386at/cons_conf.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 1988-1994, The University of Utah and + * the Computer Systems Laboratory at the University of Utah (CSL). + * All rights reserved. + * + * Permission to use, copy, modify and distribute this software is hereby + * granted provided that (1) source code retains these copyright, permission, + * and disclaimer notices, and (2) redistributions including binaries + * reproduce the notices in supporting documentation, and (3) all advertising + * materials mentioning features or use of this software display the following + * acknowledgement: ``This product includes software developed by the + * Computer Systems Laboratory at the University of Utah.'' + * + * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS + * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF + * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * CSL requests users of this software to return to csl-dist@cs.utah.edu any + * improvements that they make and grant CSL redistribution rights. + * + * Utah $Hdr: cons_conf.c 1.7 94/12/14$ + */ + +/* + * This entire table could be autoconfig()ed but that would mean that + * the kernel's idea of the console would be out of sync with that of + * the standalone boot. I think it best that they both use the same + * known algorithm unless we see a pressing need otherwise. + */ +#include +#include + +#ifdef MACH_HYP +#include +#else /* MACH_HYP */ +#include "kd.h" +#if NCOM > 0 +#include "com.h" +#endif +#endif /* MACH_HYP */ + +#if ENABLE_IMMEDIATE_CONSOLE +#include "immc.h" +#endif /* ENABLE_IMMEDIATE_CONSOLE */ + +/* + * The rest of the consdev fields are filled in by the respective + * cnprobe routine. + */ +struct consdev constab[] = { +#ifdef MACH_HYP + {"hyp", hypcnprobe, hypcninit, hypcngetc, hypcnputc}, +#else /* MACH_HYP */ +#if ENABLE_IMMEDIATE_CONSOLE + {"immc", immc_cnprobe, immc_cninit, immc_cngetc, immc_cnputc}, +#endif /* ENABLE_IMMEDIATE_CONSOLE */ + {"kd", kdcnprobe, kdcninit, kdcngetc, kdcnputc}, +#if NCOM > 0 + {"com", comcnprobe, comcninit, comcngetc, comcnputc}, +#endif +#endif /* MACH_HYP */ + {0} +}; diff --git a/i386/i386at/cram.h b/i386/i386at/cram.h new file mode 100644 index 0000000..ac40cf1 --- /dev/null +++ b/i386/i386at/cram.h @@ -0,0 +1,86 @@ +/* + * 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. + */ +/* + * cram.h + */ + +/* + Copyright 1988, 1989 by Olivetti Advanced Technology Center, Inc., +Cupertino, 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 Olivetti +not be used in advertising or publicity pertaining to distribution +of the software without specific, written prior permission. + + OLIVETTI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, +IN NO EVENT SHALL OLIVETTI 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 OUR OF OR IN CONNECTION +WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#ifndef _CRAM_H_ +#define _CRAM_H_ + +/* XXX: this conflicts with read/writing the RTC */ + +/* + * outb(CMOS_ADDR, addr); + * result = inb(CMOS_DATA); + * + * where "addr" tells what value you want to read (some are listed + * below). Interrupts should be disabled while you do this. + */ + +/* I/O ports */ + +#define CMOS_ADDR 0x70 /* port for CMOS ram address */ +#define CMOS_DATA 0x71 /* port for CMOS ram data */ + + +/* Addresses, related masks, and potential results */ + +#define CMOS_SHUTDOWN 0xf +#define CM_NORM_RST 0x0 +#define CM_LOAD_SYS 0x4 +#define CM_JMP_467 0xa + +#define CMOS_EB 0x14 /* read Equipment Byte */ +#define CM_SCRMSK 0x30 /* mask for EB query to get screen */ +#define CM_EGA_VGA 0x00 /* "not CGA or MONO" */ +#define CM_CGA_40 0x10 +#define CM_CGA_80 0x20 +#define CM_MONO_80 0x30 + +#endif /* _CRAM_H_ */ diff --git a/i386/i386at/disk.h b/i386/i386at/disk.h new file mode 100644 index 0000000..c558375 --- /dev/null +++ b/i386/i386at/disk.h @@ -0,0 +1,89 @@ +/* + * 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. + */ +/* + Copyright 1988, 1989 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. +*/ + +/* + * disk.h + */ + +#ifndef _DISK_H_ +#define _DISK_H_ + +#define V_NUMPAR 16 /* maximum number of partitions */ + +#define VTOC_SANE 0x600DDEEE /* Indicates a sane VTOC */ +#define PDLOCATION 29 /* location of VTOC */ + +#define LBLLOC 1 /* label block for xxxbsd */ + +struct localpartition { + u_int p_flag; /*permision flags*/ + long p_start; /*physical start sector no of partition*/ + long p_size; /*# of physical sectors in partition*/ +}; +typedef struct localpartition localpartition_t; + +struct evtoc { + u_int fill0[6]; + u_int cyls; /*number of cylinders per drive*/ + u_int tracks; /*number tracks per cylinder*/ + u_int sectors; /*number sectors per track*/ + u_int fill1[13]; + u_int version; /*layout version*/ + u_int alt_ptr; /*byte offset of alternates table*/ + u_short alt_len; /*byte length of alternates table*/ + u_int sanity; /*to verify vtoc sanity*/ + u_int xcyls; /*number of cylinders per drive*/ + u_int xtracks; /*number tracks per cylinder*/ + u_int xsectors; /*number sectors per track*/ + u_short nparts; /*number of partitions*/ + u_short fill2; /*pad for 286 compiler*/ + char label[40]; + struct localpartition part[V_NUMPAR];/*partition headers*/ + char fill[512-352]; +}; + +#endif /* _DISK_H_ */ diff --git a/i386/i386at/elf.h b/i386/i386at/elf.h new file mode 100644 index 0000000..26f4d87 --- /dev/null +++ b/i386/i386at/elf.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2013 Richard Braun. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef _X86_ELF_H +#define _X86_ELF_H + +#define ELF_SHT_SYMTAB 2 +#define ELF_SHT_STRTAB 3 + +struct elf_shdr { + unsigned int name; + unsigned int type; + unsigned int flags; + unsigned long addr; + unsigned long offset; + unsigned int size; + unsigned int link; + unsigned int info; + unsigned int addralign; + unsigned int entsize; +}; + +#ifdef __LP64__ + +struct elf_sym { + unsigned int name; + unsigned char info; + unsigned char other; + unsigned short shndx; + unsigned long value; + unsigned long size; +}; + +#else /* __LP64__ */ + +struct elf_sym { + unsigned int name; + unsigned long value; + unsigned long size; + unsigned char info; + unsigned char other; + unsigned short shndx; +}; + +#endif /* __LP64__ */ + +#endif /* _X86_ELF_H */ diff --git a/i386/i386at/i8250.h b/i386/i386at/i8250.h new file mode 100644 index 0000000..9b8a801 --- /dev/null +++ b/i386/i386at/i8250.h @@ -0,0 +1,134 @@ +/* + * 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. + */ +/* + Copyright 1988, 1989 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. +*/ + +/* + * Header file for i8250 chip + */ + +#ifndef _I8250_H_ +#define _I8250_H_ + +/* port offsets from the base i/o address */ + +#define RDAT 0 +#define RIE 1 +#define RID 2 +#define RFC 2 +#define RLC 3 +#define RMC 4 +#define RLS 5 +#define RMS 6 +#define RDLSB 0 +#define RDMSB 1 + +/* interrupt control register */ + +#define IERD 0x01 /* read int */ +#define IETX 0x02 /* xmit int */ +#define IELS 0x04 /* line status int */ +#define IEMS 0x08 /* modem int */ + +/* interrupt status register */ + +#define IDIP 0x01 /* not interrupt pending */ +#define IDMS 0x00 /* modem int */ +#define IDTX 0x02 /* xmit int */ +#define IDRD 0x04 /* read int */ +#define IDLS 0x06 /* line status int */ +#define IDMASK 0x0f /* interrupt ID mask */ + +/* line control register */ + +#define LC5 0x00 /* word length 5 */ +#define LC6 0x01 /* word length 6 */ +#define LC7 0x02 /* word length 7 */ +#define LC8 0x03 /* word length 8 */ +#define LCSTB 0x04 /* 2 stop */ +#define LCPEN 0x08 /* parity enable */ +#define LCEPS 0x10 /* even parity select */ +#define LCSP 0x20 /* stick parity */ +#define LCBRK 0x40 /* send break */ +#define LCDLAB 0x80 /* divisor latch access bit */ +#define LCPAR 0x38 /* parity mask */ + +/* line status register */ + +#define LSDR 0x01 /* data ready */ +#define LSOR 0x02 /* overrun error */ +#define LSPE 0x04 /* parity error */ +#define LSFE 0x08 /* framing error */ +#define LSBI 0x10 /* break interrupt */ +#define LSTHRE 0x20 /* xmit holding reg empty */ +#define LSTSRE 0x40 /* xmit shift reg empty */ + +/* modem control register */ + +#define MCDTR 0x01 /* DTR */ +#define MCRTS 0x02 /* RTS */ +#define MCOUT1 0x04 /* OUT1 */ +#define MCOUT2 0x08 /* OUT2 */ +#define MCLOOP 0x10 /* loopback */ + +/* modem status register */ + +#define MSDCTS 0x01 /* delta CTS */ +#define MSDDSR 0x02 /* delta DSR */ +#define MSTERI 0x04 /* delta RE */ +#define MSDRLSD 0x08 /* delta CD */ +#define MSCTS 0x10 /* CTS */ +#define MSDSR 0x20 /* DSR */ +#define MSRI 0x40 /* RE */ +#define MSRLSD 0x80 /* CD */ + +/* divisor latch register settings for various baud rates */ + +#define BCNT1200 0x60 +#define BCNT2400 0x30 +#define BCNT4800 0x18 +#define BCNT9600 0x0c + +#endif /* _I8250_H_ */ diff --git a/i386/i386at/idt.h b/i386/i386at/idt.h new file mode 100644 index 0000000..19e0abe --- /dev/null +++ b/i386/i386at/idt.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 1994 The University of Utah and + * the Computer Systems Laboratory at the University of Utah (CSL). + * All rights reserved. + * + * Permission to use, copy, modify and distribute this software is hereby + * granted provided that (1) source code retains these copyright, permission, + * and disclaimer notices, and (2) redistributions including binaries + * reproduce the notices in supporting documentation, and (3) all advertising + * materials mentioning features or use of this software display the following + * acknowledgement: ``This product includes software developed by the + * Computer Systems Laboratory at the University of Utah.'' + * + * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS + * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF + * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * CSL requests users of this software to return to csl-dist@cs.utah.edu any + * improvements that they make and grant CSL redistribution rights. + * + * Author: Bryan Ford, University of Utah CSL + */ + +#ifndef _I386AT_IDT_ +#define _I386AT_IDT_ + +/* There are 256 interrupt vectors on x86, + * the first 32 are taken by cpu faults */ +#define IDTSZ (0x100) + +/* PIC sits at 0x20-0x2f */ +#define PIC_INT_BASE 0x20 + +/* IOAPIC sits at 0x30-0x47 */ +#define IOAPIC_INT_BASE 0x30 + +/* IOAPIC spurious interrupt vector set to 0xff */ +#define IOAPIC_SPURIOUS_BASE 0xff + +/* Remote -> local AST requests */ +#define CALL_AST_CHECK 0xfa + +/* Currently for TLB shootdowns */ +#define CALL_PMAP_UPDATE 0xfb + +#include + +#ifndef __ASSEMBLER__ +extern void idt_init (void); +extern void ap_idt_init (int cpu); +#endif /* __ASSEMBLER__ */ + +#endif /* _I386AT_IDT_ */ diff --git a/i386/i386at/immc.c b/i386/i386at/immc.c new file mode 100644 index 0000000..00fc973 --- /dev/null +++ b/i386/i386at/immc.c @@ -0,0 +1,134 @@ +/* + * Copyright (c) 1995-1994 The University of Utah and + * the Computer Systems Laboratory at the University of Utah (CSL). + * All rights reserved. + * + * Permission to use, copy, modify and distribute this software is hereby + * granted provided that (1) source code retains these copyright, permission, + * and disclaimer notices, and (2) redistributions including binaries + * reproduce the notices in supporting documentation, and (3) all advertising + * materials mentioning features or use of this software display the following + * acknowledgement: ``This product includes software developed by the + * Computer Systems Laboratory at the University of Utah.'' + * + * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS + * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF + * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * CSL requests users of this software to return to csl-dist@cs.utah.edu any + * improvements that they make and grant CSL redistribution rights. + * + * Author: Bryan Ford, University of Utah CSL + */ + +#if ENABLE_IMMEDIATE_CONSOLE + +#include +#include +#include +#include + +/* This is a special "feature" (read: kludge) + intended for use only for kernel debugging. + It enables an extremely simple console output mechanism + that sends text straight to CGA/EGA/VGA video memory. + It has the nice property of being functional right from the start, + so it can be used to debug things that happen very early + before any devices are initialized. */ + +boolean_t immediate_console_enable = TRUE; + +/* + * XXX we assume that pcs *always* have a console + */ +int +immc_cnprobe(struct consdev *cp) +{ + int maj, unit, pri; + + maj = 0; + unit = 0; + pri = CN_INTERNAL; + + cp->cn_dev = makedev(maj, unit); + cp->cn_pri = pri; + return 0; +} + +int +immc_cninit(struct consdev *cp) +{ + return 0; +} + +int immc_cnmaygetc(void) +{ + return -1; +} + +int +immc_cngetc(dev_t dev, int wait) +{ + if (wait) { + int c; + while ((c = immc_cnmaygetc()) < 0) + continue; + return c; + } + else + return immc_cnmaygetc(); +} + +int +immc_cnputc(dev_t dev, int c) +{ + static int ofs = -1; + + if (!immediate_console_enable) + return -1; + if (ofs < 0 || ofs >= 80) + { + ofs = 0; + immc_cnputc(dev, '\n'); + } + + if (c == '\n') + { + memmove((void *) phystokv(0xb8000), + (void *) phystokv(0xb8000+80*2), 80*2*24); + memset((void *) phystokv((0xb8000+80*2*24)), 0, 80*2); + ofs = 0; + } + else if (c == '\r') + { + ofs = 0; + } + else if (c == '\t') + { + ofs = (ofs & ~7) + 8; + } + else + { + volatile unsigned char *p; + + if (ofs >= 80) + { + immc_cnputc(dev, '\r'); + immc_cnputc(dev, '\n'); + } + + p = (void *) phystokv(0xb8000 + 80*2*24 + ofs*2); + p[0] = c; + p[1] = 0x0f; + ofs++; + } + return 0; +} + +void +immc_romputc(char c) +{ + immc_cnputc (0, c); +} + +#endif /* ENABLE_IMMEDIATE_CONSOLE */ diff --git a/i386/i386at/immc.h b/i386/i386at/immc.h new file mode 100644 index 0000000..dc802c8 --- /dev/null +++ b/i386/i386at/immc.h @@ -0,0 +1,31 @@ +/* Declarations for the immediate console. + + Copyright (C) 2015 Free Software Foundation, Inc. + + This file is part of the GNU Mach. + + The GNU Mach is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Mach is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the GNU Mach. If not, see . */ + +#ifndef _IMMC_H_ +#define _IMMC_H_ + +#include + +int immc_cnprobe(struct consdev *cp); +int immc_cninit(struct consdev *cp); +int immc_cngetc(dev_t dev, int wait); +int immc_cnputc(dev_t dev, int c); +void immc_romputc(char c); + +#endif /* _IMMC_H_ */ diff --git a/i386/i386at/int_init.c b/i386/i386at/int_init.c new file mode 100644 index 0000000..5c8fce6 --- /dev/null +++ b/i386/i386at/int_init.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 1994 The University of Utah and + * the Computer Systems Laboratory at the University of Utah (CSL). + * All rights reserved. + * + * Permission to use, copy, modify and distribute this software is hereby + * granted provided that (1) source code retains these copyright, permission, + * and disclaimer notices, and (2) redistributions including binaries + * reproduce the notices in supporting documentation, and (3) all advertising + * materials mentioning features or use of this software display the following + * acknowledgement: ``This product includes software developed by the + * Computer Systems Laboratory at the University of Utah.'' + * + * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS + * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF + * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * CSL requests users of this software to return to csl-dist@cs.utah.edu any + * improvements that they make and grant CSL redistribution rights. + * + * Author: Bryan Ford, University of Utah CSL + */ + +#include +#include +#include +#include +#include +#ifdef APIC +#include +#endif + +/* defined in locore.S */ +extern vm_offset_t int_entry_table[]; + +static void +int_fill(struct real_gate *myidt) +{ + int i; +#ifndef APIC + int base = PIC_INT_BASE; + int nirq = 16; +#else + int base = IOAPIC_INT_BASE; + int nirq = NINTR; +#endif + + for (i = 0; i < nirq; i++) { + fill_idt_gate(myidt, base + i, + int_entry_table[i], KERNEL_CS, + ACC_PL_K|ACC_INTR_GATE, 0); + } +#if NCPUS > 1 + fill_idt_gate(myidt, CALL_AST_CHECK, + int_entry_table[i], KERNEL_CS, + ACC_PL_K|ACC_INTR_GATE, 0); + i++; + fill_idt_gate(myidt, CALL_PMAP_UPDATE, + int_entry_table[i], KERNEL_CS, + ACC_PL_K|ACC_INTR_GATE, 0); + i++; +#endif +#ifdef APIC + fill_idt_gate(myidt, IOAPIC_SPURIOUS_BASE, + int_entry_table[i], KERNEL_CS, + ACC_PL_K|ACC_INTR_GATE, 0); + i++; +#endif +} + +void +int_init(void) +{ + int_fill(idt); +} + +#if NCPUS > 1 +void ap_int_init(int cpu) +{ + int_fill(mp_desc_table[cpu]->idt); +} +#endif diff --git a/i386/i386at/int_init.h b/i386/i386at/int_init.h new file mode 100644 index 0000000..3c11ebc --- /dev/null +++ b/i386/i386at/int_init.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Barry deFreese. + */ +/* + * Initialization functions. + * + */ + +#ifndef _INT_INIT_H_ +#define _INT_INIT_H_ + +#include + +#ifndef __ASSEMBLER__ +extern void int_init (void); +extern void ap_int_init (int cpu); +#endif /* __ASSEMBLER__ */ + +#endif /* _INT_INIT_H_ */ diff --git a/i386/i386at/interrupt.S b/i386/i386at/interrupt.S new file mode 100644 index 0000000..77424b4 --- /dev/null +++ b/i386/i386at/interrupt.S @@ -0,0 +1,142 @@ +/* + * Copyright (c) 1995 Shantanu Goel + * 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. + * + * THE AUTHOR ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. THE AUTHOR DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + */ + +#include + +#include +#ifdef APIC +# include +#else +# include +#endif +#include + +#define READ_ISR (OCW_TEMPLATE|READ_NEXT_RD|READ_IS_ONRD) + +/* + * Generic interrupt handler. + * + * On entry, %eax contains the irq number. + * + * Note: kdb_kintr needs to know our stack usage + */ + +#define S_REGS 28(%esp) +#define S_RET 24(%esp) +#define S_IRQ 20(%esp) +#define S_IPL 16(%esp) + +ENTRY(interrupt) +#ifdef APIC + cmpl $255,%eax /* was this a spurious intr? */ + jne 1f + ret /* if so, just return */ +1: +#endif + subl $24,%esp /* Two local variables + 4 parameters */ + movl %eax,S_IRQ /* save irq number */ + + call spl7 /* set ipl */ + movl %eax,S_IPL /* save previous ipl */ + + movl S_IRQ,%ecx /* restore irq number */ + +#if NCPUS > 1 + cmpl $CALL_PMAP_UPDATE,%ecx /* was this a SMP pmap_update request? */ + je _call_single + + cmpl $CALL_AST_CHECK,%ecx /* was this a SMP remote -> local ast request? */ + je _call_local_ast +#endif + +#ifndef APIC + movl $1,%eax + shll %cl,%eax /* get corresponding IRQ mask */ + orl EXT(curr_pic_mask),%eax /* add current mask */ + + cmpl $8,%ecx /* do we need to ack slave? */ + jl 1f /* no, only master */ + + /* EOI on slave */ + movb %ah,%al + outb %al,$(PIC_SLAVE_OCW) /* mask slave out */ + + movb $(SPECIFIC_EOI),%al /* specific EOI for this irq */ + andb $7,%cl /* irq number for the slave */ + orb %cl,%al /* combine them */ + outb %al,$(PIC_SLAVE_ICW) /* ack interrupt to slave */ + + movb $(SPECIFIC_EOI + I_AM_SLAVE_2),%al /* specific master EOI for cascaded slave */ + outb %al,$(PIC_MASTER_ICW) /* ack interrupt to master */ + + movl EXT(curr_pic_mask),%eax /* restore original mask */ + movb %ah,%al + outb %al,$(PIC_SLAVE_OCW) /* unmask slave */ + jmp 2f + +1: + /* EOI on master */ + outb %al,$(PIC_MASTER_OCW) /* mask master out */ + + movb $(SPECIFIC_EOI),%al /* specific EOI for this irq */ + orb %cl,%al /* combine with irq number */ + outb %al,$(PIC_MASTER_ICW) /* ack interrupt to master */ + + movl EXT(curr_pic_mask),%eax /* restore original mask */ + outb %al,$(PIC_MASTER_OCW) /* unmask master */ +2: +#else + movl %ecx,(%esp) /* load irq number as 1st arg */ + call EXT(ioapic_irq_eoi) /* ioapic irq specific EOI */ +#endif + + movl S_IPL,%eax + movl %eax,4(%esp) /* previous ipl as 2nd arg */ + + movl S_RET,%eax + movl %eax,8(%esp) /* return address as 3rd arg */ + + movl S_REGS,%eax + movl %eax,12(%esp) /* address of interrupted registers as 4th arg */ + + movl S_IRQ,%eax /* copy irq number */ + + shll $2,%eax /* irq * 4 */ + movl EXT(iunit)(%eax),%edx /* get device unit number */ + movl %edx,(%esp) /* unit number as 1st arg */ + + call *EXT(ivect)(%eax) /* call interrupt handler */ + +_completed: + movl S_IPL,%eax /* restore previous ipl */ + movl %eax,(%esp) + call splx_cli /* restore previous ipl */ + + addl $24,%esp /* pop local variables */ + ret + +#if NCPUS > 1 +_call_single: + call EXT(lapic_eoi) /* lapic EOI before the handler to allow extra update */ + call EXT(pmap_update_interrupt) + jmp _completed + +_call_local_ast: + call EXT(lapic_eoi) /* lapic EOI */ + call EXT(ast_check) /* AST check on this cpu */ + jmp _completed + +#endif +END(interrupt) diff --git a/i386/i386at/ioapic.c b/i386/i386at/ioapic.c new file mode 100644 index 0000000..2553a2c --- /dev/null +++ b/i386/i386at/ioapic.c @@ -0,0 +1,463 @@ +/* + * Copyright (C) 2019 Free Software Foundation, Inc. + * + * This file is part of GNU Mach. + * + * GNU Mach is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Mach is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* only for macros */ +#include +#include +#include +#include +#include + +static int has_irq_specific_eoi = 0; +int timer_pin; + +uint32_t lapic_timer_val = 0; +uint32_t calibrated_ticks = 0; + +spl_t curr_ipl[NCPUS] = {0}; +int spl_init = 0; + +def_simple_lock_irq_data(static, ioapic_lock) /* Lock for non-atomic window accesses to ioapic */ + +int iunit[NINTR] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + /* 2nd IOAPIC */ + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63 }; + +interrupt_handler_fn ivect[NINTR] = { + /* 00 */ (interrupt_handler_fn)hardclock, + /* 01 */ kdintr, /* kdintr, ... */ + /* 02 */ intnull, + /* 03 */ intnull, /* lnpoll, comintr, ... */ + + /* 04 */ intnull, /* comintr, ... */ + /* 05 */ intnull, /* comintr, wtintr, ... */ + /* 06 */ intnull, /* fdintr, ... */ + /* 07 */ intnull, /* qdintr, ... */ + + /* 08 */ intnull, + /* 09 */ intnull, /* ether */ + /* 10 */ intnull, + /* 11 */ intnull, + + /* 12 */ intnull, + /* 13 */ fpintr, /* always */ + /* 14 */ intnull, /* hdintr, ... */ + /* 15 */ intnull, /* ??? */ + + /* 16 */ intnull, /* PIRQA */ + /* 17 */ intnull, /* PIRQB */ + /* 18 */ intnull, /* PIRQC */ + /* 19 */ intnull, /* PIRQD */ + /* 20 */ intnull, /* PIRQE */ + /* 21 */ intnull, /* PIRQF */ + /* 22 */ intnull, /* PIRQG */ + /* 23 */ intnull, /* PIRQH */ + + /* 24 */ intnull, + /* 25 */ intnull, + /* 26 */ intnull, + /* 27 */ intnull, + /* 28 */ intnull, + /* 29 */ intnull, + /* 30 */ intnull, + /* 31 */ intnull, + + /* 32 */ intnull, + /* 33 */ intnull, + /* 34 */ intnull, + /* 35 */ intnull, + /* 36 */ intnull, + /* 37 */ intnull, + /* 38 */ intnull, + /* 39 */ intnull, + /* 40 */ intnull, + /* 41 */ intnull, + /* 42 */ intnull, + /* 43 */ intnull, + /* 44 */ intnull, + /* 45 */ intnull, + /* 46 */ intnull, + /* 47 */ intnull, + /* 48 */ intnull, + /* 49 */ intnull, + /* 50 */ intnull, + /* 51 */ intnull, + /* 52 */ intnull, + /* 53 */ intnull, + /* 54 */ intnull, + /* 55 */ intnull, + + /* 56 */ intnull, + /* 57 */ intnull, + /* 58 */ intnull, + /* 59 */ intnull, + /* 60 */ intnull, + /* 61 */ intnull, + /* 62 */ intnull, + /* 63 */ intnull, +}; + +void +picdisable(void) +{ + int i; + + asm("cli"); + for (i = 0; i < NCPUS; i++) + curr_ipl[i] = SPLHI; + + /* + ** Disable PIC + */ + outb ( PIC_SLAVE_OCW, PICS_MASK ); + outb ( PIC_MASTER_OCW, PICM_MASK ); +} + +void +intnull(int unit_dev) +{ + printf("intnull(%d)\n", unit_dev); +} + +static uint32_t +ioapic_read(uint8_t id, uint8_t reg) +{ + volatile ApicIoUnit *ioapic = apic_get_ioapic(id)->ioapic; + ioapic->select.r = reg; + return ioapic->window.r; +} + +static void +ioapic_write(uint8_t id, uint8_t reg, uint32_t value) +{ + volatile ApicIoUnit *ioapic = apic_get_ioapic(id)->ioapic; + ioapic->select.r = reg; + ioapic->window.r = value; +} + +static void +ioapic_read_entry(int apic, int pin, struct ioapic_route_entry *e) +{ + union ioapic_route_entry_union entry; + + entry.lo = ioapic_read(apic, APIC_IO_REDIR_LOW(pin)); + entry.hi = ioapic_read(apic, APIC_IO_REDIR_HIGH(pin)); + + *e = entry.both; +} + +/* Write the high word first because mask bit is in low word */ +static void +ioapic_write_entry(int apic, int pin, struct ioapic_route_entry e) +{ + union ioapic_route_entry_union entry = {{0, 0}}; + + entry.both = e; + ioapic_write(apic, APIC_IO_REDIR_HIGH(pin), entry.hi); + ioapic_write(apic, APIC_IO_REDIR_LOW(pin), entry.lo); +} + +/* When toggling the interrupt via mask, write low word only */ +static void +ioapic_toggle_entry(int apic, int pin, int mask) +{ + union ioapic_route_entry_union entry; + + spl_t s = simple_lock_irq(&ioapic_lock); + ioapic_read_entry(apic, pin, &entry.both); + entry.both.mask = mask & 0x1; + ioapic_write(apic, APIC_IO_REDIR_LOW(pin), entry.lo); + simple_unlock_irq(s, &ioapic_lock); +} + +static int +ioapic_version(int apic) +{ + return (ioapic_read(apic, APIC_IO_VERSION) >> APIC_IO_VERSION_SHIFT) & 0xff; +} + +static int +ioapic_gsis(int apic) +{ + return ((ioapic_read(apic, APIC_IO_VERSION) >> APIC_IO_ENTRIES_SHIFT) & 0xff) + 1; +} + +static void timer_expiry_callback(void *arg) +{ + volatile int *done = arg; + *done = 1; +} + +static uint32_t +timer_measure_10x_apic_hz(void) +{ + volatile int done = 0; + uint32_t start = 0xffffffff; + timer_elt_data_t tmp_timer; + tmp_timer.fcn = timer_expiry_callback; + tmp_timer.param = (void *)&done; + + printf("timer calibration..."); + + /* Set APIC timer */ + lapic->init_count.r = start; + + /* Delay for 10 ticks (10 * 1/hz seconds) */ + set_timeout(&tmp_timer, 10); + do { + cpu_pause(); + } while (!done); + + /* Stop APIC timer */ + lapic->lvt_timer.r |= LAPIC_DISABLE; + + printf(" done\n"); + + return start - lapic->cur_count.r; +} + +void +calibrate_lapic_timer(void) +{ + spl_t s; + + /* Set one-shot timer */ + lapic->divider_config.r = LAPIC_TIMER_DIVIDE_2; + lapic->lvt_timer.r = IOAPIC_INT_BASE; + + /* Measure number of APIC timer ticks in 10 mach ticks + * divide by 10 because we want to know how many in 1 tick */ + if (!calibrated_ticks) { + s = splhigh(); + spl0(); + calibrated_ticks = timer_measure_10x_apic_hz() / 10; + splx(s); + } +} + +void +lapic_enable_timer(void) +{ + /* Set up counter */ + lapic->init_count.r = calibrated_ticks; + lapic->divider_config.r = LAPIC_TIMER_DIVIDE_2; + + /* Set the timer to interrupt periodically on remapped timer GSI */ + lapic->lvt_timer.r = IOAPIC_INT_BASE | LAPIC_TIMER_PERIODIC; + + /* Some buggy hardware requires this set again */ + lapic->divider_config.r = LAPIC_TIMER_DIVIDE_2; + + /* Enable interrupts for the first time */ + printf("LAPIC timer configured on cpu%d\n", cpu_number()); +} + +void +ioapic_toggle(int pin, int mask) +{ + int apic = 0; + ioapic_toggle_entry(apic, pin, mask); +} + +void +ioapic_irq_eoi(int pin) +{ + int apic = 0; + union ioapic_route_entry_union oldentry, entry; + + if (pin == 0) + goto skip_specific_eoi; + + spl_t s = simple_lock_irq(&ioapic_lock); + + if (!has_irq_specific_eoi) { + /* Workaround for old IOAPICs with no specific EOI */ + + /* Mask the pin and change to edge triggered */ + ioapic_read_entry(apic, pin, &entry.both); + oldentry = entry; + entry.both.mask = IOAPIC_MASK_DISABLED; + entry.both.trigger = IOAPIC_EDGE_TRIGGERED; + ioapic_write_entry(apic, pin, entry.both); + + /* Restore level entry */ + ioapic_write_entry(apic, pin, oldentry.both); + } else { + volatile ApicIoUnit *ioapic = apic_get_ioapic(apic)->ioapic; + + ioapic_read_entry(apic, pin, &entry.both); + ioapic->eoi.r = entry.both.vector; + } + + simple_unlock_irq(s, &ioapic_lock); + +skip_specific_eoi: + lapic_eoi (); +} + +static unsigned int +override_irq(IrqOverrideData *override, union ioapic_route_entry_union *entry) +{ + if (override->flags & APIC_IRQ_OVERRIDE_TRIGGER_MASK) { + entry->both.trigger = (override->flags & APIC_IRQ_OVERRIDE_LEVEL_TRIGGERED) ? + IOAPIC_LEVEL_TRIGGERED : IOAPIC_EDGE_TRIGGERED; + } else { + if (override->bus == 0) { + /* ISA is edge-triggered by default */ + entry->both.trigger = IOAPIC_EDGE_TRIGGERED; + } else { + entry->both.trigger = IOAPIC_LEVEL_TRIGGERED; + } + } + + if (override->flags & APIC_IRQ_OVERRIDE_POLARITY_MASK) { + entry->both.polarity = (override->flags & APIC_IRQ_OVERRIDE_ACTIVE_LOW) ? + IOAPIC_ACTIVE_LOW : IOAPIC_ACTIVE_HIGH; + } else { + if (override->bus == 0) { + /* EISA is active-low for level-triggered interrupts */ + if (entry->both.trigger == IOAPIC_LEVEL_TRIGGERED) { + entry->both.polarity = IOAPIC_ACTIVE_LOW; + } else { + entry->both.polarity = IOAPIC_ACTIVE_HIGH; + } + } + } + printf("IRQ override: pin=%d gsi=%d trigger=%s polarity=%s\n", + override->irq, override->gsi, + entry->both.trigger == IOAPIC_LEVEL_TRIGGERED ? "LEVEL" : "EDGE", + entry->both.polarity == IOAPIC_ACTIVE_LOW ? "LOW" : "HIGH"); + + return override->gsi; +} + +void +ioapic_configure(void) +{ + /* Assume first IO APIC maps to GSI base 0 */ + int gsi, apic = 0, bsp = 0, pin; + IrqOverrideData *irq_over; + int timer_gsi; + int version = ioapic_version(apic); + int ngsis = ioapic_gsis(apic); + int ngsis2 = 0; + + if (version >= 0x20) { + has_irq_specific_eoi = 1; + } + + printf("IOAPIC version 0x%x\n", version); + + /* Disable IOAPIC interrupts and set spurious interrupt */ + lapic->spurious_vector.r = IOAPIC_SPURIOUS_BASE; + + union ioapic_route_entry_union entry = {{0, 0}}; + + entry.both.delvmode = IOAPIC_FIXED; + entry.both.destmode = IOAPIC_PHYSICAL; + entry.both.mask = IOAPIC_MASK_DISABLED; + entry.both.dest = apic_get_cpu_apic_id(bsp); + + for (pin = 0; pin < 16; pin++) { + gsi = pin; + + /* ISA legacy IRQs */ + entry.both.trigger = IOAPIC_EDGE_TRIGGERED; + entry.both.polarity = IOAPIC_ACTIVE_HIGH; + + if ((irq_over = acpi_get_irq_override(pin))) { + gsi = override_irq(irq_over, &entry); + } + entry.both.vector = IOAPIC_INT_BASE + gsi; + ioapic_write_entry(apic, pin, entry.both); + + /* Timer workaround for x86 */ + if (pin == 0) { + /* Save timer info */ + timer_gsi = gsi; + } else { + /* Remap timer irq */ + if (gsi == timer_gsi) { + timer_pin = pin; + /* Remap GSI base to timer pin so ivect[0] is the timer */ + entry.both.vector = IOAPIC_INT_BASE; + ioapic_write_entry(apic, timer_pin, entry.both); + /* Mask the duplicate pin 0 as we will be using timer_pin */ + mask_irq(0); + } + } + } + + for (pin = 16; pin < ngsis; pin++) { + gsi = pin; + + /* PCI IRQs PIRQ A-H */ + entry.both.trigger = IOAPIC_LEVEL_TRIGGERED; + entry.both.polarity = IOAPIC_ACTIVE_LOW; + + if ((irq_over = acpi_get_irq_override(pin))) { + gsi = override_irq(irq_over, &entry); + } + entry.both.vector = IOAPIC_INT_BASE + gsi; + ioapic_write_entry(apic, pin, entry.both); + } + + printf("IOAPIC 0 configured with GSI 0-%d\n", ngsis - 1); + + /* Second IOAPIC */ + if (apic_get_num_ioapics() > 1) { + apic = 1; + ngsis2 = ioapic_gsis(apic); + + for (pin = 0; pin < ngsis2; pin++) { + gsi = pin + ngsis; + + /* Defaults */ + entry.both.trigger = IOAPIC_LEVEL_TRIGGERED; + entry.both.polarity = IOAPIC_ACTIVE_LOW; + + if ((irq_over = acpi_get_irq_override(pin + ngsis))) { + gsi = override_irq(irq_over, &entry); + } + entry.both.vector = IOAPIC_INT_BASE + gsi; + ioapic_write_entry(apic, pin, entry.both); + } + + printf("IOAPIC 1 configured with GSI %d-%d\n", ngsis, ngsis + ngsis2 - 1); + } + + /* Start the IO APIC receiving interrupts */ + lapic_setup(); + lapic_enable(); +} diff --git a/i386/i386at/kd.c b/i386/i386at/kd.c new file mode 100644 index 0000000..2bea3c8 --- /dev/null +++ b/i386/i386at/kd.c @@ -0,0 +1,3059 @@ +/* + * 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. + */ +/* + * Olivetti Mach Console driver v0.0 + * Copyright Ing. C. Olivetti & C. S.p.A. 1988, 1989 + * All rights reserved. + * + */ +/* + Copyright 1988, 1989 by Olivetti Advanced Technology Center, Inc., +Cupertino, 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 Olivetti +not be used in advertising or publicity pertaining to distribution +of the software without specific, written prior permission. + + OLIVETTI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, +IN NO EVENT SHALL OLIVETTI 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 OUR OF OR IN CONNECTION +WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/* + Copyright 1988, 1989 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. +*/ + +/* $ Header: $ */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG 1 /* export feep() */ + +#if 0 +#define BROKEN_KEYBOARD_RESET +#endif + +struct tty kd_tty; +extern boolean_t rebootflag; + +static void charput(csrpos_t pos, char ch, char chattr); +static void charmvup(csrpos_t from, csrpos_t to, int count); +static void charmvdown(csrpos_t from, csrpos_t to, int count); +static void charclear(csrpos_t to, int count, char chattr); +static void charsetcursor(csrpos_t newpos); +static void kd_noopreset(void); + +/* + * These routines define the interface to the device-specific layer. + * See kdsoft.h for a more complete description of what each routine does. + */ +void (*kd_dput)(csrpos_t, char, char) = charput; /* put attributed char */ +void (*kd_dmvup)(csrpos_t, csrpos_t, int) = charmvup; /* block move up */ +void (*kd_dmvdown)(csrpos_t, csrpos_t, int) = charmvdown; /* block move down */ +void (*kd_dclear)(csrpos_t, int, char) = charclear; /* block clear */ +void (*kd_dsetcursor)(csrpos_t) = charsetcursor; + /* set cursor position on displayed page */ +void (*kd_dreset)(void) = kd_noopreset; /* prepare for reboot */ + +/* + * Globals used for both character-based controllers and bitmap-based + * controllers. Default is EGA. + */ + +vm_offset_t kd_bitmap_start = (vm_offset_t)0xa0000; /* XXX - put in kd.h */ +u_char *vid_start = (u_char *)EGA_START; + /* VM start of video RAM or frame buffer */ +csrpos_t kd_curpos = 0; /* set indirectly by kd_setpos--see kdsoft.h */ +short kd_lines = 25; +short kd_cols = 80; +char kd_attr = KA_NORMAL; /* current attribute */ +char kd_color = KA_NORMAL; +char kd_attrflags = 0; /* Not reverse, underline, blink */ + +/* + * kd_state shows the state of the modifier keys (ctrl, caps lock, + * etc.) It should normally be changed by calling set_kd_state(), so + * that the keyboard status LEDs are updated correctly. + */ +int kd_state = KS_NORMAL; +int kb_mode = KB_ASCII; /* event/ascii */ + +/* + * State for the keyboard "mouse". + */ +int kd_kbd_mouse = 0; +int kd_kbd_magic_scale = 6; +int kd_kbd_magic_button = 0; + +/* + * Some keyboard commands work by sending a command, waiting for an + * ack (handled by kdintr), then sending data, which generates a + * second ack. If we are in the middle of such a sequence, kd_ack + * shows what the ack is for. + * + * When a byte is sent to the keyboard, it is kept around in last_sent + * in case it needs to be resent. + * + * The rest of the variables here hold the data required to complete + * the sequence. + * + * XXX - the System V driver keeps a command queue, I guess in case we + * want to start a command while another is in progress. Is this + * something we should worry about? + */ +enum why_ack {NOT_WAITING, SET_LEDS, DATA_ACK}; +enum why_ack kd_ack = NOT_WAITING; + +u_char last_sent = 0; + +u_char kd_nextled = 0; + +/* + * We don't provide any mutex protection for this flag because we know + * that this module will have been initialized by the time multiple + * threads are running. + */ +boolean_t kd_initialized = FALSE; /* driver initialized? */ +boolean_t kd_extended = FALSE; + +/* Array for processing escape sequences. */ +#define K_MAXESC 32 +u_char esc_seq[K_MAXESC]; +u_char *esc_spt = (u_char *)0; + +/* + * This array maps scancodes to Ascii characters (or character + * sequences). + * Each row corresponds to one key. There are NUMOUTPUT bytes per key + * state. The states are ordered: Normal, SHIFT, CTRL, ALT, + * SHIFT/ALT. + */ + +/* This new keymap from Tudor Hulubei (tudor@cs.unh.edu) makes the + following changes to the keyboard driver: + + - Alt + key (m-key) returns `ESC key' instead of `ESC N key'. + - Backspace returns 0x7f instead of 0x08. + - Delete returns `ESC [ 9' instead of 0x7f. + - Alt + function keys return key sequences that are different + from the key sequences returned by the function keys alone. + This is done with the idea of allowing a terminal server to + implement multiple virtual consoles mapped on Alt+F1, Alt+F2, + etc, as in Linux. + + -- Derek Upham 1997/06/25 */ + +unsigned char key_map[NUMKEYS][WIDTH_KMAP] = { +{NC,NC,NC, NC,NC,NC, NC,NC,NC, NC,NC,NC, NC,NC,NC}, +{K_ESC,NC,NC, K_ESC,NC,NC, K_ESC,NC,NC, 0x1b,K_ESC,NC, K_ESC,NC,NC}, +{K_ONE,NC,NC, K_BANG,NC,NC, K_ONE,NC,NC, 0x1b,K_ONE,NC, 0x1b,0x4e,K_BANG}, +{K_TWO,NC,NC, K_ATSN,NC,NC, K_NUL,NC,NC, 0x1b,K_TWO,NC, 0x1b,0x4e,K_ATSN}, +{K_THREE,NC,NC, K_POUND,NC,NC, K_THREE,NC,NC, 0x1b,K_THREE,NC, 0x1b,0x4e,K_POUND}, +{K_FOUR,NC,NC, K_DOLLAR,NC,NC, K_FOUR,NC,NC, 0x1b,K_FOUR,NC, 0x1b,0x4e,K_DOLLAR}, +{K_FIVE,NC,NC, K_PERC,NC,NC, K_FIVE,NC,NC, 0x1b,K_FIVE,NC, 0x1b,0x4e,K_PERC}, +{K_SIX,NC,NC, K_CARET,NC,NC, K_RS,NC,NC, 0x1b,K_SIX,NC, 0x1b,0x4e,K_CARET}, +{K_SEVEN,NC,NC, K_AMPER,NC,NC, K_SEVEN,NC,NC, 0x1b,K_SEVEN,NC, 0x1b,0x4e,K_AMPER}, +{K_EIGHT,NC,NC, K_ASTER,NC,NC, K_EIGHT,NC,NC, 0x1b,K_EIGHT,NC, 0x1b,0x4e,K_ASTER}, +{K_NINE,NC,NC, K_LPAREN,NC,NC, K_NINE,NC,NC, 0x1b,K_NINE,NC, 0x1b,0x4e,K_LPAREN}, +{K_ZERO,NC,NC, K_RPAREN,NC,NC, K_ZERO,NC,NC, 0x1b,K_ZERO,NC, 0x1b,0x4e,K_RPAREN}, +{K_MINUS,NC,NC, K_UNDSC,NC,NC, K_US,NC,NC, 0x1b,K_MINUS,NC, 0x1b,0x4e,K_UNDSC}, +{K_EQL,NC,NC, K_PLUS,NC,NC, K_EQL,NC,NC, 0x1b,K_EQL,NC, 0x1b,0x4e,K_PLUS}, +{K_DEL,NC,NC, K_DEL,NC,NC, K_DEL,NC,NC, 0x1b,K_DEL,NC, K_DEL,NC,NC}, +{K_HT,NC,NC, K_GS,NC,NC, K_HT,NC,NC, 0x1b,K_HT,NC, K_GS,NC,NC}, +{K_q,NC,NC, K_Q,NC,NC, K_DC1,NC,NC, 0x1b,K_q,NC, 0x1b,0x4e,K_Q}, +{K_w,NC,NC, K_W,NC,NC, K_ETB,NC,NC, 0x1b,K_w,NC, 0x1b,0x4e,K_W}, +{K_e,NC,NC, K_E,NC,NC, K_ENQ,NC,NC, 0x1b,K_e,NC, 0x1b,0x4e,K_E}, +{K_r,NC,NC, K_R,NC,NC, K_DC2,NC,NC, 0x1b,K_r,NC, 0x1b,0x4e,K_R}, +{K_t,NC,NC, K_T,NC,NC, K_DC4,NC,NC, 0x1b,K_t,NC, 0x1b,0x4e,K_T}, +{K_y,NC,NC, K_Y,NC,NC, K_EM,NC,NC, 0x1b,K_y,NC, 0x1b,0x4e,K_Y}, +{K_u,NC,NC, K_U,NC,NC, K_NAK,NC,NC, 0x1b,K_u,NC, 0x1b,0x4e,K_U}, +{K_i,NC,NC, K_I,NC,NC, K_HT,NC,NC, 0x1b,K_i,NC, 0x1b,0x4e,K_I}, +{K_o,NC,NC, K_O,NC,NC, K_SI,NC,NC, 0x1b,K_o,NC, 0x1b,0x4e,K_O}, +{K_p,NC,NC, K_P,NC,NC, K_DLE,NC,NC, 0x1b,K_p,NC, 0x1b,0x4e,K_P}, +{K_LBRKT,NC,NC, K_LBRACE,NC,NC, K_ESC,NC,NC, 0x1b,K_LBRKT,NC, 0x1b,0x4e,K_LBRACE}, +{K_RBRKT,NC,NC, K_RBRACE,NC,NC, K_GS,NC,NC, 0x1b,K_RBRKT,NC, 0x1b,0x4e,K_RBRACE}, +{K_CR,NC,NC, K_CR,NC,NC, K_CR,NC,NC, 0x1b,K_CR,NC, K_CR,NC,NC}, +{K_SCAN,K_CTLSC,NC, K_SCAN,K_CTLSC,NC, K_SCAN,K_CTLSC,NC, K_SCAN,K_CTLSC,NC, K_SCAN,K_CTLSC,NC}, +{K_a,NC,NC, K_A,NC,NC, K_SOH,NC,NC, 0x1b,K_a,NC, 0x1b,0x4e,K_A}, +{K_s,NC,NC, K_S,NC,NC, K_DC3,NC,NC, 0x1b,K_s,NC, 0x1b,0x4e,K_S}, +{K_d,NC,NC, K_D,NC,NC, K_EOT,NC,NC, 0x1b,K_d,NC, 0x1b,0x4e,K_D}, +{K_f,NC,NC, K_F,NC,NC, K_ACK,NC,NC, 0x1b,K_f,NC, 0x1b,0x4e,K_F}, +{K_g,NC,NC, K_G,NC,NC, K_BEL,NC,NC, 0x1b,K_g,NC, 0x1b,0x4e,K_G}, +{K_h,NC,NC, K_H,NC,NC, K_BS,NC,NC, 0x1b,K_h,NC, 0x1b,0x4e,K_H}, +{K_j,NC,NC, K_J,NC,NC, K_LF,NC,NC, 0x1b,K_j,NC, 0x1b,0x4e,K_J}, +{K_k,NC,NC, K_K,NC,NC, K_VT,NC,NC, 0x1b,K_k,NC, 0x1b,0x4e,K_K}, +{K_l,NC,NC, K_L,NC,NC, K_FF,NC,NC, 0x1b,K_l,NC, 0x1b,0x4e,K_L}, +{K_SEMI,NC,NC, K_COLON,NC,NC, K_SEMI,NC,NC, 0x1b,K_SEMI,NC, 0x1b,0x4e,K_COLON}, +{K_SQUOTE,NC,NC,K_DQUOTE,NC,NC, K_SQUOTE,NC,NC,0x1b,K_SQUOTE,NC, 0x1b,0x4e,K_DQUOTE}, +{K_GRAV,NC,NC, K_TILDE,NC,NC, K_RS,NC,NC, 0x1b,K_GRAV,NC, 0x1b,0x4e,K_TILDE}, +{K_SCAN,K_LSHSC,NC, K_SCAN,K_LSHSC,NC, K_SCAN,K_LSHSC,NC, K_SCAN,K_LSHSC,NC, K_SCAN,K_LSHSC,NC}, +{K_BSLSH,NC,NC, K_PIPE,NC,NC, K_FS,NC,NC, 0x1b,K_BSLSH,NC, 0x1b,0x4e,K_PIPE}, +{K_z,NC,NC, K_Z,NC,NC, K_SUB,NC,NC, 0x1b,K_z,NC, 0x1b,0x4e,K_Z}, +{K_x,NC,NC, K_X,NC,NC, K_CAN,NC,NC, 0x1b,K_x,NC, 0x1b,0x4e,K_X}, +{K_c,NC,NC, K_C,NC,NC, K_ETX,NC,NC, 0x1b,K_c,NC, 0x1b,0x4e,K_C}, +{K_v,NC,NC, K_V,NC,NC, K_SYN,NC,NC, 0x1b,K_v,NC, 0x1b,0x4e,K_V}, +{K_b,NC,NC, K_B,NC,NC, K_STX,NC,NC, 0x1b,K_b,NC, 0x1b,0x4e,K_B}, +{K_n,NC,NC, K_N,NC,NC, K_SO,NC,NC, 0x1b,K_n,NC, 0x1b,0x4e,K_N}, +{K_m,NC,NC, K_M,NC,NC, K_CR,NC,NC, 0x1b,K_m,NC, 0x1b,0x4e,K_M}, +{K_COMMA,NC,NC, K_LTHN,NC,NC, K_COMMA,NC,NC, 0x1b,K_COMMA,NC, 0x1b,0x4e,K_LTHN}, +{K_PERIOD,NC,NC,K_GTHN,NC,NC, K_PERIOD,NC,NC,0x1b,K_PERIOD,NC, 0x1b,0x4e,K_GTHN}, +{K_SLASH,NC,NC, K_QUES,NC,NC, K_SLASH,NC,NC, 0x1b,K_SLASH,NC, 0x1b,0x4e,K_QUES}, +{K_SCAN,K_RSHSC,NC, K_SCAN,K_RSHSC,NC, K_SCAN,K_RSHSC,NC, K_SCAN,K_RSHSC,NC, K_SCAN,K_RSHSC,NC}, +{K_ASTER,NC,NC, K_ASTER,NC,NC, K_ASTER,NC,NC, 0x1b,K_ASTER,NC, 0x1b,0x4e,K_ASTER}, +{K_SCAN,K_ALTSC,NC, K_SCAN,K_ALTSC,NC, K_SCAN,K_ALTSC,NC, K_SCAN,K_ALTSC,NC, K_SCAN,K_ALTSC,NC}, +{K_SPACE,NC,NC, K_SPACE,NC,NC, K_NUL,NC,NC, 0x1b,K_SPACE,NC, K_SPACE,NC,NC}, +{K_SCAN,K_CLCKSC,NC, K_SCAN,K_CLCKSC,NC, K_SCAN,K_CLCKSC,NC, K_SCAN,K_CLCKSC,NC, K_SCAN,K_CLCKSC,NC}, +{K_F1, K_F1S, K_F1, K_F1A, K_F1S}, +{K_F2, K_F2S, K_F2, K_F2A, K_F2S}, +{K_F3, K_F3S, K_F3, K_F3A, K_F3S}, +{K_F4, K_F4S, K_F4, K_F4A, K_F4S}, +{K_F5, K_F5S, K_F5, K_F5A, K_F5S}, +{K_F6, K_F6S, K_F6, K_F6A, K_F6S}, +{K_F7, K_F7S, K_F7, K_F7A, K_F7S}, +{K_F8, K_F8S, K_F8, K_F8A, K_F8S}, +{K_F9, K_F9S, K_F9, K_F9A, K_F9S}, +{K_F10, K_F10S, K_F10, K_F10A, K_F10S}, +{K_SCAN,K_NLCKSC,NC, K_SCAN,K_NLCKSC,NC, K_SCAN,K_NLCKSC,NC, K_SCAN,K_NLCKSC,NC, K_SCAN,K_NLCKSC,NC}, +{K_SCRL, K_NUL,NC,NC, K_SCRL, K_SCRL, K_NUL,NC,NC}, +{K_HOME, K_SEVEN,NC,NC, K_HOME, K_HOME, 0x1b,0x4e,K_SEVEN}, +{K_UA, K_EIGHT,NC,NC, K_UA, K_UA, 0x1b,0x4e,K_EIGHT}, +{K_PUP, K_NINE,NC,NC, K_PUP, K_PUP, 0x1b,0x4e,K_NINE}, +{0x1b,0x5b,0x53, K_MINUS,NC,NC, 0x1b,0x5b,0x53, 0x1b,0x5b,0x53, 0x1b,0x4e,0x2d}, +{K_LA, K_FOUR,NC,NC, K_LA, K_LA, 0x1b,0x4e,K_FOUR}, +{0x1b,0x5b,0x47, K_FIVE,NC,NC, 0x1b,0x5b,0x47, 0x1b,0x5b,0x47, 0x1b,0x4e,0x35}, +{K_RA, K_SIX,NC,NC, K_RA, K_RA, 0x1b,0x4e,K_SIX}, +{0x1b,0x5b,0x54, K_PLUS,NC,NC, 0x1b,0x5b,0x54, 0x1b,0x5b,0x54, 0x1b,0x4e,0x2b}, +{K_END, K_ONE,NC,NC, K_END, K_END, 0x1b,0x4e,K_ONE}, +{K_DA, K_TWO,NC,NC, K_DA, K_DA, 0x1b,0x4e,K_TWO}, +{K_PDN, K_THREE,NC,NC, K_PDN, K_PDN, 0x1b,0x4e,K_THREE}, +{K_INS, K_ZERO,NC,NC, K_INS, K_INS, 0x1b,0x4e,K_ZERO}, +{0x1b,0x5b,0x39, K_PERIOD,NC,NC, K_DEL,NC,NC, K_DEL,NC,NC, 0x1b,0x4e,K_PERIOD}, +{NC,NC,NC, NC,NC,NC, NC,NC,NC, NC,NC,NC, NC,NC,NC}, +{NC,NC,NC, NC,NC,NC, NC,NC,NC, NC,NC,NC, NC,NC,NC}, +{NC,NC,NC, NC,NC,NC, NC,NC,NC, NC,NC,NC, NC,NC,NC}, +{K_F11, K_F11S, K_F11, K_F11A, K_F11S}, +{K_F12, K_F12S, K_F12, K_F12A, K_F12S} +}; + + +/* + * Globals used only for character-based controllers. + */ + +short kd_index_reg = EGA_IDX_REG; +short kd_io_reg = EGA_IO_REG; + + +/* + * Globals used only for bitmap-based controllers. See kdsoft.h for + * an explanation of what some of these variables are used for. + */ + +u_char *font_start = 0; /* starting addr of font */ + +short fb_width = 0; /* bits in frame buffer scan line */ +short fb_height = 0; /* scan lines in frame buffer*/ +short char_width = 0; /* bit width of 1 char */ +short char_height = 0; /* bit height of 1 char */ +short chars_in_font = 0; +short cursor_height = 0; /* bit height of cursor */ + +/* These initial values are simply guesses. */ +u_char char_black = 0; +u_char char_white = 0xff; + +short xstart = 0; +short ystart = 0; + +short char_byte_width = 0; /* char_width/NBBY */ +short fb_byte_width = 0; /* fb_width/NBBY */ +short font_byte_width = 0; /* num bytes in 1 scan line of font */ + +/* + * Switch for poll vs. interrupt. + */ +int kd_pollc = 0; + +#ifdef DEBUG +static void +pause(void) +{ + int i; + + for (i = 0; i < 50000; ++i) + ; +} + +/* + * feep: + * + * Ring the bell for a short time. + * Warning: uses outb(). You may prefer to use kd_debug_put. + */ +void +feep(void) +{ + kd_bellon(); + pause(); + kd_belloff(NULL); +} + +/* + * Put a debugging character on the screen. + * LOC=0 means put it in the bottom right corner, LOC=1 means put it + * one column to the left, etc. + */ +void +kd_debug_put( + int loc, + char c) +{ + csrpos_t pos = ONE_PAGE - (loc+1) * ONE_SPACE; + + (*kd_dput)(pos, c, KA_NORMAL); +} +#endif /* DEBUG */ + + +extern boolean_t mouse_in_use; +int old_kb_mode; + +void +cnpollc(boolean_t on) +{ + if (mouse_in_use) { + if (on) { + /* switch into X */ + old_kb_mode = kb_mode; + kb_mode = KB_ASCII; + X_kdb_enter(); + + kd_pollc++; + } else { + --kd_pollc; + + /* switch out of X */ + X_kdb_exit(); + kb_mode = old_kb_mode; + } + } else { + if (on) { + kd_pollc++; + } else { + --kd_pollc; + } + } +} + + + +/* + * kdopen: + * + * This opens the console driver and sets up the tty and other + * rudimentary stuff including calling the line discipline for + * setting up the device independent stuff for a tty driver. + * + * input: device number 'dev', and flag + * + * output: device is opened and setup + * + */ +int +kdopen( + dev_t dev, + int flag, + io_req_t ior) +{ + struct tty *tp; + spl_t o_pri; + + tp = &kd_tty; + o_pri = simple_lock_irq(&tp->t_lock); + if (!(tp->t_state & (TS_ISOPEN|TS_WOPEN))) { + /* XXX ttychars allocates memory */ + simple_unlock_nocheck(&tp->t_lock.slock); + ttychars(tp); + simple_lock_nocheck(&tp->t_lock.slock); + /* + * Special support for boot-time rc scripts, which don't + * stty the console. + */ + tp->t_oproc = kdstart; + tp->t_stop = kdstop; + tp->t_ospeed = tp->t_ispeed = B115200; + tp->t_flags = ODDP|EVENP|ECHO|CRMOD|XTABS|LITOUT; + kdinit(); + } + tp->t_state |= TS_CARR_ON; + simple_unlock_irq(o_pri, &tp->t_lock); + return (char_open(dev, tp, flag, ior)); +} + + +/* + * kdclose: + * + * This function merely executes the device independent code for + * closing the line discipline. + * + * input: device number 'dev', and flag + * + * output: device is closed + * + */ +/*ARGSUSED*/ +void +kdclose(dev_t dev, int flag) +{ + struct tty *tp; + + tp = &kd_tty; + { + spl_t s; + s = simple_lock_irq(&tp->t_lock); + ttyclose(tp); + simple_unlock_irq(s, &tp->t_lock); + } + + return; +} + + +/* + * kdread: + * + * This function executes the device independent code to read from + * the tty. + * + * input: device number 'dev' + * + * output: characters are read from tty clists + * + */ +/*ARGSUSED*/ +int +kdread(dev_t dev, io_req_t uio) +{ + struct tty *tp; + + tp = &kd_tty; + tp->t_state |= TS_CARR_ON; + return((*linesw[kd_tty.t_line].l_read)(tp, uio)); +} + + +/* + * kdwrite: + * + * This function does the device independent write action for this + * console (tty) driver. + * + * input: device number 'dev' + * + * output: characters are written to tty clists + * + */ +/*ARGSUSED*/ +int +kdwrite(dev_t dev, io_req_t uio) +{ + return((*linesw[kd_tty.t_line].l_write)(&kd_tty, uio)); +} + +/* + * Mmap. + */ + +/*ARGSUSED*/ +vm_offset_t +kdmmap(dev_t dev, vm_offset_t off, vm_prot_t prot) +{ + if (off >= (128*1024)) + return(-1); + + /* Get page frame number for the page to be mapped. */ + return(i386_btop(kd_bitmap_start+off)); +} + +int +kdportdeath( + dev_t dev, + mach_port_t port) +{ + return (tty_portdeath(&kd_tty, (ipc_port_t)port)); +} + +/*ARGSUSED*/ +io_return_t kdgetstat( + dev_t dev, + dev_flavor_t flavor, + dev_status_t data, /* pointer to OUT array */ + mach_msg_type_number_t *count) /* OUT */ +{ + io_return_t result; + + switch (flavor) { + case KDGSTATE: + if (*count < 1) + return (D_INVALID_OPERATION); + *data = kd_state; + *count = 1; + result = D_SUCCESS; + break; + + case KDGKBENT: + result = kdgetkbent((struct kbentry *)data); + *count = sizeof(struct kbentry)/sizeof(int); + break; + + default: + result = tty_get_status(&kd_tty, flavor, data, count); + break; + } + return (result); +} + +/*ARGSUSED*/ +io_return_t kdsetstat( + dev_t dev, + dev_flavor_t flavor, + dev_status_t data, + mach_msg_type_number_t count) +{ + io_return_t result; + + switch (flavor) { + case KDSKBENT: + if (count < sizeof(struct kbentry)/sizeof(int)) { + return (D_INVALID_OPERATION); + } + result = kdsetkbent((struct kbentry *)data, 0); + break; + + case KDSETBELL: + if (count < 1) + return (D_INVALID_OPERATION); + result = kdsetbell(*data, 0); + break; + + default: + result = tty_set_status(&kd_tty, flavor, data, count); + } + return (result); +} + + + +/* + * kdsetbell: + * + * Turn the bell on or off. Returns error code, if given bogus + * on/off value. + */ +int +kdsetbell( + int val, /* on or off */ + int flags) /* flags set for console */ +{ + int err = 0; + + if (val == KD_BELLON) + kd_bellon(); + else if (val == KD_BELLOFF) + kd_belloff(NULL); + else + err = D_INVALID_OPERATION; + + return(err); +} + +/* + * kdgetkbent: + * + * Get entry from key mapping table. Returns error code, if any. + */ +int +kdgetkbent(struct kbentry *kbent) +{ + u_char *cp; + spl_t o_pri = SPLKD(); /* probably superfluous */ + + cp = &key_map[kbent->kb_index][CHARIDX(kbent->kb_state)]; + kbent->kb_value[0] = *cp++; + kbent->kb_value[1] = *cp++; + kbent->kb_value[2] = *cp; + (void)splx(o_pri); + return(0); +} + + +/* + * kdsetkbent: + * + * Set entry in key mapping table. Return error code, if any. + */ +int +kdsetkbent( + struct kbentry *kbent, + int flags) /* flags set for console */ +{ + u_char *cp; + spl_t o_pri; + + o_pri = SPLKD(); + cp = &key_map[kbent->kb_index][CHARIDX(kbent->kb_state)]; + *cp++ = kbent->kb_value[0]; + *cp++ = kbent->kb_value[1]; + *cp = kbent->kb_value[2]; + (void)splx(o_pri); + return(0); +} + +/* + * kdintr: + * + * This function is the interrupt code for the driver. Since this is + * a special tty (console), interrupts are only for input, so we read in + * the character. If in ascii mode, we then do the mapping translation + * from the keyboard switch table and place the characters on the tty's + * input switch table. If in event mode, we create and queue a kd_event. + * + * input: interrupt vector 'vec' + * + * output: character or sequence is placed on appropriate queue + * + */ +/*ARGSUSED*/ +void +kdintr(int vec) +{ + struct tty *tp; + unsigned char c; + unsigned char scancode; + unsigned int char_idx; + boolean_t up = FALSE; /* key-up event */ + + if (kd_pollc) + return; /* kdb polling kbd */ + + if (!kd_initialized) + return; + + tp = &kd_tty; +#ifdef old + while ((inb(K_STATUS) & K_OBUF_FUL) == 0) + ; /* this should never loop */ +#else /* old */ + { + /* + * Allow for keyboards that raise interrupt before + * the character gets to the buffer. But don't wait + * forever if grabbing the character by polling leaves + * the interrupt on but buffer empty. + */ + /* + * Micronics VLB motherboard with 486DX2 can report keyboard + * interrupt before K_STATUS register indicates that the + * output buffer is full. Moreover, the bus won't settle w + * while we poll K_STATUS at speed. Temporary fix is to break + * out after safety runs out and pick up keyboard event. This + * should be fixed eventually by putting a 1us timout between + * inb's to K_STATUS and fix the pic initialization order to + * avoid bootup keyboard wedging (ie make kd a real device) + */ + int safety = 1000; + while ((inb(K_STATUS) & K_OBUF_FUL) == 0) + if (!safety--) break; /* XXX */ + } +#endif /* old */ + /* + * We may have seen a mouse event. + */ + if ((inb(K_STATUS) & 0x20) == 0x20) { + if (mouse_in_use) { + mouse_handle_byte((u_char)inb(K_RDWR)); + return; + } else { + printf("M%xI", inb(K_RDWR)); + return; + } + } + + scancode = inb(K_RDWR); + if (scancode == K_EXTEND && kb_mode != KB_EVENT) { + kd_extended = TRUE; + goto done; + } else if (scancode == K_RESEND) { + kd_resend(); + goto done; + } else if (scancode == K_ACKSC) { + kd_handle_ack(); + goto done; + } else if (kd_kbd_mouse && kd_kbd_magic(scancode)) { + goto done; + } else if (kdcheckmagic(scancode)) { + goto done; + } else if (kb_mode == KB_EVENT) { + kd_enqsc(scancode); + goto done; + } /* else... */ + + if (scancode & K_UP) { + up = TRUE; + scancode &= ~K_UP; + } + if (scancode < NUMKEYS) { + /* Lookup in map, then process. */ + char_idx = kdstate2idx(kd_state, kd_extended); + c = key_map[scancode][char_idx]; + if (c == K_SCAN) { + c = key_map[scancode][++char_idx]; + set_kd_state(do_modifier(kd_state, c, up)); + } else if (!up) { + /* regular key-down */ + unsigned int max; /* max index for char sequence */ + + max = char_idx + NUMOUTPUT; + char_idx++; + if (!kd_extended) { + if (kd_state&KS_CLKED) { + if (kd_isupper(c)) { + c += ('a' - 'A'); + max = char_idx; + } + else if (kd_islower(c)) { + c -= ('a' - 'A'); + max = char_idx; + } + } + /* + * Notice that even if the keypad is remapped, + * NumLock only effects the keys that are + * physically part of the keypad. Is this + * The Right Thing? + */ + if ((kd_state&KS_NLKED) && + (((K_HOMESC) <= scancode) && + (scancode <= (K_DELSC)))) { + char_idx = CHARIDX(SHIFT_STATE); + c = key_map[scancode][char_idx]; + max = char_idx + NUMOUTPUT; + char_idx++; + } + } + + /* + * here's where we actually put the char (or + * char sequence, for function keys) onto the + * input queue. + */ + for ( ; (c != K_DONE) && (char_idx <= max); + c = key_map[scancode][char_idx++]) { + (*linesw[tp->t_line].l_rint)(c, tp); + } + kd_extended = FALSE; + } + } + + done: + return; +} + +/* + * kd_handle_ack: + * + * For pending commands, complete the command. For data bytes, + * drop the ack on the floor. + */ +void +kd_handle_ack(void) +{ + switch (kd_ack) { + case SET_LEDS: + kd_setleds2(); + kd_ack = DATA_ACK; + break; + case DATA_ACK: + kd_ack = NOT_WAITING; + break; + case NOT_WAITING: + printf("unexpected ACK from keyboard\n"); + break; + default: + panic("bogus kd_ack\n"); + break; + } +} + +/* + * kd_resend: + * + * Resend a missed keyboard command or data byte. + */ +void +kd_resend(void) +{ + if (kd_ack == NOT_WAITING) + printf("unexpected RESEND from keyboard\n"); + else + kd_senddata(last_sent); +} + + +/* + * do_modifier: + * + * Change keyboard state according to which modifier key and + * whether it went down or up. + * + * input: the current state, the key, and the key's direction. + * The key can be any key, not just a modifier key. + * + * output: the new state + */ +int +do_modifier( + int state, + Scancode c, + boolean_t up) +{ + switch (c) { + case (K_ALTSC): + if (up) + state &= ~KS_ALTED; + else + state |= KS_ALTED; + kd_extended = FALSE; + break; +#ifndef ORC + case (K_CLCKSC): +#endif /* ORC */ + case (K_CTLSC): + if (up) + state &= ~KS_CTLED; + else + state |= KS_CTLED; + kd_extended = FALSE; + break; +#ifdef ORC + case (K_CLCKSC): + if (!up) + state ^= KS_CLKED; + break; +#endif /* ORC */ + case (K_NLCKSC): + if (!up) + state ^= KS_NLKED; + break; + case (K_LSHSC): + case (K_RSHSC): + if (up) + state &= ~KS_SHIFTED; + else + state |= KS_SHIFTED; + kd_extended = FALSE; + break; + } + + return(state); +} + + +/* + * kdcheckmagic: + * + * Check for magic keystrokes for invoking the debugger or + * rebooting or ... + * + * input: an unprocessed scancode + * + * output: TRUE if a magic key combination was recognized and + * processed. FALSE otherwise. + * + * side effects: + * various actions possible, depending on which keys are + * pressed. If the debugger is called, steps are taken + * to ensure that the system doesn't think the magic keys + * are still held down. + */ +boolean_t +kdcheckmagic(Scancode scancode) +{ + static int magic_state = KS_NORMAL; /* like kd_state */ + boolean_t up = FALSE; + + if (scancode == 0x46) /* scroll lock */ +/* if (scancode == 0x52) ** insert key */ + { + kd_kbd_mouse = !kd_kbd_mouse; + kd_kbd_magic_button = 0; + return(TRUE); + } + if (scancode & K_UP) { + up = TRUE; + scancode &= ~K_UP; + } + magic_state = do_modifier(magic_state, scancode, up); + + if ((magic_state&(KS_CTLED|KS_ALTED)) == (KS_CTLED|KS_ALTED)) { + switch (scancode) { +#if MACH_KDB + case K_dSC: /* ctl-alt-d */ + kdb_kintr(); /* invoke debugger */ + /* Returned from debugger, so reset kbd state. */ + (void)SPLKD(); + magic_state = KS_NORMAL; + if (kb_mode == KB_ASCII) + kd_state = KS_NORMAL; + /* setting leds kills kbd */ + else { + kd_enqsc(K_ALTSC | K_UP); + kd_enqsc(K_CTLSC | K_UP); + kd_enqsc(K_dSC | K_UP); + } + return(TRUE); + break; +#endif /* MACH_KDB */ + case K_DELSC: /* ctl-alt-del */ + /* if rebootflag is on, reboot the system */ + if (rebootflag) + kdreboot(); + break; + } + } + return(FALSE); +} + + +/* + * kdstate2idx: + * + * Return the value for the 2nd index into key_map that + * corresponds to the given state. + */ +unsigned int +kdstate2idx(unsigned int state, /* bit vector, not a state index */ + boolean_t extended) +{ + int state_idx = NORM_STATE; + + if ((!extended) && state != KS_NORMAL) { + if ((state&(KS_SHIFTED|KS_ALTED)) == (KS_SHIFTED|KS_ALTED)) + state_idx = SHIFT_ALT; + /* CTRL should have higher priority than SHIFT. That + way, CTRL-SHIFT-2 and CTRL-2 produce the same keycode. + --Derek Upham 1997/06/25 */ + else if (state&KS_CTLED) + state_idx = CTRL_STATE; + else if (state&KS_SHIFTED) + state_idx = SHIFT_STATE; + else if (state&KS_ALTED) + state_idx = ALT_STATE; + } + + return (CHARIDX(state_idx)); +} + +/* + * kdstart: + * + * This function does the general processing of characters and other + * operations for the device driver. The device independent portion of + * the tty driver calls this routine (it's setup in kdinit) with a + * given command. That command is then processed, and control is passed + * back to the kernel. + * + * input: tty pointer 'tp', and command to execute 'cmd' + * + * output: command is executed + * + * Entered and left at spltty. Drops priority to spl0 to display character. + * ASSUMES that it is never called from interrupt-driven code. + */ +void +kdstart(struct tty *tp) +{ + spl_t o_pri; + int ch; + + if (tp->t_state & TS_TTSTOP) + return; + for ( ; ; ) { + tp->t_state &= ~TS_BUSY; + if (tp->t_state & TS_TTSTOP) + break; + if ((tp->t_outq.c_cc <= 0) || (ch = getc(&tp->t_outq)) == -1) + break; + /* + * Drop priority for long screen updates. ttstart() calls us at + * spltty. + */ + o_pri = splsoftclock(); /* block timeout */ + kd_putc_esc(ch); + splx(o_pri); + } + if (tp->t_outq.c_cc <= TTLOWAT(tp)) { + tt_write_wakeup(tp); + } +} + +/*ARGSUSED*/ +void +kdstop( + struct tty *tp, + int flags) +{ + /* + * do nothing - all characters are output by one call to + * kdstart. + */ +} + +/* + * kdinit: + * + * This code initializes the structures and sets up the port registers + * for the console driver. + * + * Each bitmap-based graphics card is likely to require a unique + * way to determine the card's presence. The driver runs through + * each "special" card that it knows about and uses the first one + * that it finds. If it doesn't find any, it assumes that an + * EGA-like card is installed. + * + * input : None. Interrupts are assumed to be disabled + * output : Driver is initialized + * + */ +void +kdinit(void) +{ + unsigned char k_comm; /* keyboard command byte */ + + if (kd_initialized) + return; + + esc_spt = esc_seq; + kd_attr = KA_NORMAL; + + kd_attrflags = 0; + kd_color = KA_NORMAL; + /* + * board specific initialization: set up globals and kd_dxxx + * pointers, and synch displayed cursor with logical cursor. + */ + kd_xga_init(); + + /* get rid of any garbage in output buffer */ + if (inb(K_STATUS) & K_OBUF_FUL) + (void)inb(K_RDWR); + + kd_sendcmd(KC_CMD_READ); /* ask for the ctlr command byte */ + k_comm = kd_getdata(); + k_comm &= ~K_CB_DISBLE; /* clear keyboard disable bit */ + k_comm |= K_CB_ENBLIRQ; /* enable interrupt */ + kd_sendcmd(KC_CMD_WRITE); /* write new ctlr command byte */ + kd_senddata(k_comm); + unmask_irq(KBD_IRQ); + kd_initialized = TRUE; + +#if ENABLE_IMMEDIATE_CONSOLE + /* Now that we're set up, we no longer need or want the + immediate console. */ + { + extern boolean_t immediate_console_enable; + immediate_console_enable = FALSE; + } + + /* The immediate console printed stuff at the bottom of the + screen rather than at the cursor position, so that's where + we should start. */ + kd_setpos(ONE_PAGE - ONE_LINE); printf("\n"); +#endif /* ENABLE_IMMEDIATE_CONSOLE */ + + cnsetleds(kd_state = KS_NORMAL); + /* clear the LEDs AFTER we + enable the keyboard controller. + This keeps NUM-LOCK from being + set on the NEC Versa. */ + + /* Allocate the input buffer. */ + ttychars(&kd_tty); +} + +/* + * kd_belloff: + * + * This routine shuts the bell off, by sending the appropriate code + * to the speaker port. + * + * input : None + * output : bell is turned off + * + */ +static boolean_t kd_bellstate = FALSE; + +void +kd_belloff(void * param) +{ + unsigned char status; + + status = (inb(K_PORTB) & ~(K_SPKRDATA | K_ENABLETMR2)); + outb(K_PORTB, status); + kd_bellstate = FALSE; + return; +} + + +/* + * kd_bellon: + * + * This routine turns the bell on. + * + * input : None + * output : bell is turned on + * + */ +void +kd_bellon(void) +{ + unsigned char status; + + /* program timer 2 */ + outb(K_TMRCTL, K_SELTMR2 | K_RDLDTWORD | K_TSQRWAVE | K_TBINARY); + outb(K_TMR2, 1500 & 0xff); /* LSB */ + outb(K_TMR2, (int)1500 >> 8); /* MSB */ + + /* start speaker - why must we turn on K_SPKRDATA? */ + status = (inb(K_PORTB)| K_ENABLETMR2 | K_SPKRDATA); + outb(K_PORTB, status); + return; +} + +/* + * + * Function kd_putc_esc(): + * + * This function puts a character on the screen, handling escape + * sequences. + * + * input : character to be displayed (or part of an escape code) + * output : character is displayed, or some action is taken + * + */ +void +kd_putc_esc(u_char c) +{ + if (c == (K_ESC)) { + if (esc_spt == esc_seq) { + *(esc_spt++)=(K_ESC); + *(esc_spt) = '\0'; + } else { + kd_putc((K_ESC)); + esc_spt = esc_seq; + } + } else { + if (esc_spt - esc_seq) { + if (esc_spt - esc_seq > K_MAXESC - 1) + esc_spt = esc_seq; + else { + *(esc_spt++) = c; + *(esc_spt) = '\0'; + kd_parseesc(); + } + } else { + kd_putc(c); + } + } +} + +/* + * + * Function kd_putc(): + * + * This function simply puts a character on the screen. It does some + * special processing for linefeed, carriage return, backspace and + * the bell. + * + * input : character to be displayed + * output : character is displayed, or some action is taken + * + */ +int sit_for_0 = 1; + +void +kd_putc(u_char ch) +{ + if ((!ch) && sit_for_0) + return; + + switch (ch) { + case ((K_LF)): + kd_down(); + break; + case ((K_CR)): + kd_cr(); + break; + case ((K_BS)): + kd_left(); + break; + case ((K_HT)): + kd_tab(); + break; + case ((K_BEL)): + /* + * Similar problem to K_BS here (behavior might depend + * on tty setting). Also check LF and CR. + */ + if (!kd_bellstate) + { + kd_bellon(); + timeout(kd_belloff, 0, hz/8 ); + kd_bellstate = TRUE; + } + break; + default: + (*kd_dput)(kd_curpos, ch, kd_attr); + kd_right(); + break; + } + return; +} + + +/* + * kd_setpos: + * + * This function sets the software and hardware cursor position + * on the screen, using device-specific code to actually move and + * display the cursor. + * + * input : position on (or off) screen to move the cursor to + * output : cursor position is updated, screen has been scrolled + * if necessary to bring cursor position back onto + * screen. + * + */ +void +kd_setpos(csrpos_t newpos) +{ + if (newpos > ONE_PAGE) { + kd_scrollup(); + newpos = BOTTOM_LINE; + } + if (newpos < 0) { + kd_scrolldn(); + newpos = 0; + } + + (*kd_dsetcursor)(newpos); +} + + +/* + * kd_scrollup: + * + * This function scrolls the screen up one line using a DMA memory + * copy. + * + * input : None + * output : lines on screen appear to be shifted up one line + * + */ +void +kd_scrollup(void) +{ + csrpos_t to; + csrpos_t from; + int count; + + /* scroll up */ + to = 0; + from = ONE_LINE; + count = (ONE_PAGE - ONE_LINE)/ONE_SPACE; + (*kd_dmvup)(from, to, count); + + /* clear bottom line */ + to = BOTTOM_LINE; + count = ONE_LINE/ONE_SPACE; + (*kd_dclear)(to, count, kd_attr); + return; +} + + +/* + * kd_scrolldn: + * + * Scrolls the characters on the screen down one line. + * + * input : None + * output : Lines on screen appear to be moved down one line + * + */ +void +kd_scrolldn(void) +{ + csrpos_t to; + csrpos_t from; + int count; + + /* move down */ + to = ONE_PAGE - ONE_SPACE; + from = ONE_PAGE - ONE_LINE - ONE_SPACE; + count = (ONE_PAGE - ONE_LINE) / ONE_SPACE; + (*kd_dmvdown)(from, to, count); + + /* clear top line */ + to = 0; + count = ONE_LINE/ONE_SPACE; + (*kd_dclear)(to, count, kd_attr); + return; + +} + + +/* + * kd_parseesc: + * + * This routine begins the parsing of an escape sequence. It uses the + * escape sequence array and the escape spot pointer to handle + * asynchronous parsing of escape sequences. + * + * input : String of characters prepended by an escape + * output : Appropriate actions are taken depending on the string as + * defined by the ansi terminal specification + * + */ +void +kd_parseesc(void) +{ + u_char *escp; + + escp = esc_seq + 1; /* point to char following ESC */ + switch(*(escp)) { + case 'c': + kd_cls(); + kd_home(); + esc_spt = esc_seq; /* reset spot in ESC sequence */ + break; + case '[': + escp++; + kd_parserest(escp); + break; + case '\0': + break; /* not enough info yet */ + default: + kd_putc(*escp); + esc_spt = esc_seq; /* inv sequence char, reset */ + break; + } + return; + +} + + +/* kd_update_kd_attr: + * + * Updates kd_attr according to kd_attrflags and kd_color. + * This code has its origin from console.c and selection.h in + * linux 2.2 drivers/char/. + * Modified for GNU Mach by Marcus Brinkmann. + */ + +#define reverse_video_char(a) (((a) & 0x88) | ((((a) >> 4) | ((a) << 4)) & 0x77)) +static void +kd_update_kd_attr(void) +{ + kd_attr = kd_color; + if (kd_attrflags & KAX_UNDERLINE) + kd_attr = (kd_attr & 0xf0) | KAX_COL_UNDERLINE; + else if (kd_attrflags & KAX_DIM) + kd_attr = (kd_attr & 0xf0) | KAX_COL_DIM; + if (kd_attrflags & KAX_REVERSE) + kd_attr = reverse_video_char(kd_attr); + if (kd_attrflags & KAX_BLINK) + kd_attr ^= 0x80; + if (kd_attrflags & KAX_BOLD) + kd_attr ^= 0x08; +} + +/* color_table added by Julio Merino to take proper color order. + * I get this code from Linux 2.2 source code in file: + * linux/drivers/char/console.c + */ +unsigned char color_table[] = { 0, 4, 2, 6, 1, 5, 3, 7, + 8,12,10,14, 9,13,11,15 }; +/* + * kd_parserest: + * + * This function will complete the parsing of an escape sequence and + * call the appropriate support routine if it matches a character. This + * function could be greatly improved by using a function jump table, and + * removing this bulky switch statement. + * + * input : An string + * output : Appropriate action based on whether the string matches a + * sequence acceptable to the ansi terminal specification + * + */ +void +kd_parserest(u_char *cp) +{ + int number[16], npar = 0, i; + csrpos_t newpos; + + for(i=0;i<=15;i++) + number[i] = MACH_ATOI_DEFAULT; + + do { + cp += mach_atoi(cp, &number[npar]); + } while (*cp == ';' && ++npar <= 15 && cp++); + + switch(*cp) { + case 'm': + for (i=0;i<=npar;i++) + switch(number[i]) { + case MACH_ATOI_DEFAULT: + case 0: + kd_attrflags = 0; + kd_color = KA_NORMAL; + break; + case 1: + kd_attrflags |= KAX_BOLD; + kd_attrflags &= ~KAX_DIM; + break; + case 2: + kd_attrflags |= KAX_DIM; + kd_attrflags &= ~KAX_BOLD; + break; + case 4: + kd_attrflags |= KAX_UNDERLINE; + break; + case 5: + kd_attrflags |= KAX_BLINK; + break; + case 7: + kd_attrflags |= KAX_REVERSE; + break; + case 8: + kd_attrflags |= KAX_INVISIBLE; + break; + case 21: + case 22: + kd_attrflags &= ~(KAX_BOLD | KAX_DIM); + break; + case 24: + kd_attrflags &= ~KAX_UNDERLINE; + break; + case 25: + kd_attrflags &= ~KAX_BLINK; + break; + case 27: + kd_attrflags &= ~KAX_REVERSE; + break; + case 38: + kd_attrflags |= KAX_UNDERLINE; + kd_color = (kd_color & 0xf0) | (KA_NORMAL & 0x0f); + break; + case 39: + kd_attrflags &= ~KAX_UNDERLINE; + kd_color = (kd_color & 0xf0) | (KA_NORMAL & 0x0f); + break; + default: + if (number[i] >= 30 && number[i] <= 37) { + /* foreground color */ + kd_color = (kd_color & 0xf0) | color_table[(number[i] - 30)]; + } else if (number[i] >= 40 && number[i] <= 47) { + /* background color */ + kd_color = (kd_color & 0x0f) | (color_table[(number[i] - 40)] << 4); + } + break; + } + kd_update_kd_attr(); + esc_spt = esc_seq; + break; + case '@': + if (number[0] == MACH_ATOI_DEFAULT) + kd_insch(1); + else + kd_insch(number[0]); + esc_spt = esc_seq; + break; + case 'A': + if (number[0] == MACH_ATOI_DEFAULT) + kd_up(); + else + while (number[0]--) + kd_up(); + esc_spt = esc_seq; + break; + case 'B': + if (number[0] == MACH_ATOI_DEFAULT) + kd_down(); + else + while (number[0]--) + kd_down(); + esc_spt = esc_seq; + break; + case 'C': + if (number[0] == MACH_ATOI_DEFAULT) + kd_right(); + else + while (number[0]--) + kd_right(); + esc_spt = esc_seq; + break; + case 'D': + if (number[0] == MACH_ATOI_DEFAULT) + kd_left(); + else + while (number[0]--) + kd_left(); + esc_spt = esc_seq; + break; + case 'E': + kd_cr(); + if (number[0] == MACH_ATOI_DEFAULT) + kd_down(); + else + while (number[0]--) + kd_down(); + esc_spt = esc_seq; + break; + case 'F': + kd_cr(); + if (number[0] == MACH_ATOI_DEFAULT) + kd_up(); + else + while (number[0]--) + kd_up(); + esc_spt = esc_seq; + break; + case 'G': + if (number[0] == MACH_ATOI_DEFAULT) + number[0] = 0; + else + if (number[0] > 0) + --number[0]; /* because number[0] is from 1 */ + kd_setpos(BEG_OF_LINE(kd_curpos) + number[0] * ONE_SPACE); + esc_spt = esc_seq; + break; + case 'f': + case 'H': + if (number[0] == MACH_ATOI_DEFAULT && number[1] == MACH_ATOI_DEFAULT) + { + kd_home(); + esc_spt = esc_seq; + break; + } + if (number[0] == MACH_ATOI_DEFAULT) + number[0] = 0; + else if (number[0] > 0) + --number[0]; /* numbered from 1 */ + newpos = (number[0] * ONE_LINE); /* setup row */ + if (number[1] == MACH_ATOI_DEFAULT) + number[1] = 0; + else if (number[1] > 0) + number[1]--; + newpos += (number[1] * ONE_SPACE); /* setup column */ + if (newpos < 0) + newpos = 0; /* upper left */ + if (newpos > ONE_PAGE) + newpos = (ONE_PAGE - ONE_SPACE); /* lower right */ + kd_setpos(newpos); + esc_spt = esc_seq; + break; /* done or not ready */ + case 'J': + switch(number[0]) { + case MACH_ATOI_DEFAULT: + case 0: + kd_cltobcur(); /* clears from current + pos to bottom. + */ + break; + case 1: + kd_cltopcur(); /* clears from top to + current pos. + */ + break; + case 2: + kd_cls(); + break; + default: + break; + } + esc_spt = esc_seq; /* reset it */ + break; + case 'K': + switch(number[0]) { + case MACH_ATOI_DEFAULT: + case 0: + kd_cltoecur(); /* clears from current + pos to eoln. + */ + break; + case 1: + kd_clfrbcur(); /* clears from begin + of line to current + pos. + */ + break; + case 2: + kd_eraseln(); /* clear entire line */ + break; + default: + break; + } + esc_spt = esc_seq; + break; + case 'L': + if (number[0] == MACH_ATOI_DEFAULT) + kd_insln(1); + else + kd_insln(number[0]); + esc_spt = esc_seq; + break; + case 'M': + if (number[0] == MACH_ATOI_DEFAULT) + kd_delln(1); + else + kd_delln(number[0]); + esc_spt = esc_seq; + break; + case 'P': + if (number[0] == MACH_ATOI_DEFAULT) + kd_delch(1); + else + kd_delch(number[0]); + esc_spt = esc_seq; + break; + case 'S': + if (number[0] == MACH_ATOI_DEFAULT) + kd_scrollup(); + else + while (number[0]--) + kd_scrollup(); + esc_spt = esc_seq; + break; + case 'T': + if (number[0] == MACH_ATOI_DEFAULT) + kd_scrolldn(); + else + while (number[0]--) + kd_scrolldn(); + esc_spt = esc_seq; + break; + case 'X': + if (number[0] == MACH_ATOI_DEFAULT) + kd_erase(1); + else + kd_erase(number[0]); + esc_spt = esc_seq; + break; + case '\0': + break; /* not enough yet */ + default: + kd_putc(*cp); /* show inv character */ + esc_spt = esc_seq; /* inv entry, reset */ + break; + } + return; +} + +void +kd_tab(void) +{ + int i; + + for (i = 8 - (CURRENT_COLUMN(kd_curpos) % 8); i > 0; i--) { + kd_putc(' '); + } + +} + + +/* + * kd_cls: + * + * This function clears the screen with spaces and the current attribute. + * + * input : None + * output : Screen is cleared + * + */ +void +kd_cls(void) +{ + (*kd_dclear)(0, ONE_PAGE/ONE_SPACE, kd_attr); + return; +} + + +/* + * kd_home: + * + * This function will move the cursor to the home position on the screen, + * as well as set the internal cursor position (kd_curpos) to home. + * + * input : None + * output : Cursor position is moved + * + */ +void +kd_home(void) +{ + kd_setpos(0); + return; +} + + +/* + * kd_up: + * + * This function moves the cursor up one line position. + * + * input : None + * output : Cursor moves up one line, or screen is scrolled + * + */ +void +kd_up(void) +{ + if (kd_curpos < ONE_LINE) + kd_scrolldn(); + else + kd_setpos(kd_curpos - ONE_LINE); + return; +} + + +/* + * kd_down: + * + * This function moves the cursor down one line position. + * + * input : None + * output : Cursor moves down one line or the screen is scrolled + * + */ +void +kd_down(void) +{ + if (kd_curpos >= (ONE_PAGE - ONE_LINE)) + kd_scrollup(); + else + kd_setpos(kd_curpos + ONE_LINE); + return; +} + + +/* + * kd_right: + * + * This function moves the cursor one position to the right. + * + * input : None + * output : Cursor moves one position to the right + * + */ +void +kd_right(void) +{ + if (kd_curpos < (ONE_PAGE - ONE_SPACE)) + kd_setpos(kd_curpos + ONE_SPACE); + else { + kd_scrollup(); + kd_setpos(BEG_OF_LINE(kd_curpos)); + } + return; +} + + +/* + * kd_left: + * + * This function moves the cursor one position to the left. + * + * input : None + * output : Cursor moves one position to the left + * + */ +void +kd_left(void) +{ + if (0 < kd_curpos) + kd_setpos(kd_curpos - ONE_SPACE); + return; +} + + +/* + * kd_cr: + * + * This function moves the cursor to the beginning of the current + * line. + * + * input : None + * output : Cursor moves to the beginning of the current line + * + */ +void +kd_cr(void) +{ + kd_setpos(BEG_OF_LINE(kd_curpos)); + return; +} + + +/* + * kd_cltobcur: + * + * This function clears from the current cursor position to the bottom + * of the screen. + * + * input : None + * output : Screen is cleared from current cursor position to bottom + * + */ +void +kd_cltobcur(void) +{ + csrpos_t start; + int count; + + start = kd_curpos; + count = (ONE_PAGE - kd_curpos)/ONE_SPACE; + (*kd_dclear)(start, count, kd_attr); + return; +} + + +/* + * kd_cltopcur: + * + * This function clears from the current cursor position to the top + * of the screen. + * + * input : None + * output : Screen is cleared from current cursor position to top + * + */ +void +kd_cltopcur(void) +{ + int count; + + count = (kd_curpos + ONE_SPACE) / ONE_SPACE; + (*kd_dclear)(0, count, kd_attr); + return; +} + + +/* + * kd_cltoecur: + * + * This function clears from the current cursor position to eoln. + * + * input : None + * output : Line is cleared from current cursor position to eoln + * + */ +void +kd_cltoecur(void) +{ + csrpos_t i; + csrpos_t hold; + + hold = BEG_OF_LINE(kd_curpos) + ONE_LINE; + for (i = kd_curpos; i < hold; i += ONE_SPACE) { + (*kd_dput)(i, K_SPACE, kd_attr); + } +} + + +/* + * kd_clfrbcur: + * + * This function clears from the beginning of the line to the current + * cursor position. + * + * input : None + * output : Line is cleared from beginning to current position + * + */ +void +kd_clfrbcur(void) +{ + csrpos_t i; + + for (i = BEG_OF_LINE(kd_curpos); i <= kd_curpos; i += ONE_SPACE) { + (*kd_dput)(i, K_SPACE, kd_attr); + } +} + + +/* + * kd_delln: + * + * This function deletes 'number' lines on the screen by effectively + * scrolling the lines up and replacing the old lines with spaces. + * + * input : number of lines to delete + * output : lines appear to be deleted + * + */ +void +kd_delln(int number) +{ + csrpos_t to; + csrpos_t from; + int delbytes; /* num of bytes to delete */ + int count; /* num of words to move or fill */ + + if (number <= 0) + return; + + delbytes = number * ONE_LINE; + to = BEG_OF_LINE(kd_curpos); + if (to + delbytes >= ONE_PAGE) + delbytes = ONE_PAGE - to; + if (to + delbytes < ONE_PAGE) { + from = to + delbytes; + count = (ONE_PAGE - from) / ONE_SPACE; + (*kd_dmvup)(from, to, count); + } + + to = ONE_PAGE - delbytes; + count = delbytes / ONE_SPACE; + (*kd_dclear)(to, count, kd_attr); + return; +} + + +/* + * kd_insln: + * + * This function inserts a line above the current one by + * scrolling the current line and all the lines below it down. + * + * input : number of lines to insert + * output : New lines appear to be inserted + * + */ +void +kd_insln(int number) +{ + csrpos_t to; + csrpos_t from; + int count; + csrpos_t top; /* top of block to be moved */ + int insbytes; /* num of bytes inserted */ + + if (number <= 0) + return; + + top = BEG_OF_LINE(kd_curpos); + insbytes = number * ONE_LINE; + if (top + insbytes > ONE_PAGE) + insbytes = ONE_PAGE - top; + to = ONE_PAGE - ONE_SPACE; + from = to - insbytes; + if (from > top) { + count = (from - top + ONE_SPACE) / ONE_SPACE; + (*kd_dmvdown)(from, to, count); + } + + count = insbytes / ONE_SPACE; + (*kd_dclear)(top, count, kd_attr); + return; +} + + +/* + * kd_delch: + * + * This function deletes a number of characters from the current + * position in the line. + * + * input : number of characters to delete + * output : characters appear to be deleted + * + */ +void +kd_delch(int number) +{ + int count; /* num words moved/filled */ + int delbytes; /* bytes to delete */ + csrpos_t to; + csrpos_t from; + csrpos_t nextline; /* start of next line */ + + if (number <= 0) + return; + + nextline = BEG_OF_LINE(kd_curpos) + ONE_LINE; + delbytes = number * ONE_SPACE; + if (kd_curpos + delbytes > nextline) + delbytes = nextline - kd_curpos; + if (kd_curpos + delbytes < nextline) { + from = kd_curpos + delbytes; + to = kd_curpos; + count = (nextline - from) / ONE_SPACE; + (*kd_dmvup)(from, to, count); + } + + to = nextline - delbytes; + count = delbytes / ONE_SPACE; + (*kd_dclear)(to, count, kd_attr); + return; + +} + + +/* + * kd_erase: + * + * This function overwrites characters with a space starting with the + * current cursor position and ending in number spaces away. + * + * input : number of characters to erase + * output : characters appear to be blanked or erased + * + */ +void +kd_erase(int number) +{ + csrpos_t i; + csrpos_t stop; + + stop = kd_curpos + (ONE_SPACE * number); + if (stop > BEG_OF_LINE(kd_curpos) + ONE_LINE) + stop = BEG_OF_LINE(kd_curpos) + ONE_LINE; + for (i = kd_curpos; i < stop; i += ONE_SPACE) { + (*kd_dput)(i, K_SPACE, kd_attr); + } + return; +} + + +/* + * kd_eraseln: + * + * This function erases the current line with spaces. + * + * input : None + * output : Current line is erased + * + */ +void +kd_eraseln(void) +{ + csrpos_t i; + csrpos_t stop; + + stop = BEG_OF_LINE(kd_curpos) + ONE_LINE; + for (i = BEG_OF_LINE(kd_curpos); i < stop; i += ONE_SPACE) { + (*kd_dput)(i, K_SPACE, kd_attr); + } + return; +} + + +/* + * kd_insch: + * + * This function inserts a blank at the current cursor position + * and moves all other characters on the line over. + * + * input : number of blanks to insert + * output : Blanks are inserted at cursor position + * + */ +void +kd_insch(int number) +{ + csrpos_t to; + csrpos_t from; + int count; + csrpos_t nextline; /* start of next line */ + int insbytes; /* num of bytes inserted */ + + if (number <= 0) + return; + + nextline = BEG_OF_LINE(kd_curpos) + ONE_LINE; + insbytes = number * ONE_SPACE; + if (kd_curpos + insbytes > nextline) + insbytes = nextline - kd_curpos; + + to = nextline - ONE_SPACE; + from = to - insbytes; + if (from >= kd_curpos) { + count = (from - kd_curpos + ONE_SPACE) / ONE_SPACE; + (*kd_dmvdown)(from, to, count); + } + + count = insbytes / ONE_SPACE; + (*kd_dclear)(kd_curpos, count, kd_attr); + return; +} + + +/* + * kd_isupper, kd_islower: + * + * Didn't want to include ctype.h because it brings in stdio.h, and + * only want to see if the darn character is uppercase or lowercase. + * + * input : Character 'c' + * output : isuuper gives TRUE if character is uppercase, islower + * returns TRUE if character is lowercase + * + */ +boolean_t +kd_isupper(u_char c) +{ + if (('A' <= c) && (c <= 'Z')) + return(TRUE); + return(FALSE); +} + +boolean_t +kd_islower(u_char c) +{ + if (('a' <= c) && (c <= 'z')) + return(TRUE); + return(FALSE); +} + +/* + * kd_senddata: + * + * This function sends a byte to the keyboard RDWR port, but + * first waits until the input/output data buffer is clear before + * sending the data. Note that this byte can be either data or a + * keyboard command. + * + */ +void +kd_senddata(unsigned char ch) +{ + while (inb(K_STATUS) & K_IBUF_FUL) + ; + outb(K_RDWR, ch); + last_sent = ch; + return; +} + +/* + * kd_sendcmd: + * + * This function sends a command byte to the keyboard command + * port, but first waits until the input/output data buffer is + * clear before sending the data. + * + */ +void +kd_sendcmd(unsigned char ch) +{ + while (inb(K_STATUS) & K_IBUF_FUL) + ; + outb(K_CMD, ch); + return; +} + + +/* + * kd_getdata: + * + * This function returns a data byte from the keyboard RDWR port, + * after waiting until the port is flagged as having something to + * read. + */ +unsigned char +kd_getdata(void) +{ + while ((inb(K_STATUS) & K_OBUF_FUL) == 0) + ; + return(inb(K_RDWR)); +} + +void +kd_cmdreg_write(int val) +{ +int ch=KC_CMD_WRITE; + + while (inb(K_STATUS) & K_IBUF_FUL) + ; + outb(K_CMD, ch); + + while (inb(K_STATUS) & K_IBUF_FUL) + ; + outb(K_RDWR, val); +} + +void +kd_mouse_drain(void) +{ + int i; + while(inb(K_STATUS) & K_IBUF_FUL) + ; + while((i = inb(K_STATUS)) & K_OBUF_FUL) + printf("kbd: S = %x D = %x\n", i, inb(K_RDWR)); +} + +/* + * set_kd_state: + * + * Set kd_state and update the keyboard status LEDs. + */ +void +set_kd_state(int newstate) +{ + kd_state = newstate; + kd_setleds1(state2leds(newstate)); +} + +/* + * state2leds: + * + * Return a byte containing LED settings for the keyboard, given + * a state vector. + */ +u_char +state2leds(int state) +{ + u_char result = 0; + + if (state & KS_NLKED) + result |= K_LED_NUMLK; + if (state & KS_CLKED) + result |= K_LED_CAPSLK; + return(result); +} + +/* + * kd_setleds[12]: + * + * Set the keyboard LEDs according to the given byte. + */ +void +kd_setleds1(u_char val) +{ + if (kd_ack != NOT_WAITING) { +#ifdef MACH_KBD + printf("kd_setleds1: unexpected state (%d)\n", kd_ack); +#endif + return; + } + + kd_ack = SET_LEDS; + kd_nextled = val; + kd_senddata(K_CMD_LEDS); +} + +void +kd_setleds2(void) +{ + kd_senddata(kd_nextled); +} + + +/* + * cnsetleds: + * + * like kd_setleds[12], but not interrupt-based. + * Currently disabled because cngetc ignores caps lock and num + * lock anyway. + */ +void +cnsetleds(u_char val) +{ + kd_senddata(K_CMD_LEDS); + (void)kd_getdata(); /* XXX - assume is ACK */ + kd_senddata(val); + (void)kd_getdata(); /* XXX - assume is ACK */ +} + +void +kdreboot(void) +{ + (*kd_dreset)(); + +#ifndef BROKEN_KEYBOARD_RESET + kd_sendcmd(0xFE); /* XXX - magic # */ + delay(1000000); /* wait to see if anything happens */ +#endif + /* + * If that didn't work, then we'll just have to try and + * do it the hard way. + */ + cpu_shutdown(); +} + +static int which_button[] = {0, MOUSE_LEFT, MOUSE_MIDDLE, MOUSE_RIGHT}; +static struct mouse_motion moved; + +int +kd_kbd_magic(int scancode) +{ +int new_button = 0; + + if (kd_kbd_mouse == 2) + printf("sc = %x\n", scancode); + + switch (scancode) { +/* f1 f2 f3 */ + case 0x3d: + new_button++; + case 0x3c: + new_button++; + case 0x3b: + new_button++; + if (kd_kbd_magic_button && (new_button != kd_kbd_magic_button)) { + /* down w/o up */ + mouse_button(which_button[kd_kbd_magic_button], 1); + } + /* normal */ + if (kd_kbd_magic_button == new_button) { + mouse_button(which_button[new_button], 1); + kd_kbd_magic_button = 0; + } else { + mouse_button(which_button[new_button], 0); + kd_kbd_magic_button = new_button; + } + break; + +/* right left up down */ + case 0x4d: + moved.mm_deltaX = kd_kbd_magic_scale; + moved.mm_deltaY = 0; + mouse_moved(moved); + break; + case 0x4b: + moved.mm_deltaX = -kd_kbd_magic_scale; + moved.mm_deltaY = 0; + mouse_moved(moved); + break; + case 0x48: + moved.mm_deltaX = 0; + moved.mm_deltaY = kd_kbd_magic_scale; + mouse_moved(moved); + break; + case 0x50: + moved.mm_deltaX = 0; + moved.mm_deltaY = -kd_kbd_magic_scale; + mouse_moved(moved); + break; +/* home pageup end pagedown */ + case 0x47: + moved.mm_deltaX = -2*kd_kbd_magic_scale; + moved.mm_deltaY = 2*kd_kbd_magic_scale; + mouse_moved(moved); + break; + case 0x49: + moved.mm_deltaX = 2*kd_kbd_magic_scale; + moved.mm_deltaY = 2*kd_kbd_magic_scale; + mouse_moved(moved); + break; + case 0x4f: + moved.mm_deltaX = -2*kd_kbd_magic_scale; + moved.mm_deltaY = -2*kd_kbd_magic_scale; + mouse_moved(moved); + break; + case 0x51: + moved.mm_deltaX = 2*kd_kbd_magic_scale; + moved.mm_deltaY = -2*kd_kbd_magic_scale; + mouse_moved(moved); + break; + + default: + return 0; + } + return 1; +} + + + +/* + * Code specific to EGA/CGA/VGA boards. This code relies on the fact + * that the "slam" functions take a word count and ONE_SPACE takes up + * 1 word. + */ +#define SLAMBPW 2 /* bytes per word for "slam" fcns */ + +/* + * xga_getpos: + * + * This function returns the current hardware cursor position on the + * screen, scaled for compatibility with kd_curpos. + * + * input : None + * output : returns the value of cursor position on screen + * + */ +static csrpos_t +xga_getpos(void) + +{ + unsigned char low; + unsigned char high; + short pos; + + outb(kd_index_reg, C_HIGH); + high = inb(kd_io_reg); + outb(kd_index_reg, C_LOW); + low = inb(kd_io_reg); + pos = (0xff&low) + ((unsigned short)high<<8); + + return(ONE_SPACE * (csrpos_t)pos); +} + + +/* + * kd_xga_init: + * + * Initialization specific to character-based graphics adapters. + */ +void +kd_xga_init(void) +{ + unsigned char start, stop; + +#if 0 + unsigned char screen; + + /* XXX: this conflicts with read/writing the RTC */ + + outb(CMOS_ADDR, CMOS_EB); + screen = inb(CMOS_DATA) & CM_SCRMSK; + switch(screen) { + default: + printf("kd: unknown screen type, defaulting to EGA\n"); + /* FALLTHROUGH */ + case CM_EGA_VGA: +#endif + /* + * Here we'll want to query to bios on the card + * itself, because then we can figure out what + * type we have exactly. At this point we only + * know that the card is NOT CGA or MONO. For + * now, however, we assume backwards compatibility + * with 0xb8000 as the starting screen offset + * memory location for these cards. + * + */ + + vid_start = (u_char *)phystokv(EGA_START); + kd_index_reg = EGA_IDX_REG; + kd_io_reg = EGA_IO_REG; + kd_lines = 25; + kd_cols = 80; + kd_bitmap_start = 0xa0000; /* XXX - magic numbers */ + { /* XXX - is there a cleaner way to do this? */ + char *addr = (char *)phystokv(kd_bitmap_start); + int i; + for (i = 0; i < 200; i++) + addr[i] = 0x00; + } +#if 0 + break; + /* XXX: some buggy BIOSes report these... */ + case CM_CGA_40: + vid_start = (u_char *)phystokv(CGA_START); + kd_index_reg = CGA_IDX_REG; + kd_io_reg = CGA_IO_REG; + kd_lines = 25; + kd_cols = 40; + break; + case CM_CGA_80: + vid_start = (u_char *)phystokv(CGA_START); + kd_index_reg = CGA_IDX_REG; + kd_io_reg = CGA_IO_REG; + kd_lines = 25; + kd_cols = 80; + break; + case CM_MONO_80: + vid_start = (u_char *)phystokv(MONO_START); + kd_index_reg = MONO_IDX_REG; + kd_io_reg = MONO_IO_REG; + kd_lines = 25; + kd_cols = 80; + break; + } +#endif + + outb(kd_index_reg, C_START); + start = inb(kd_io_reg); + /* Make sure cursor is enabled */ + start &= ~0x20; + outb(kd_io_reg, start); + outb(kd_index_reg, C_STOP); + stop = inb(kd_io_reg); + + if (!start && !stop) + { + /* Some firmware seem not to be initializing the cursor size + * any more... Try using standard values. */ + outb(kd_index_reg, C_START); + outb(kd_io_reg, 14); + outb(kd_index_reg, C_STOP); + outb(kd_io_reg, 15); + } + + kd_setpos(xga_getpos()); +} + + +/* + * charput: + * + * Put attributed character for EGA/CGA/etc. + */ +static void +charput(csrpos_t pos, char ch, char chattr) +{ + *(vid_start + pos) = ch; + *(vid_start + pos + 1) = chattr; +} + + +/* + * charsetcursor: + * + * Set hardware cursor position for EGA/CGA/etc. + */ +static void +charsetcursor(csrpos_t newpos) +{ + short curpos; /* position, not scaled for attribute byte */ + + curpos = newpos / ONE_SPACE; + outb(kd_index_reg, C_HIGH); + outb(kd_io_reg, (u_char)(curpos>>8)); + outb(kd_index_reg, C_LOW); + outb(kd_io_reg, (u_char)(curpos&0xff)); + + kd_curpos = newpos; +} + + +/* + * charmvup: + * + * Block move up for EGA/CGA/etc. + */ +static void +charmvup(csrpos_t from, csrpos_t to, int count) +{ + kd_slmscu(vid_start+from, vid_start+to, count); +} + + +/* + * charmvdown: + * + * Block move down for EGA/CGA/etc. + */ +static void +charmvdown(csrpos_t from, csrpos_t to, int count) +{ + kd_slmscd(vid_start+from, vid_start+to, count); +} + + +/* + * charclear: + * + * Fast clear for CGA/EGA/etc. + */ +static void +charclear(csrpos_t to, int count, char chattr) +{ + kd_slmwd(vid_start+to, count, ((unsigned short)chattr<<8)+K_SPACE); +} + + +/* + * kd_noopreset: + * + * No-op reset routine for kd_dreset. + */ +static void +kd_noopreset(void) +{ +} + + +/* + * bmpput: Copy a character from the font to the frame buffer. + */ + +void +bmpput( + csrpos_t pos, + char ch, + char chattr) +{ + short xbit, ybit; /* u/l corner of char pos */ + u_char *to, *from; + short i, j; + u_char mask = (chattr == KA_REVERSE ? 0xff : 0); + + if ((u_char)ch >= chars_in_font) + ch = K_QUES; + + bmpch2bit(pos, &xbit, &ybit); + to = bit2fbptr(xbit, ybit); + from = font_start + ch * char_byte_width; + for (i = 0; i < char_height; ++i) { + for (j = 0; j < char_byte_width; ++j) + *(to+j) = *(from+j) ^ mask; + to += fb_byte_width; + from += font_byte_width; + } +} + +/* + * bmpcp1char: copy 1 char from one place in the frame buffer to + * another. + */ +static void +bmpcp1char( + csrpos_t from, + csrpos_t to) +{ + short from_xbit, from_ybit; + short to_xbit, to_ybit; + u_char *tp, *fp; + short i, j; + + bmpch2bit(from, &from_xbit, &from_ybit); + bmpch2bit(to, &to_xbit, &to_ybit); + + tp = bit2fbptr(to_xbit, to_ybit); + fp = bit2fbptr(from_xbit, from_ybit); + + for (i = 0; i < char_height; ++i) { + for (j = 0; j < char_byte_width; ++j) + *(tp+j) = *(fp+j); + tp += fb_byte_width; + fp += fb_byte_width; + } +} + +/* + * bmpvmup: Copy a block of character positions upwards. + */ +void +bmpmvup( + csrpos_t from, + csrpos_t to, + int count) +{ + short from_xbit, from_ybit; + short to_xbit, to_ybit; + short i; + + bmpch2bit(from, &from_xbit, &from_ybit); + bmpch2bit(to, &to_xbit, &to_ybit); + + if (from_xbit == xstart && to_xbit == xstart && count%kd_cols == 0) { + /* fast case - entire lines */ + from_xbit = to_xbit = 0; + bmppaintcsr(kd_curpos, char_black); /* don't copy cursor */ + count /= kd_cols; /* num lines */ + count *= fb_byte_width * (char_height+cursor_height); + kd_slmscu(bit2fbptr(from_xbit, from_ybit), + bit2fbptr(to_xbit, to_ybit), + count/SLAMBPW); + bmppaintcsr(kd_curpos, char_white); + } else { + /* slow case - everything else */ + for (i=0; i < count; ++i) { + bmpcp1char(from, to); + from += ONE_SPACE; + to += ONE_SPACE; + } + } +} + +/* + * bmpmvdown: copy a block of characters down. + */ +void +bmpmvdown( + csrpos_t from, + csrpos_t to, + int count) +{ + short from_xbit, from_ybit; + short to_xbit, to_ybit; + short i; + + bmpch2bit(from, &from_xbit, &from_ybit); + bmpch2bit(to, &to_xbit, &to_ybit); + + if (from_xbit == xstart + (kd_cols - 1) * char_width + && to_xbit == xstart + (kd_cols - 1) * char_width + && count%kd_cols == 0) { + /* fast case - entire lines*/ + from_xbit = to_xbit = 8 * (fb_byte_width - 1); + /* last byte on line */ + bmppaintcsr(kd_curpos, char_black); /* don't copy cursor */ + count /= kd_cols; /* num lines */ + count *= fb_byte_width * (char_height+cursor_height); + kd_slmscd(bit2fbptr(from_xbit, from_ybit), + bit2fbptr(to_xbit, to_ybit), + count/SLAMBPW); + bmppaintcsr(kd_curpos, char_white); + } else { + /* slow case - everything else */ + for (i=0; i < count; ++i) { + bmpcp1char(from, to); + from -= ONE_SPACE; + to -= ONE_SPACE; + } + } +} + +/* + * bmpclear: clear one or more character positions. + */ +void +bmpclear( + csrpos_t to, /* 1st char */ + int count, /* num chars */ + char chattr) /* reverse or normal */ +{ + short i; + u_short clearval; + u_short clearbyte = (chattr == KA_REVERSE ? char_white : char_black); + + clearval = (u_short)(clearbyte<<8) + clearbyte; + if (to == 0 && count >= kd_lines * kd_cols) { + /* fast case - entire page */ + kd_slmwd(vid_start, (fb_byte_width * fb_height)/SLAMBPW, + clearval); + } else + /* slow case */ + for (i = 0; i < count; ++i) { + bmpput(to, K_SPACE, chattr); + to += ONE_SPACE; + } +} + +/* + * bmpsetcursor: update the display and set the logical cursor. + */ +void +bmpsetcursor(csrpos_t pos) +{ + /* erase old cursor & paint new one */ + bmppaintcsr(kd_curpos, char_black); + bmppaintcsr(pos, char_white); + kd_curpos = pos; +} + +/* + * bmppaintcsr: paint cursor bits. + */ +void +bmppaintcsr( + csrpos_t pos, + u_char val) +{ + short xbit, ybit; + u_char *cp; + short line, byte; + + bmpch2bit(pos, &xbit, &ybit); + ybit += char_height; /* position at bottom of line */ + cp = bit2fbptr(xbit, ybit); + for (line = 0; line < cursor_height; ++line) { + for (byte = 0; byte < char_byte_width; ++byte) + *(cp+byte) = val; + cp += fb_byte_width; + } +} + +/* + * bmpch2bit: convert character position to x and y bit addresses. + * (0, 0) is the upper left corner. + */ +void +bmpch2bit( + csrpos_t pos, + short *xb, + short *yb) /* x, y bit positions, u/l corner */ +{ + short xch, ych; + + xch = (pos / ONE_SPACE) % kd_cols; + ych = pos / (ONE_SPACE * kd_cols); + *xb = xstart + xch * char_width; + *yb = ystart + ych * (char_height + cursor_height); +} + +/* + * bit2fbptr: return a pointer into the frame buffer corresponding to + * the bit address (x, y). + * Assumes that xb and yb don't point to the middle of a + * byte. + */ +u_char * +bit2fbptr( + short xb, + short yb) +{ + return(vid_start + yb * fb_byte_width + xb/8); +} + + +/* + * console stuff + */ + +/* + * XXX we assume that pcs *always* have a console + */ +int +kdcnprobe(struct consdev *cp) +{ + int maj, unit, pri; + + maj = 0; + unit = 0; + pri = CN_INTERNAL; + + cp->cn_dev = makedev(maj, unit); + cp->cn_pri = pri; + return 0; +} + +int +kdcninit(struct consdev *cp) +{ + kdinit(); + return 0; +} + +int +kdcngetc(dev_t dev, int wait) +{ + if (wait) { + int c; + while ((c = kdcnmaygetc()) < 0) + continue; + return c; + } + else + return kdcnmaygetc(); +} + +int +kdcnputc(dev_t dev, int c) +{ + if (!kd_initialized) + return -1; + + /* Note that tab is handled in kd_putc */ + if (c == '\n') + kd_putc('\r'); + kd_putc_esc(c); + + return 0; +} + +/* + * kdcnmaygetc: + * + * Get one character using polling, rather than interrupts. Used + * by the kernel debugger. Note that Caps Lock is ignored. + * Normally this routine is called with interrupts already + * disabled, but there is code in place so that it will be more + * likely to work even if interrupts are turned on. + */ +int +kdcnmaygetc(void) +{ + unsigned char c; + unsigned char scancode; + unsigned int char_idx; +#ifdef notdef + spl_t o_pri; +#endif + boolean_t up; + + if (! kd_initialized) + return -1; + + kd_extended = FALSE; +#ifdef notdef + o_pri = splhi(); +#endif + for ( ; ; ) { + if (!(inb(K_STATUS) & K_OBUF_FUL)) + return -1; + + up = FALSE; + /* + * We'd come here for mouse events in debugger, if + * the mouse were on. + */ + if ((inb(K_STATUS) & 0x20) == 0x20) { + printf("M%xP", inb(K_RDWR)); + continue; + } + scancode = inb(K_RDWR); + /* + * Handle extend modifier and + * ack/resend, otherwise we may never receive + * a key. + */ + if (scancode == K_EXTEND) { + kd_extended = TRUE; + continue; + } else if (scancode == K_RESEND) { + printf("cngetc: resend"); + kd_resend(); + continue; + } else if (scancode == K_ACKSC) { + printf("cngetc: handle_ack"); + kd_handle_ack(); + continue; + } + if (scancode & K_UP) { + up = TRUE; + scancode &= ~K_UP; + } + if (kd_kbd_mouse) + kd_kbd_magic(scancode); + if (scancode < NUMKEYS) { + /* Lookup in map, then process. */ + char_idx = kdstate2idx(kd_state, kd_extended); + c = key_map[scancode][char_idx]; + if (c == K_SCAN) { + c = key_map[scancode][++char_idx]; + kd_state = do_modifier(kd_state, c, up); +#ifdef notdef + cnsetleds(state2leds(kd_state)); +#endif + } else if (! up + && c == K_ESC + && key_map[scancode][char_idx+1] == 0x5b) { + /* As a convenience for the nice + people using our debugger, remap + some keys to the readline-like + shortcuts supported by dde. + + XXX This is a workaround for the + limited kernel getchar interface. + It is only used by the debugger. */ + c = key_map[scancode][char_idx+2]; + switch (c) { +#define _MAP(A,B,C) (C) +#define MAP(T) _MAP(T) +#define CTRL(c) ((c) & 0x1f) + case MAP(K_HOME): c = CTRL('a'); break; + case MAP(K_UA): c = CTRL('p'); break; + case MAP(K_LA): c = CTRL('b'); break; + case MAP(K_RA): c = CTRL('f'); break; + case MAP(K_DA): c = CTRL('n'); break; + case MAP(K_END): c = CTRL('e'); break; + /* delete */ + case 0x39: c = CTRL('d'); break; +#undef CTRL +#undef MAP +#undef _MAP + default: + /* Retain the old behavior. */ + c = K_ESC; + } + + return(c); + } else if (!up) { + /* regular key-down */ + if (c == K_CR) + c = K_LF; +#ifdef notdef + splx(o_pri); +#endif + return(c & 0177); + } + } + } +} diff --git a/i386/i386at/kd.h b/i386/i386at/kd.h new file mode 100644 index 0000000..5bfabce --- /dev/null +++ b/i386/i386at/kd.h @@ -0,0 +1,744 @@ +/* + * 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. + */ +/* ********************************************************************** + File: kd.h + Description: definitions for AT keyboard/display driver + Authors: Eugene Kuerner, Adrienne Jardetzky, Mike Kupfer + + $ Header: $ + + Copyright Ing. C. Olivetti & C. S.p.A. 1988, 1989. + All rights reserved. +********************************************************************** */ +/* + Copyright 1988, 1989 by Olivetti Advanced Technology Center, Inc., +Cupertino, 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 Olivetti +not be used in advertising or publicity pertaining to distribution +of the software without specific, written prior permission. + + OLIVETTI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, +IN NO EVENT SHALL OLIVETTI 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 OUR OF OR IN CONNECTION +WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/* + * This file contains defines and structures that implement hardware + * keyboard mapping into ansi defined output codes. Note that this + * is structured so that "re-mapping" of actual keys is allowed at + * anytime during execution of the console driver. And each scan code + * is potentially expanded into NUMKEYS characters. Which is programmable + * at runtime or whenever. + * + * 02 Nov 1988 orc!eugene + * + */ + +#ifndef _KD_H_ +#define _KD_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Where memory for various graphics adapters starts. + */ +#define EGA_START 0x0b8000 +#define CGA_START 0x0b8000 +#define MONO_START 0x0b0000 + +/* + * Common I/O ports. + */ +#define K_TMR0 0x40 /* timer 0, 1, or 2 value (r/w) */ +#define K_TMR1 0x41 +#define K_TMR2 0x42 +#define K_TMRCTL 0x43 /* timer control (write-only) */ +#define K_RDWR 0x60 /* keyboard data & cmds (read/write) */ +#define K_PORTB 0x61 /* r/w. speaker & status lines */ +#define K_STATUS 0x64 /* keybd status (read-only) */ +#define K_CMD 0x64 /* keybd ctlr command (write-only) */ + +/* + * I/O ports for various graphics adapters. + */ +#define EGA_IDX_REG 0x3d4 +#define EGA_IO_REG 0x3d5 +#define CGA_IDX_REG 0x3d4 +#define CGA_IO_REG 0x3d5 +#define MONO_IDX_REG 0x3b4 +#define MONO_IO_REG 0x3b5 + +/* + * Commands sent to graphics adapter. + */ +#define C_START 0x0a /* return cursor line start */ +#define C_STOP 0x0b /* return cursor line stop */ +#define C_LOW 0x0f /* return low byte of cursor addr */ +#define C_HIGH 0x0e /* high byte */ + +/* + * Bit definitions for K_STATUS port. + */ +#define K_OBUF_FUL 0x01 /* output (from keybd) buffer full */ +#define K_IBUF_FUL 0x02 /* input (to keybd) buffer full */ +#define K_SYSFLAG 0x04 /* "System Flag" */ +#define K_CMD_DATA 0x08 /* 1 = input buf has cmd, 0 = data */ +#define K_KBD_INHBT 0x10 /* 0 if keyboard inhibited */ + +/* + * Keyboard controller commands (sent to K_CMD port). + */ +#define KC_CMD_READ 0x20 /* read controller command byte */ +#define KC_CMD_WRITE 0x60 /* write controller command byte */ +#define KC_CMD_TEST 0xab /* test interface */ +#define KC_CMD_DUMP 0xac /* diagnostic dump */ +#define KC_CMD_DISBLE 0xad /* disable keyboard */ +#define KC_CMD_ENBLE 0xae /* enable keyboard */ +#define KC_CMD_RDKBD 0xc4 /* read keyboard ID */ +#define KC_CMD_ECHO 0xee /* used for diagnostic testing */ + +/* + * Keyboard commands (send to K_RDWR). + */ +#define K_CMD_LEDS 0xed /* set status LEDs (caps lock, etc.) */ + +/* + * Bit definitions for controller command byte (sent following + * K_CMD_WRITE command). + */ +#define K_CB_ENBLIRQ 0x01 /* enable data-ready intrpt */ +#define K_CB_SETSYSF 0x04 /* Set System Flag */ +#define K_CB_INHBOVR 0x08 /* Inhibit Override */ +#define K_CB_DISBLE 0x10 /* disable keyboard */ + +/* + * Bit definitions for "Indicator Status Byte" (sent after a + * K_CMD_LEDS command). If the bit is on, the LED is on. Undefined + * bit positions must be 0. + */ +#define K_LED_SCRLLK 0x1 /* scroll lock */ +#define K_LED_NUMLK 0x2 /* num lock */ +#define K_LED_CAPSLK 0x4 /* caps lock */ + +/* + * Bit definitions for "Miscellaneous port B" (K_PORTB). + */ +/* read/write */ +#define K_ENABLETMR2 0x01 /* enable output from timer 2 */ +#define K_SPKRDATA 0x02 /* direct input to speaker */ +#define K_ENABLEPRTB 0x04 /* "enable" port B */ +#define K_EIOPRTB 0x08 /* enable NMI on parity error */ +/* read-only */ +#define K_REFRESHB 0x10 /* refresh flag from INLTCONT PAL */ +#define K_OUT2B 0x20 /* timer 2 output */ +#define K_ICKB 0x40 /* I/O channel check (parity error) */ + +/* + * Bit definitions for timer control port (K_TMRCTL). + */ +/* select timer 0, 1, or 2. Don't mess with 0 or 1. */ +#define K_SELTMRMASK 0xc0 +#define K_SELTMR0 0x00 +#define K_SELTMR1 0x40 +#define K_SELTMR2 0x80 + +/* read/load control */ +#define K_RDLDTMRMASK 0x30 +#define K_HOLDTMR 0x00 /* freeze timer until read */ +#define K_RDLDTLSB 0x10 /* read/load LSB */ +#define K_RDLDTMSB 0x20 /* read/load MSB */ +#define K_RDLDTWORD 0x30 /* read/load LSB then MSB */ + +/* mode control */ +#define K_TMDCTLMASK 0x0e +#define K_TCOUNTINTR 0x00 /* "Term Count Intr" */ +#define K_TONESHOT 0x02 /* "Progr One-Shot" */ +#define K_TRATEGEN 0x04 /* "Rate Gen (/n)" */ +#define K_TSQRWAVE 0x06 /* "Sqr Wave Gen" */ +#define K_TSOFTSTRB 0x08 /* "Softw Trig Strob" */ +#define K_THARDSTRB 0x0a /* "Hardw Trig Strob" */ + +/* count mode */ +#define K_TCNTMDMASK 0x01 +#define K_TBINARY 0x00 /* 16-bit binary counter */ +#define K_TBCD 0x01 /* 4-decade BCD counter */ + + + +/* + * Fun definitions for displayed characters and characters read from + * the keyboard. + */ + +/* + * Attributes for character sent to display. + */ +#define KA_NORMAL 0x07 +#define KA_REVERSE 0x70 + +#define KAX_REVERSE 0x01 +#define KAX_UNDERLINE 0x02 +#define KAX_BLINK 0x04 +#define KAX_BOLD 0x08 +#define KAX_DIM 0x10 +#define KAX_INVISIBLE 0x20 + +#define KAX_COL_UNDERLINE 0x0f /* bright white */ +#define KAX_COL_DIM 0x08 /* gray */ + +/* + * For an EGA-like display, each character takes two bytes, one for the + * actual character, followed by one for its attributes. + * Be very careful if you change ONE_SPACE, as these constants are also used + * to define the device-independent display implemented by kd.c. + * (See kdsoft.h for more details on the device-independent display.) + */ +#define ONE_SPACE 2 /* bytes in 1 char, EGA-like display */ +#define BOTTOM_LINE 3840 /* 1st byte in last line of display */ +#define ONE_PAGE 4000 /* number of bytes in page */ +#define ONE_LINE 160 /* number of bytes in line */ + +#define BEG_OF_LINE(pos) ((pos) - (pos)%ONE_LINE) +#define CURRENT_COLUMN(pos) (((pos) % ONE_LINE) / ONE_SPACE) + +#define NUMKEYS 89 +#define NUMSTATES 5 /* NORM_STATE, ... */ +#define NUMOUTPUT 3 /* max size of byte seq from key */ +#define WIDTH_KMAP (NUMSTATES * NUMOUTPUT) + +/* + * Keyboard states. Used for KDGKBENT, KDSKBENT ioctl's. If you + * change these values, you should also rearrange the entries in + * key_map. + */ +/* "state indices" (for computing key_map index) */ +#define NORM_STATE 0 +#define SHIFT_STATE 1 +#define CTRL_STATE 2 +#define ALT_STATE 3 +#define SHIFT_ALT 4 +/* macro to convert from state index to actual key_map index */ +#define CHARIDX(sidx) ((sidx) * NUMOUTPUT) + /* where sidx is in [NORM_STATE ... SHIFT_ALT] */ + +/* "state bits" for kd_state vector */ +#define KS_NORMAL 0x00 +#define KS_SLKED 0x01 +#define KS_NLKED 0x02 +#define KS_CLKED 0x04 +#define KS_ALTED 0x08 +#define KS_SHIFTED 0x10 +#define KS_CTLED 0x20 + + +/* special codes */ +#define K_UP 0x80 /* OR'd in if key below is released */ +#define K_EXTEND 0xe0 /* marker for "extended" sequence */ +#define K_ACKSC 0xfa /* ack for keyboard command */ +#define K_RESEND 0xfe /* request to resend keybd cmd */ + +/* modifier keys */ +#define K_CTLSC 0x1d /* control down */ +#define K_LSHSC 0x2a /* left shift down */ +#define K_RSHSC 0x36 /* right shift down */ +#define K_ALTSC 0x38 /* alt key down */ +#define K_CLCKSC 0x3a /* caps lock */ +#define K_NLCKSC 0x45 /* num lock down */ + +/* "special keys" */ +#define K_BSSC 0x0e /* backspace */ +#define K_TABSC 0x0f /* tab */ +#define K_RETSC 0x1c /* return */ +#define K_SPSC 0x39 /* space */ +#define K_ESCSC 0x01 /* ESC */ + +/* alphabetic keys */ +#define K_qSC 0x10 +#define K_wSC 0x11 +#define K_eSC 0x12 +#define K_rSC 0x13 +#define K_tSC 0x14 +#define K_ySC 0x15 +#define K_uSC 0x16 +#define K_iSC 0x17 +#define K_oSC 0x18 +#define K_pSC 0x19 + +#define K_aSC 0x1e +#define K_sSC 0x1f +#define K_dSC 0x20 +#define K_fSC 0x21 +#define K_gSC 0x22 +#define K_hSC 0x23 +#define K_jSC 0x24 +#define K_kSC 0x25 +#define K_lSC 0x26 + +#define K_zSC 0x2c +#define K_xSC 0x2d +#define K_cSC 0x2e +#define K_vSC 0x2f +#define K_bSC 0x30 +#define K_nSC 0x31 +#define K_mSC 0x32 + +/* numbers and punctuation */ +#define K_ONESC 0x02 /* 1 */ +#define K_TWOSC 0x03 /* 2 */ +#define K_THREESC 0x04 /* 3 */ +#define K_FOURSC 0x05 /* 4 */ +#define K_FIVESC 0x06 /* 5 */ +#define K_SIXSC 0x07 /* 6 */ +#define K_SEVENSC 0x08 /* 7 */ +#define K_EIGHTSC 0x09 /* 8 */ +#define K_NINESC 0x0a /* 9 */ +#define K_ZEROSC 0x0b /* 0 */ + +#define K_MINUSSC 0x0c /* - */ +#define K_EQLSC 0x0d /* = */ +#define K_LBRKTSC 0x1a /* [ */ +#define K_RBRKTSC 0x1b /* ] */ +#define K_SEMISC 0x27 /* ; */ +#define K_SQUOTESC 0x28 /* ' */ +#define K_GRAVSC 0x29 /* ` */ +#define K_BSLSHSC 0x2b /* \ */ +#define K_COMMASC 0x33 /* , */ +#define K_PERIODSC 0x34 /* . */ +#define K_SLASHSC 0x35 /* / */ + +/* keypad keys */ +#define K_HOMESC 0x47 /* scancode for home */ +#define K_DELSC 0x53 /* scancode for del */ + +/* + * Ascii values and flag characters for key map. + * A function key is represented by the 3-byte char sequence that it + * corresponds to. + * Other mappable non-Ascii keys (e.g., "ctrl") are represented by a + * two-byte sequence: K_SCAN, followed by the key's scan code. + */ +#define K_DONE 0xffu /* must be same as NC */ +#define NC 0xffu /* No character defined */ + +#define K_SCAN 0xfeu /* followed by scan code */ + +/* ascii char set */ +#define K_NUL 0x00 /* Null character */ +#define K_SOH 0x01 +#define K_STX 0x02 +#define K_ETX 0x03 +#define K_EOT 0x04 +#define K_ENQ 0x05 +#define K_ACK 0x06 +#define K_BEL 0x07 /* bell character */ +#define K_BS 0x08 /* back space */ +#define K_HT 0x09 +#define K_LF 0x0a /* line feed */ +#define K_VT 0x0b +#define K_FF 0x0c +#define K_CR 0x0d /* carriage return */ +#define K_SO 0x0e +#define K_SI 0x0f +#define K_DLE 0x10 +#define K_DC1 0x11 +#define K_DC2 0x12 +#define K_DC3 0x13 +#define K_DC4 0x14 +#define K_NAK 0x15 +#define K_SYN 0x16 +#define K_ETB 0x17 +#define K_CAN 0x18 +#define K_EM 0x19 +#define K_SUB 0x1a +#define K_ESC 0x1b /* escape character */ +#define K_FS 0x1c +#define K_GS 0x1d +#define K_RS 0x1e +#define K_US 0x1f +#define K_SPACE 0x20 /* space character */ +#define K_BANG 0x21 /* ! */ +#define K_DQUOTE 0x22 /* " */ +#define K_POUND 0x23 /* # */ +#define K_DOLLAR 0x24 /* $ */ +#define K_PERC 0x25 /* % */ +#define K_AMPER 0x26 /* & */ +#define K_SQUOTE 0x27 /* ' */ +#define K_LPAREN 0x28 /* ( */ +#define K_RPAREN 0x29 /* ) */ +#define K_ASTER 0x2a /* * */ +#define K_PLUS 0x2b /* + */ +#define K_COMMA 0x2c /* , */ +#define K_MINUS 0x2d /* - */ +#define K_PERIOD 0x2e /* . */ +#define K_SLASH 0x2f /* / */ +#define K_ZERO 0x30 /* 0 */ +#define K_ONE 0x31 /* 1 */ +#define K_TWO 0x32 /* 2 */ +#define K_THREE 0x33 /* 3 */ +#define K_FOUR 0x34 /* 4 */ +#define K_FIVE 0x35 /* 5 */ +#define K_SIX 0x36 /* 6 */ +#define K_SEVEN 0x37 /* 7 */ +#define K_EIGHT 0x38 /* 8 */ +#define K_NINE 0x39 /* 9 */ +#define K_COLON 0x3a /* : */ +#define K_SEMI 0x3b /* ; */ +#define K_LTHN 0x3c /* < */ +#define K_EQL 0x3d /* = */ +#define K_GTHN 0x3e /* > */ +#define K_QUES 0x3f /* ? */ +#define K_ATSN 0x40 /* @ */ +#define K_A 0x41 /* A */ +#define K_B 0x42 /* B */ +#define K_C 0x43 /* C */ +#define K_D 0x44 /* D */ +#define K_E 0x45 /* E */ +#define K_F 0x46 /* F */ +#define K_G 0x47 /* G */ +#define K_H 0x48 /* H */ +#define K_I 0x49 /* I */ +#define K_J 0x4a /* J */ +#define K_K 0x4b /* K */ +#define K_L 0x4c /* L */ +#define K_M 0x4d /* M */ +#define K_N 0x4e /* N */ +#define K_O 0x4f /* O */ +#define K_P 0x50 /* P */ +#define K_Q 0x51 /* Q */ +#define K_R 0x52 /* R */ +#define K_S 0x53 /* S */ +#define K_T 0x54 /* T */ +#define K_U 0x55 /* U */ +#define K_V 0x56 /* V */ +#define K_W 0x57 /* W */ +#define K_X 0x58 /* X */ +#define K_Y 0x59 /* Y */ +#define K_Z 0x5a /* Z */ +#define K_LBRKT 0x5b /* [ */ +#define K_BSLSH 0x5c /* \ */ +#define K_RBRKT 0x5d /* ] */ +#define K_CARET 0x5e /* ^ */ +#define K_UNDSC 0x5f /* _ */ +#define K_GRAV 0x60 /* ` */ +#define K_a 0x61 /* a */ +#define K_b 0x62 /* b */ +#define K_c 0x63 /* c */ +#define K_d 0x64 /* d */ +#define K_e 0x65 /* e */ +#define K_f 0x66 /* f */ +#define K_g 0x67 /* g */ +#define K_h 0x68 /* h */ +#define K_i 0x69 /* i */ +#define K_j 0x6a /* j */ +#define K_k 0x6b /* k */ +#define K_l 0x6c /* l */ +#define K_m 0x6d /* m */ +#define K_n 0x6e /* n */ +#define K_o 0x6f /* o */ +#define K_p 0x70 /* p */ +#define K_q 0x71 /* q */ +#define K_r 0x72 /* r */ +#define K_s 0x73 /* s */ +#define K_t 0x74 /* t */ +#define K_u 0x75 /* u */ +#define K_v 0x76 /* v */ +#define K_w 0x77 /* w */ +#define K_x 0x78 /* x */ +#define K_y 0x79 /* y */ +#define K_z 0x7a /* z */ +#define K_LBRACE 0x7b /* { */ +#define K_PIPE 0x7c /* | */ +#define K_RBRACE 0x7d /* } */ +#define K_TILDE 0x7e /* ~ */ +#define K_DEL 0x7f /* delete */ + +/* Ascii sequences to be generated by the named key */ +#define K_F1 0x1b,0x4f,0x50 +#define K_F1S 0x1b,0x4f,0x70 +#define K_F2 0x1b,0x4f,0x51 +#define K_F2S 0x1b,0x4f,0x71 +#define K_F3 0x1b,0x4f,0x52 +#define K_F3S 0x1b,0x4f,0x72 +#define K_F4 0x1b,0x4f,0x53 +#define K_F4S 0x1b,0x4f,0x73 +#define K_F5 0x1b,0x4f,0x54 +#define K_F5S 0x1b,0x4f,0x74 +#define K_F6 0x1b,0x4f,0x55 +#define K_F6S 0x1b,0x4f,0x75 +#define K_F7 0x1b,0x4f,0x56 +#define K_F7S 0x1b,0x4f,0x76 +#define K_F8 0x1b,0x4f,0x57 +#define K_F8S 0x1b,0x4f,0x77 +#define K_F9 0x1b,0x4f,0x58 +#define K_F9S 0x1b,0x4f,0x78 +#define K_F10 0x1b,0x4f,0x59 +#define K_F10S 0x1b,0x4f,0x79 +#define K_F11 0x1b,0x4f,0x5a +#define K_F11S 0x1b,0x4f,0x7a +#define K_F12 0x1b,0x4f,0x41 +#define K_F12S 0x1b,0x4f,0x61 + +/* These are the Alt-FxxA #defines. They work with the new keymap + -- Derek Upham 1997/06/25 */ +#define K_F1A 0x1b,0x4f,0x30 +#define K_F2A 0x1b,0x4f,0x31 +#define K_F3A 0x1b,0x4f,0x32 +#define K_F4A 0x1b,0x4f,0x33 +#define K_F5A 0x1b,0x4f,0x34 +#define K_F6A 0x1b,0x4f,0x35 +#define K_F7A 0x1b,0x4f,0x36 +#define K_F8A 0x1b,0x4f,0x37 +#define K_F9A 0x1b,0x4f,0x38 +#define K_F10A 0x1b,0x4f,0x39 +#define K_F11A 0x1b,0x4f,0x3a +#define K_F12A 0x1b,0x4f,0x3b + +#define K_SCRL 0x1b,0x5b,0x4d +#define K_HOME 0x1b,0x5b,0x48 +#define K_UA 0x1b,0x5b,0x41 +#define K_PUP 0x1b,0x5b,0x56 +#define K_LA 0x1b,0x5b,0x44 +#define K_RA 0x1b,0x5b,0x43 +#define K_END 0x1b,0x5b,0x59 +#define K_DA 0x1b,0x5b,0x42 +#define K_PDN 0x1b,0x5b,0x55 +#define K_INS 0x1b,0x5b,0x40 + +#define KBD_IRQ 1 + +/* + * This array maps scancodes to Ascii characters (or character + * sequences). + * The first index is the scancode. The first NUMOUTPUT characters + * (accessed using the second index) correspond to the key's char + * sequence for the Normal state. The next NUMOUTPUT characters + * are for the Shift state, then Ctrl, then Alt, then Shift/Alt. + */ +#ifdef KERNEL +extern u_char key_map[NUMKEYS][WIDTH_KMAP]; +#endif /* KERNEL */ + + + +/* + * These routines are declared here so that all the modules making + * up the kd driver agree on how to do locking. + */ + +#ifdef KERNEL +#include +#define SPLKD spltty +#endif /* KERNEL */ + + +/* + * Ioctl's on /dev/console. + */ + +/* + * KDGKBENT, KDSKBENT - Get and set keyboard table entry. Useful for + * remapping keys. + * + * KDGSTATE - Get the keyboard state variable, which flags the + * modifier keys (shift, ctrl, etc.) that are down. See + * KS_NORMAL et al above. Used for debugging. + * + * KDSETBELL - Turns the bell on or off. + */ + +#define KDGKBENT _IOWR('k', 1, struct kbentry) /* get keybd entry */ + +#define KDSKBENT _IOW('k', 2, struct kbentry) /* set keybd entry */ + +#define KDGSTATE _IOR('k', 3, int) /* get keybd state */ + +#define KDSETBELL _IOW('k', 4, int) /* turn bell on or off */ +# define KD_BELLON 1 +# define KD_BELLOFF 0 + +/* + * This struct is used for getting and setting key definitions. The + * values for kb_index are obtainable from the man page for + * keyboard(7) (though they should really be defined here!). + */ +struct kbentry { + u_char kb_state; /* which state to use */ + u_char kb_index; /* which keycode */ + u_char kb_value[NUMOUTPUT]; /* value to get/set */ +}; + + +/* + * Ioctl's on /dev/kbd. + */ + +#ifdef KERNEL +extern int kb_mode; +#endif + +struct X_kdb { + u_int *ptr; + u_int size; +}; + +#define K_X_KDB_ENTER _IOW('K', 16, struct X_kdb) +#define K_X_KDB_EXIT _IOW('K', 17, struct X_kdb) + +#define K_X_IN 0x01000000 +#define K_X_OUT 0x02000000 +#define K_X_BYTE 0x00010000 +#define K_X_WORD 0x00020000 +#define K_X_LONG 0x00040000 +#define K_X_TYPE 0x03070000 +#define K_X_PORT 0x0000ffff + +extern boolean_t kd_isupper (u_char); +extern boolean_t kd_islower (u_char); +extern void kd_senddata (unsigned char); +extern void kd_sendcmd (unsigned char); +extern void kd_cmdreg_write (int); +extern void kd_mouse_drain (void); +extern void set_kd_state (int); +extern void kd_setleds1 (u_char); +extern void kd_setleds2 (void); +extern void cnsetleds (u_char); +extern void kdreboot (void); +extern void kd_putc_esc (u_char); +extern void kd_putc (u_char); +extern void kd_parseesc (void); +extern void kd_down (void); +extern void kd_up (void); +extern void kd_cr (void); +extern void kd_tab (void); +extern void kd_left (void); +extern void kd_right (void); +extern void kd_scrollup (void); +extern void kd_scrolldn (void); +extern void kd_cls (void); +extern void kd_home (void); +extern void kd_insch (int number); +extern void kd_cltobcur (void); +extern void kd_cltopcur (void); +extern void kd_cltoecur (void); +extern void kd_clfrbcur (void); +extern void kd_eraseln (void); +extern void kd_insln (int); +extern void kd_delln (int); +extern void kd_delch (int); +extern void kd_erase (int); +extern void kd_bellon (void); +extern void kd_belloff (void *param); +extern void kdinit (void); +extern int kdsetkbent (struct kbentry *, int); +extern int kdgetkbent (struct kbentry *); +extern int kdsetbell (int, int); +extern void kd_resend (void); +extern void kd_handle_ack (void); +extern int kd_kbd_magic (int); +extern unsigned int kdstate2idx (unsigned int, boolean_t); +extern void kd_parserest (u_char *); +extern int kdcnprobe(struct consdev *cp); +extern int kdcninit(struct consdev *cp); +extern int kdcngetc(dev_t dev, int wait); +extern int kdcnmaygetc (void); +extern int kdcnputc(dev_t dev, int c); +extern void kd_setpos(csrpos_t newpos); + +extern void kd_slmwd (void *start, int count, int value); +extern void kd_slmscu (void *from, void *to, int count); +extern void kd_slmscd (void *from, void *to, int count); + +extern void kdintr(int vec); + +#if MACH_KDB +#include +#endif /* MACH_KDB */ + +extern int kdopen(dev_t dev, int flag, io_req_t ior); +extern void kdclose(dev_t dev, int flag); +extern int kdread(dev_t dev, io_req_t uio); +extern int kdwrite(dev_t dev, io_req_t uio); + +extern io_return_t kdgetstat( + dev_t dev, + dev_flavor_t flavor, + dev_status_t data, + mach_msg_type_number_t *count); + +extern io_return_t kdsetstat( + dev_t dev, + dev_flavor_t flavor, + dev_status_t data, + mach_msg_type_number_t count); + +extern int kdportdeath(dev_t dev, mach_port_t port); +extern vm_offset_t kdmmap(dev_t dev, vm_offset_t off, vm_prot_t prot); + +boolean_t kdcheckmagic(Scancode scancode); + +int do_modifier(int state, Scancode c, boolean_t up); + +/* + * Generic routines for bitmap devices (i.e., assume no hardware + * assist). Assumes a simple byte ordering (i.e., a byte at a lower + * address is to the left of the byte at the next higher address). + * For the 82786, this works anyway if the characters are 2 bytes + * wide. (more bubble gum and paper clips.) + * + * See the comments above (in i386at/kd.c) about SLAMBPW. + */ +void bmpch2bit(csrpos_t pos, short *xb, short *yb); +void bmppaintcsr(csrpos_t pos, u_char val); +u_char *bit2fbptr(short xb, short yb); + +unsigned char kd_getdata(void); +unsigned char state2leds(int state); + +void kdstart(struct tty *tp); +void kdstop(struct tty *tp, int flags); + +void kd_xga_init(void); + +#endif /* _KD_H_ */ diff --git a/i386/i386at/kd_event.c b/i386/i386at/kd_event.c new file mode 100644 index 0000000..247d95b --- /dev/null +++ b/i386/i386at/kd_event.c @@ -0,0 +1,392 @@ +/* + * 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. + */ +/* ********************************************************************** + File: kd_event.c + Description: Driver for event interface to keyboard. + + $ Header: $ + + Copyright Ing. C. Olivetti & C. S.p.A. 1989. All rights reserved. +********************************************************************** */ +/* + Copyright 1988, 1989 by Olivetti Advanced Technology Center, Inc., +Cupertino, 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 Olivetti +not be used in advertising or publicity pertaining to distribution +of the software without specific, written prior permission. + + OLIVETTI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, +IN NO EVENT SHALL OLIVETTI 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 OUR OF OR IN CONNECTION +WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#ifdef APIC +# include +#else +# include +#endif + +#include "kd_event.h" + +/* + * Code for /dev/kbd. The interrupt processing is done in kd.c, + * which calls into this module to enqueue scancode events when + * the keyboard is in Event mode. + */ + +/* + * Note: These globals are protected by raising the interrupt level + * via SPLKD. + */ + +kd_event_queue kbd_queue; /* queue of keyboard events */ +queue_head_t kbd_read_queue = { &kbd_read_queue, &kbd_read_queue }; + +static boolean_t initialized = FALSE; + + +/* + * kbdinit - set up event queue. + */ + +static void +kbdinit(void) +{ + spl_t s = SPLKD(); + + if (!initialized) { + kdq_reset(&kbd_queue); + initialized = TRUE; + } + splx(s); +} + + +/* + * kbdopen - Verify that open is read-only and remember process + * group leader. + */ + +/*ARGSUSED*/ +int +kbdopen(dev_t dev, int flags, io_req_t ior) +{ + spl_t o_pri = spltty(); + kdinit(); + splx(o_pri); + kbdinit(); + + return(0); +} + + +/* + * kbdclose - Make sure that the kd driver is in Ascii mode and + * reset various flags. + */ + +/*ARGSUSED*/ +void +kbdclose( + dev_t dev, + int flags) +{ + spl_t s = SPLKD(); + + kb_mode = KB_ASCII; + kdq_reset(&kbd_queue); + splx(s); +} + + +io_return_t kbdgetstat( + dev_t dev, + dev_flavor_t flavor, + dev_status_t data, /* pointer to OUT array */ + mach_msg_type_number_t *count) /* OUT */ +{ + switch (flavor) { + case KDGKBDTYPE: + *data = KB_VANILLAKB; + *count = 1; + break; + case DEV_GET_SIZE: + data[DEV_GET_SIZE_DEVICE_SIZE] = 0; + data[DEV_GET_SIZE_RECORD_SIZE] = sizeof(kd_event); + *count = DEV_GET_SIZE_COUNT; + break; + default: + return (D_INVALID_OPERATION); + } + return (D_SUCCESS); +} + +io_return_t kbdsetstat( + dev_t dev, + dev_flavor_t flavor, + dev_status_t data, + mach_msg_type_number_t count) +{ + switch (flavor) { + case KDSKBDMODE: + kb_mode = *data; + /* XXX - what to do about unread events? */ + /* XXX - should check that 'data' contains an OK valud */ + break; + case KDSETLEDS: + if (count != 1) + return (D_INVALID_OPERATION); + kd_setleds1 (*data); + break; + case K_X_KDB_ENTER: + return X_kdb_enter_init((unsigned int *)data, count); + case K_X_KDB_EXIT: + return X_kdb_exit_init((unsigned int *)data, count); + default: + return (D_INVALID_OPERATION); + } + return (D_SUCCESS); +} + + + +/* + * kbdread - dequeue and return any queued events. + */ +int +kbdread( + dev_t dev, + io_req_t ior) +{ + int err, count; + spl_t s; + + /* Check if IO_COUNT is a multiple of the record size. */ + if (ior->io_count % sizeof(kd_event) != 0) + return D_INVALID_SIZE; + + err = device_read_alloc(ior, (vm_size_t)ior->io_count); + if (err != KERN_SUCCESS) + return (err); + + s = SPLKD(); + if (kdq_empty(&kbd_queue)) { + if (ior->io_mode & D_NOWAIT) { + splx(s); + return (D_WOULD_BLOCK); + } + ior->io_done = kbd_read_done; + enqueue_tail(&kbd_read_queue, (queue_entry_t) ior); + splx(s); + return (D_IO_QUEUED); + } + count = 0; + while (!kdq_empty(&kbd_queue) && count < ior->io_count) { + kd_event *ev; + + ev = kdq_get(&kbd_queue); + *(kd_event *)(&ior->io_data[count]) = *ev; + count += sizeof(kd_event); + } + splx(s); + ior->io_residual = ior->io_count - count; + return (D_SUCCESS); +} + +boolean_t kbd_read_done(io_req_t ior) +{ + int count; + spl_t s; + + s = SPLKD(); + if (kdq_empty(&kbd_queue)) { + ior->io_done = kbd_read_done; + enqueue_tail(&kbd_read_queue, (queue_entry_t)ior); + splx(s); + return (FALSE); + } + + count = 0; + while (!kdq_empty(&kbd_queue) && count < ior->io_count) { + kd_event *ev; + + ev = kdq_get(&kbd_queue); + *(kd_event *)(&ior->io_data[count]) = *ev; + count += sizeof(kd_event); + } + splx(s); + + ior->io_residual = ior->io_count - count; + ds_read_done(ior); + + return (TRUE); +} + + + +/* + * kd_enqsc - enqueue a scancode. Should be called at SPLKD. + */ + +void +kd_enqsc(Scancode sc) +{ + kd_event ev; + + ev.type = KEYBD_EVENT; + /* Not used but we set it to avoid garbage */ + ev.unused_time.seconds = 0; + ev.unused_time.microseconds = 0; + ev.value.sc = sc; + kbd_enqueue(&ev); +} + + +/* + * kbd_enqueue - enqueue an event and wake up selecting processes, if + * any. Should be called at SPLKD. + */ + +void +kbd_enqueue(kd_event *ev) +{ + if (kdq_full(&kbd_queue)) + printf_once("kbd: queue full\n"); + else + kdq_put(&kbd_queue, ev); + + { + io_req_t ior; + while ((ior = (io_req_t)dequeue_head(&kbd_read_queue)) != 0) + iodone(ior); + } +} + +u_int X_kdb_enter_str[512], X_kdb_exit_str[512]; +int X_kdb_enter_len = 0, X_kdb_exit_len = 0; + +static void +kdb_in_out(const u_int *p) +{ + int t = p[0]; + + switch (t & K_X_TYPE) { + case K_X_IN|K_X_BYTE: + inb(t & K_X_PORT); + break; + + case K_X_IN|K_X_WORD: + inw(t & K_X_PORT); + break; + + case K_X_IN|K_X_LONG: + inl(t & K_X_PORT); + break; + + case K_X_OUT|K_X_BYTE: + outb(t & K_X_PORT, p[1]); + break; + + case K_X_OUT|K_X_WORD: + outw(t & K_X_PORT, p[1]); + break; + + case K_X_OUT|K_X_LONG: + outl(t & K_X_PORT, p[1]); + break; + } +} + +void +X_kdb_enter(void) +{ + u_int *u_ip, *endp; + + for (u_ip = X_kdb_enter_str, endp = &X_kdb_enter_str[X_kdb_enter_len]; + u_ip < endp; + u_ip += 2) + kdb_in_out(u_ip); +} + +void +X_kdb_exit(void) +{ + u_int *u_ip, *endp; + + for (u_ip = X_kdb_exit_str, endp = &X_kdb_exit_str[X_kdb_exit_len]; + u_ip < endp; + u_ip += 2) + kdb_in_out(u_ip); +} + +io_return_t +X_kdb_enter_init( + u_int *data, + u_int count) +{ + if (count * sizeof X_kdb_enter_str[0] > sizeof X_kdb_enter_str) + return D_INVALID_OPERATION; + + memcpy(X_kdb_enter_str, data, count * sizeof X_kdb_enter_str[0]); + X_kdb_enter_len = count; + return D_SUCCESS; +} + +io_return_t +X_kdb_exit_init( + u_int *data, + u_int count) +{ + if (count * sizeof X_kdb_exit_str[0] > sizeof X_kdb_exit_str) + return D_INVALID_OPERATION; + + memcpy(X_kdb_exit_str, data, count * sizeof X_kdb_exit_str[0]); + X_kdb_exit_len = count; + return D_SUCCESS; +} diff --git a/i386/i386at/kd_event.h b/i386/i386at/kd_event.h new file mode 100644 index 0000000..7e66f76 --- /dev/null +++ b/i386/i386at/kd_event.h @@ -0,0 +1,62 @@ +/* + * Keyboard event handlers + * Copyright (C) 2006 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Barry deFreese. + */ +/* + * Keyboard event handling functions. + * + */ + +#ifndef _KD_EVENT_H_ +#define _KD_EVENT_H_ + +#include +#include +#include + +extern void X_kdb_enter (void); + +extern void X_kdb_exit (void); + +extern int kbdopen(dev_t dev, int flags, io_req_t ior); +extern void kbdclose(dev_t dev, int flags); +extern int kbdread(dev_t dev, io_req_t ior); + +extern io_return_t kbdgetstat( + dev_t dev, + dev_flavor_t flavor, + dev_status_t data, + mach_msg_type_number_t *count); + +extern io_return_t kbdsetstat( + dev_t dev, + dev_flavor_t flavor, + dev_status_t data, + mach_msg_type_number_t count); + +extern void kd_enqsc(Scancode sc); + +void kbd_enqueue(kd_event *ev); + +io_return_t X_kdb_enter_init(u_int *data, u_int count); +io_return_t X_kdb_exit_init(u_int *data, u_int count); + +boolean_t kbd_read_done(io_req_t ior); + +#endif /* _KD_EVENT_H_ */ diff --git a/i386/i386at/kd_mouse.c b/i386/i386at/kd_mouse.c new file mode 100644 index 0000000..9bd001c --- /dev/null +++ b/i386/i386at/kd_mouse.c @@ -0,0 +1,800 @@ +/* + * 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. + */ +/* ********************************************************************** + File: kd_mouse.c + Description: mouse driver as part of keyboard/display driver + + $ Header: $ + + Copyright Ing. C. Olivetti & C. S.p.A. 1989. + All rights reserved. +********************************************************************** */ +/* + Copyright 1988, 1989 by Olivetti Advanced Technology Center, Inc., +Cupertino, 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 Olivetti +not be used in advertising or publicity pertaining to distribution +of the software without specific, written prior permission. + + OLIVETTI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, +IN NO EVENT SHALL OLIVETTI 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 OUR OF OR IN CONNECTION +WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/* + * Hacked up support for serial mouse connected to COM1, using Mouse + * Systems 5-byte protocol at 1200 baud. This should work for + * Mouse Systems, SummaMouse, and Logitek C7 mice. + * + * The interface provided by /dev/mouse is a series of events as + * described in i386at/kd.h. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kd_mouse.h" + +static interrupt_handler_fn oldvect; /* old interrupt vector */ +static int oldunit; +extern struct bus_device *cominfo[]; + +kd_event_queue mouse_queue; /* queue of mouse events */ +boolean_t mouse_in_use = FALSE; +queue_head_t mouse_read_queue = { &mouse_read_queue, &mouse_read_queue }; + + +/* + * The state of the 3 buttons is encoded in the low-order 3 bits (both + * here and in other variables in the driver). + */ +u_char lastbuttons; /* previous state of mouse buttons */ +#define MOUSE_UP 1 +#define MOUSE_DOWN 0 +#define MOUSE_ALL_UP 0x7 + +int mouse_baud = BCNT1200; + +boolean_t mouse_char_cmd = FALSE; /* mouse response is to cmd */ +boolean_t mouse_char_wanted = FALSE; /* want mouse response */ +int mouse_char_index; /* mouse response */ + +#define IBM_MOUSE_IRQ 12 + +/* + * init_mouse_hw - initialize the serial port. + */ +static void +init_mouse_hw(dev_t unit, int mode) +{ + unsigned short base_addr = cominfo[unit]->address; + + outb(base_addr + RIE, 0); + outb(base_addr + RLC, LCDLAB); + outb(base_addr + RDLSB, mouse_baud & 0xff); + outb(base_addr + RDMSB, (mouse_baud >> 8) & 0xff); + outb(base_addr + RLC, mode); + outb(base_addr + RMC, MCDTR | MCRTS | MCOUT2); + outb(base_addr + RIE, IERD | IELS); +} + + +/* + * mouseopen - Verify that the request is read-only, initialize, + * and remember process group leader. + */ +/* + * Low 3 bits of minor are the com port #. + * The high 5 bits of minor are the mouse type + */ +#define MOUSE_SYSTEM_MOUSE 0 +#define MICROSOFT_MOUSE 1 +#define IBM_MOUSE 2 +#define NO_MOUSE 3 +#define LOGITECH_TRACKMAN 4 +#define MICROSOFT_MOUSE7 5 +static int mouse_type; +static int mousebufsize; +static int mousebufindex = 0; +int track_man[10]; + +/*ARGSUSED*/ +int +mouseopen(dev_t dev, int flags, io_req_t ior) +{ + if (mouse_in_use) + return (D_ALREADY_OPEN); + mouse_in_use = TRUE; /* locking? */ + kdq_reset(&mouse_queue); + lastbuttons = MOUSE_ALL_UP; + + switch (mouse_type = ((minor(dev) & 0xf8) >> 3)) { + case MICROSOFT_MOUSE7: + mousebufsize = 3; + serial_mouse_open(dev); + init_mouse_hw(dev&7, LC7); + break; + case MICROSOFT_MOUSE: + mousebufsize = 3; + serial_mouse_open(dev); + init_mouse_hw(dev&7, LC8); + break; + case MOUSE_SYSTEM_MOUSE: + mousebufsize = 5; + serial_mouse_open(dev); + init_mouse_hw(dev&7, LC8); + break; + case LOGITECH_TRACKMAN: + mousebufsize = 3; + serial_mouse_open(dev); + init_mouse_hw(dev&7, LC7); + track_man[0] = comgetc(dev&7); + track_man[1] = comgetc(dev&7); + if (track_man[0] != 0x4d && + track_man[1] != 0x33) { + printf("LOGITECH_TRACKMAN: NOT M3"); + } + break; + case IBM_MOUSE: + mousebufsize = 3; + kd_mouse_open(dev, IBM_MOUSE_IRQ); + ibm_ps2_mouse_open(dev); + break; + case NO_MOUSE: + break; + } + mousebufindex = 0; + return(0); +} + +void +serial_mouse_open(dev_t dev) +{ + int unit = minor(dev) & 0x7; + int mouse_pic = cominfo[unit]->sysdep1; + + spl_t s = splhi(); /* disable interrupts */ + + oldvect = ivect[mouse_pic]; + ivect[mouse_pic] = mouseintr; + + oldunit = iunit[mouse_pic]; + iunit[mouse_pic] = unit; + + /* XXX other arrays to init? */ + splx(s); /* XXX - should come after init? */ +} + +int mouse_packets = 0; + +void +kd_mouse_open( + dev_t dev, + int mouse_pic) +{ + spl_t s = splhi(); /* disable interrupts */ + + oldvect = ivect[mouse_pic]; + ivect[mouse_pic] = kdintr; + unmask_irq(mouse_pic); + splx(s); +} + +/* + * mouseclose - Disable interrupts on the serial port, reset driver flags, + * and restore the serial port interrupt vector. + */ +void +mouseclose( + dev_t dev, + int flags) +{ + switch (mouse_type) { + case MICROSOFT_MOUSE: + case MICROSOFT_MOUSE7: + case MOUSE_SYSTEM_MOUSE: + case LOGITECH_TRACKMAN: + serial_mouse_close(dev, flags); + break; + case IBM_MOUSE: + ibm_ps2_mouse_close(dev); + kd_mouse_close(dev, IBM_MOUSE_IRQ); + {int i = 20000; for (;i--;); } + kd_mouse_drain(); + break; + case NO_MOUSE: + break; + } + + kdq_reset(&mouse_queue); /* paranoia */ + mouse_in_use = FALSE; +} + +/*ARGSUSED*/ +void +serial_mouse_close( + dev_t dev, + int flags) +{ + spl_t o_pri = splhi(); /* mutex with open() */ + int unit = minor(dev) & 0x7; + int mouse_pic = cominfo[unit]->sysdep1; + unsigned short base_addr = cominfo[unit]->address; + + assert(ivect[mouse_pic] == mouseintr); + outb(base_addr + RIE, 0); /* disable serial port */ + outb(base_addr + RMC, 0); /* no rts */ + ivect[mouse_pic] = oldvect; + iunit[mouse_pic] = oldunit; + + (void)splx(o_pri); +} + +void +kd_mouse_close( + dev_t dev, + int mouse_pic) +{ + spl_t s = splhi(); + + mask_irq(mouse_pic); + ivect[mouse_pic] = oldvect; + splx(s); +} + +io_return_t mousegetstat( + dev_t dev, + dev_flavor_t flavor, + dev_status_t data, /* pointer to OUT array */ + mach_msg_type_number_t *count) /* OUT */ +{ + switch (flavor) { + case DEV_GET_SIZE: + data[DEV_GET_SIZE_DEVICE_SIZE] = 0; + data[DEV_GET_SIZE_RECORD_SIZE] = sizeof(kd_event); + *count = DEV_GET_SIZE_COUNT; + break; + default: + return D_INVALID_OPERATION; + } + return D_SUCCESS; +} + + +/* + * mouseread - dequeue and return any queued events. + */ +int +mouseread( + dev_t dev, + io_req_t ior) +{ + int err, count; + spl_t s; + + /* Check if IO_COUNT is a multiple of the record size. */ + if (ior->io_count % sizeof(kd_event) != 0) + return D_INVALID_SIZE; + + err = device_read_alloc(ior, (vm_size_t)ior->io_count); + if (err != KERN_SUCCESS) + return (err); + + s = SPLKD(); + if (kdq_empty(&mouse_queue)) { + if (ior->io_mode & D_NOWAIT) { + splx(s); + return (D_WOULD_BLOCK); + } + ior->io_done = mouse_read_done; + enqueue_tail(&mouse_read_queue, (queue_entry_t)ior); + splx(s); + return (D_IO_QUEUED); + } + count = 0; + while (!kdq_empty(&mouse_queue) && count < ior->io_count) { + kd_event *ev; + + ev = kdq_get(&mouse_queue); + *(kd_event *)(&ior->io_data[count]) = *ev; + count += sizeof(kd_event); + } + splx(s); + ior->io_residual = ior->io_count - count; + return (D_SUCCESS); +} + +boolean_t mouse_read_done(io_req_t ior) +{ + int count; + spl_t s; + + s = SPLKD(); + if (kdq_empty(&mouse_queue)) { + ior->io_done = mouse_read_done; + enqueue_tail(&mouse_read_queue, (queue_entry_t)ior); + splx(s); + return (FALSE); + } + + count = 0; + while (!kdq_empty(&mouse_queue) && count < ior->io_count) { + kd_event *ev; + + ev = kdq_get(&mouse_queue); + *(kd_event *)(&ior->io_data[count]) = *ev; + count += sizeof(kd_event); + } + splx(s); + + ior->io_residual = ior->io_count - count; + ds_read_done(ior); + + return (TRUE); +} + + + +/* + * mouseintr - Get a byte and pass it up for handling. Called at SPLKD. + */ +void +mouseintr(int unit) +{ + unsigned short base_addr = cominfo[unit]->address; + unsigned char id, ls; + + /* get reason for interrupt and line status */ + id = inb(base_addr + RID); + ls = inb(base_addr + RLS); + + /* handle status changes */ + if (id == IDLS) { + if (ls & LSDR) { + inb(base_addr + RDAT); /* flush bad character */ + } + return; /* ignore status change */ + } + + if (id & IDRD) { + mouse_handle_byte((u_char)(inb(base_addr + RDAT) & 0xff)); + } +} + + +/* + * handle_byte - Accumulate bytes until we have an entire packet. + * If the mouse has moved or any of the buttons have changed state (up + * or down), enqueue the corresponding events. + * Called at SPLKD. + * XXX - magic numbers. + */ +int show_mouse_byte = 0; +/* + X down; middle down; middle up; X up 50 0 0; 50 0 0 22; 50 0 0 02; 40 0 0 + X down; middle down; X up; middle up 50 0 0; 50 0 0 22; 40 0 0 22; 40 0 0 2 + * + * The trick here is that all the while the middle button is down you get 4 byte + * packets with the last byte 0x22. When the middle button goes up you get a + * last packet with 0x02. + */ +int lastgitech = 0x40; /* figure whether the first 3 bytes imply */ + /* its time to expect a fourth */ +int fourthgitech = 0; /* look for the 4th byte; we must process it */ +int middlegitech = 0; /* what should the middle button be */ + +static u_char mousebuf[MOUSEBUFSIZE]; /* 5-byte packet from mouse */ + +void +mouse_handle_byte(u_char ch) +{ + if (show_mouse_byte) { + printf("%x(%c) ", ch, ch); + } + + if (mouse_char_cmd) { + /* + * Mouse character is response to command + */ + if (mousebufindex < mousebufsize) + mousebuf[mousebufindex++] = ch; + if (mouse_char_wanted) { + mouse_char_wanted = FALSE; + wakeup((vm_offset_t)&mousebuf); + } + return; + } + + if (mousebufindex == 0) { + switch (mouse_type) { + case MICROSOFT_MOUSE7: + if ((ch & 0x40) != 0x40) + return; + break; + case MICROSOFT_MOUSE: + if ((ch & 0xc0) != 0xc0) + return; + break; + case MOUSE_SYSTEM_MOUSE: + if ((ch & 0xf8) != 0x80) + return; + break; + case LOGITECH_TRACKMAN: + if (fourthgitech == 1) { + fourthgitech = 0; + if (ch & 0xf0) + middlegitech = 0x4; + else + middlegitech = 0x0; + mouse_packet_microsoft_mouse(mousebuf); + return; + } else if ((ch & 0xc0) != 0x40) + return; + break; + case IBM_MOUSE: + break; + } + } + + mousebuf[mousebufindex++] = ch; + if (mousebufindex < mousebufsize) + return; + + /* got a packet */ + mousebufindex = 0; + + switch (mouse_type) { + case MICROSOFT_MOUSE7: + case MICROSOFT_MOUSE: + mouse_packet_microsoft_mouse(mousebuf); + break; + case MOUSE_SYSTEM_MOUSE: + mouse_packet_mouse_system_mouse(mousebuf); + break; + case LOGITECH_TRACKMAN: + if ( mousebuf[1] || mousebuf[2] || + mousebuf[0] != lastgitech) { + mouse_packet_microsoft_mouse(mousebuf); + lastgitech = mousebuf[0] & 0xf0; + } else { + fourthgitech = 1; + } + break; + case IBM_MOUSE: + mouse_packet_ibm_ps2_mouse(mousebuf); + break; + } +} + +void +mouse_packet_mouse_system_mouse(u_char mousebuf[MOUSEBUFSIZE]) +{ + u_char buttons, buttonchanges; + struct mouse_motion moved; + + buttons = mousebuf[0] & 0x7; /* get current state of buttons */ + buttonchanges = buttons ^ lastbuttons; + moved.mm_deltaX = (char)mousebuf[1] + (char)mousebuf[3]; + moved.mm_deltaY = (char)mousebuf[2] + (char)mousebuf[4]; + + if (moved.mm_deltaX != 0 || moved.mm_deltaY != 0) + mouse_moved(moved); + + if (buttonchanges != 0) { + lastbuttons = buttons; + if (buttonchanges & 1) + mouse_button(MOUSE_RIGHT, buttons & 1); + if (buttonchanges & 2) + mouse_button(MOUSE_MIDDLE, (buttons & 2) >> 1); + if (buttonchanges & 4) + mouse_button(MOUSE_LEFT, (buttons & 4) >> 2); + } +} + +/* same as above for microsoft mouse */ +/* + * 3 byte microsoft format used + * + * 7 6 5 4 3 2 1 0 + * 1 1 L R Y7 Y6 X7 X6 + * 1 0 X5 X4 X3 X3 X1 X0 + * 1 0 Y5 Y4 Y3 Y2 Y1 Y0 + * + */ +void +mouse_packet_microsoft_mouse(u_char mousebuf[MOUSEBUFSIZE]) +{ + u_char buttons, buttonchanges; + struct mouse_motion moved; + + buttons = ((mousebuf[0] & 0x30) >> 4); + buttons |= middlegitech; + /* get current state of buttons */ +#ifdef gross_hack + if (buttons == 0x03) /* both buttons down */ + buttons = 0x04; +#endif /* gross_hack */ + buttons = (~buttons) & 0x07; /* convert to not pressed */ + + buttonchanges = buttons ^ lastbuttons; + moved.mm_deltaX = ((mousebuf[0] & 0x03) << 6) | (mousebuf[1] & 0x3F); + moved.mm_deltaY = ((mousebuf[0] & 0x0c) << 4) | (mousebuf[2] & 0x3F); + if (moved.mm_deltaX & 0x80) /* negative, in fact */ + moved.mm_deltaX = moved.mm_deltaX - 0x100; + if (moved.mm_deltaY & 0x80) /* negative, in fact */ + moved.mm_deltaY = moved.mm_deltaY - 0x100; + /* and finally the Y orientation is different for the microsoft mouse */ + moved.mm_deltaY = -moved.mm_deltaY; + + if (moved.mm_deltaX != 0 || moved.mm_deltaY != 0) + mouse_moved(moved); + + if (buttonchanges != 0) { + lastbuttons = buttons; + if (buttonchanges & 1) + mouse_button(MOUSE_RIGHT, (buttons & 1) ? + MOUSE_UP : MOUSE_DOWN); + if (buttonchanges & 2) + mouse_button(MOUSE_LEFT, (buttons & 2) ? + MOUSE_UP : MOUSE_DOWN); + if (buttonchanges & 4) + mouse_button(MOUSE_MIDDLE, (buttons & 4) ? + MOUSE_UP : MOUSE_DOWN); + } +} + +/* + * AUX device (PS2) open/close + */ + +/* + * Write character to mouse. Called at spltty. + */ +static void kd_mouse_write( + unsigned char ch) +{ + while (inb(K_STATUS) & K_IBUF_FUL) + continue; /* wait for 'input' port empty */ + outb(K_CMD, 0xd4); /* send next character to mouse */ + + while (inb(K_STATUS) & K_IBUF_FUL) + continue; /* wait for 'input' port empty */ + outb(K_RDWR, ch); /* send command to mouse */ +} + +/* + * Read next character from mouse, waiting for interrupt + * to deliver it. Called at spltty. + */ +static int kd_mouse_read(void) +{ + int ch; + + if (mouse_char_index >= mousebufsize) + return -1; + + while (mousebufindex <= mouse_char_index) { + mouse_char_wanted = TRUE; + assert_wait((event_t) &mousebuf, FALSE); + /* We are at tty SPL level, interrupts can not happen between + * assert_wait and thread_block. */ + thread_block((void (*)()) 0); + } + + ch = mousebuf[mouse_char_index++]; + + return ch; +} + +/* + * Prepare buffer for receiving next packet from mouse. + */ +static void kd_mouse_read_reset(void) +{ + mousebufindex = 0; + mouse_char_index = 0; +} + +void +ibm_ps2_mouse_open(dev_t dev) +{ + spl_t s = spltty(); + + lastbuttons = 0; + mouse_char_cmd = TRUE; /* responses are to commands */ + + kd_sendcmd(0xa8); /* enable mouse in kbd */ + + kd_cmdreg_write(0x47); /* allow mouse interrupts */ + /* magic number for ibm? */ + + kd_mouse_read_reset(); + kd_mouse_write(0xff); /* reset mouse */ + if (kd_mouse_read() != 0xfa) { + splx(s); + return; /* need ACK */ + } + + (void) kd_mouse_read(); /* discard 2-character mouse ID */ + (void) kd_mouse_read(); + + kd_mouse_read_reset(); + kd_mouse_write(0xea); /* set stream mode */ + if (kd_mouse_read() != 0xfa) { + splx(s); + return; /* need ACK */ + } + + kd_mouse_read_reset(); + kd_mouse_write(0xf4); /* enable */ + if (kd_mouse_read() != 0xfa) { + splx(s); + return; /* need ACK */ + } + + kd_mouse_read_reset(); + mouse_char_cmd = FALSE; /* now we get mouse packets */ + + splx(s); +} + +void +ibm_ps2_mouse_close(dev_t dev) +{ + spl_t s = spltty(); + + mouse_char_cmd = TRUE; /* responses are to commands */ + + kd_mouse_read_reset(); + kd_mouse_write(0xff); /* reset mouse */ + if (kd_mouse_read() == 0xfa) { + /* got ACK: discard 2-char mouse ID */ + (void) kd_mouse_read(); + (void) kd_mouse_read(); + } + + kd_sendcmd(0xa7); /* disable mouse in kbd */ + kd_cmdreg_write(0x65); /* disallow mouse interrupts */ + /* magic number for ibm? */ + + splx(s); +} + +/* + * 3 byte ibm ps2 format used + * + * 7 6 5 4 3 2 1 0 + * YO XO YS XS 1 M R L + * X7 X6 X5 X4 X3 X3 X1 X0 + * Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 + * + */ +void +mouse_packet_ibm_ps2_mouse(u_char mousebuf[MOUSEBUFSIZE]) +{ + u_char buttons, buttonchanges; + struct mouse_motion moved; + + buttons = mousebuf[0] & 0x7; /* get current state of buttons */ + buttonchanges = buttons ^ lastbuttons; + moved.mm_deltaX = ((mousebuf[0]&0x10) ? 0xffffff00 : 0 ) | (u_char)mousebuf[1]; + moved.mm_deltaY = ((mousebuf[0]&0x20) ? 0xffffff00 : 0 ) | (u_char)mousebuf[2]; + if (mouse_packets) { + printf("(%x:%x:%x)", mousebuf[0], mousebuf[1], mousebuf[2]); + return; + } + + if (moved.mm_deltaX != 0 || moved.mm_deltaY != 0) + mouse_moved(moved); + + if (buttonchanges != 0) { + lastbuttons = buttons; + if (buttonchanges & 1) + mouse_button(MOUSE_LEFT, !(buttons & 1)); + if (buttonchanges & 2) + mouse_button(MOUSE_RIGHT, !((buttons & 2) >> 1)); + if (buttonchanges & 4) + mouse_button(MOUSE_MIDDLE, !((buttons & 4) >> 2)); + } +} + +/* + * Enqueue a mouse-motion event. Called at SPLKD. + */ +void +mouse_moved(struct mouse_motion where) +{ + kd_event ev; + + ev.type = MOUSE_MOTION; + /* Not used but we set it to avoid garbage */ + ev.unused_time.seconds = 0; + ev.unused_time.microseconds = 0; + ev.value.mmotion = where; + mouse_enqueue(&ev); +} + +/* + * Enqueue an event for mouse button press or release. Called at SPLKD. + */ +void +mouse_button( + kev_type which, + u_char direction) +{ + kd_event ev; + + ev.type = which; + ev.value.up = (direction == MOUSE_UP) ? TRUE : FALSE; + /* Not used but we set it to avoid garbage */ + ev.unused_time.seconds = 0; + ev.unused_time.microseconds = 0; + mouse_enqueue(&ev); +} + +/* + * mouse_enqueue - enqueue an event and wake up selecting processes, if + * any. Called at SPLKD. + */ + +void +mouse_enqueue(kd_event *ev) +{ + if (kdq_full(&mouse_queue)) + printf_once("mouse: queue full\n"); + else + kdq_put(&mouse_queue, ev); + + { + io_req_t ior; + while ((ior = (io_req_t)dequeue_head(&mouse_read_queue)) != 0) + iodone(ior); + } +} diff --git a/i386/i386at/kd_mouse.h b/i386/i386at/kd_mouse.h new file mode 100644 index 0000000..a9fb128 --- /dev/null +++ b/i386/i386at/kd_mouse.h @@ -0,0 +1,72 @@ +/* + * Mouse event handlers + * Copyright (C) 2006 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Barry deFreese. + */ +/* + * Mouse event handling functions. + * + */ + +#ifndef _KD_MOUSE_H_ +#define _KD_MOUSE_H_ + +#include + +#define MOUSEBUFSIZE 5 /* num bytes def'd by protocol */ + +extern void mouse_button (kev_type which, u_char direction); + +extern void mouse_enqueue (kd_event *ev); + +extern void mouse_moved (struct mouse_motion where); + +extern void mouse_handle_byte (u_char ch); + +extern void serial_mouse_open (dev_t dev); + +extern void serial_mouse_close (dev_t dev, int flags); + +extern void kd_mouse_open (dev_t dev, int mouse_pic); + +extern void kd_mouse_close (dev_t dev, int mouse_pic); + +extern void ibm_ps2_mouse_open (dev_t dev); + +extern void ibm_ps2_mouse_close (dev_t dev); + +extern void mouse_packet_microsoft_mouse (u_char mousebuf[MOUSEBUFSIZE]); + +extern void mouse_packet_mouse_system_mouse (u_char mousebuf[MOUSEBUFSIZE]); + +extern void mouse_packet_ibm_ps2_mouse (u_char mousebuf[MOUSEBUFSIZE]); + +extern int mouseopen(dev_t dev, int flags, io_req_t ior); +extern void mouseclose(dev_t dev, int flags); +extern int mouseread(dev_t dev, io_req_t ior); + +extern io_return_t mousegetstat( + dev_t dev, + dev_flavor_t flavor, + dev_status_t data, + mach_msg_type_number_t *count); + +void mouseintr(int unit); +boolean_t mouse_read_done(io_req_t ior); + +#endif /* _KD_MOUSE_H_ */ diff --git a/i386/i386at/kd_queue.c b/i386/i386at/kd_queue.c new file mode 100644 index 0000000..ab399cd --- /dev/null +++ b/i386/i386at/kd_queue.c @@ -0,0 +1,109 @@ +/* + * 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. + */ +/* ********************************************************************** + File: kd_queue.c + Description: Event queue code for keyboard/display (and mouse) driver. + + $ Header: $ + + Copyright Ing. C. Olivetti & C. S.p.A. 1989. + All rights reserved. +********************************************************************** */ +/* + Copyright 1988, 1989 by Olivetti Advanced Technology Center, Inc., +Cupertino, 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 Olivetti +not be used in advertising or publicity pertaining to distribution +of the software without specific, written prior permission. + + OLIVETTI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, +IN NO EVENT SHALL OLIVETTI 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 OUR OF OR IN CONNECTION +WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + + +#include + +/* + * Notice that when adding an entry to the queue, the caller provides + * its own storage, which is copied into the queue. However, when + * removing an entry from the queue, the caller is given a pointer to a + * queue element. This means that the caller must either process the + * element or copy it into its own storage before unlocking the queue. + * + * These routines should be called only at a protected SPL. + */ + +#define q_next(index) (((index)+1) % KDQSIZE) + +boolean_t +kdq_empty(const kd_event_queue *q) +{ + return(q->firstfree == q->firstout); +} + +boolean_t +kdq_full(const kd_event_queue *q) +{ + return(q_next(q->firstfree) == q->firstout); +} + +void +kdq_put(kd_event_queue *q, kd_event *ev) +{ + kd_event *qp = q->events + q->firstfree; + + qp->type = ev->type; + qp->unused_time = ev->unused_time; + qp->value = ev->value; + q->firstfree = q_next(q->firstfree); +} + +kd_event * +kdq_get(kd_event_queue *q) +{ + kd_event *result = q->events + q->firstout; + + q->firstout = q_next(q->firstout); + return(result); +} + +void +kdq_reset(kd_event_queue *q) +{ + q->firstout = q->firstfree = 0; +} diff --git a/i386/i386at/kd_queue.h b/i386/i386at/kd_queue.h new file mode 100644 index 0000000..702efe8 --- /dev/null +++ b/i386/i386at/kd_queue.h @@ -0,0 +1,86 @@ +/* + * 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. + */ +/* ********************************************************************** + File: kd_queue.h + Description: definitions for keybd/display Event queue + + $ Header: $ + + Copyright Ing. C. Olivetti & C. S.p.A. 1989. + All rights reserved. +********************************************************************** */ +/* + Copyright 1988, 1989 by Olivetti Advanced Technology Center, Inc., +Cupertino, 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 Olivetti +not be used in advertising or publicity pertaining to distribution +of the software without specific, written prior permission. + + OLIVETTI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, +IN NO EVENT SHALL OLIVETTI 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 OUR OF OR IN CONNECTION +WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/* + * Definitions for keyboard/mouse events. + * + * The keyboard and mouse can be read as a stream of events. The event + * definition is the same in both cases, but only keyboard events will + * be generated by /dev/kbd, and only mouse events will be generated by + * /dev/mouse. + */ + +#ifndef _KD_QUEUE_H_ +#define _KD_QUEUE_H_ + +#include +#include + +#define KDQSIZE 100 /* is this a good size? */ + +typedef struct { + kd_event events[KDQSIZE]; + int firstfree, firstout; +} kd_event_queue; + +extern void kdq_put(kd_event_queue *, kd_event *); +extern void kdq_reset(kd_event_queue *); +extern boolean_t kdq_empty(const kd_event_queue *); +extern boolean_t kdq_full(const kd_event_queue *); +extern kd_event *kdq_get(kd_event_queue *); + +#endif /* _KD_QUEUE_H_ */ diff --git a/i386/i386at/kdasm.S b/i386/i386at/kdasm.S new file mode 100644 index 0000000..fd0e1c8 --- /dev/null +++ b/i386/i386at/kdasm.S @@ -0,0 +1,145 @@ +/* + * 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. + */ +/* + * Some inline code to speed up major block copies to and from the + * screen buffer. + * + * Copyright Ing. C. Olivetti & C. S.p.A. 1988, 1989. + * All rights reserved. + * + * orc!eugene 28 Oct 1988 + * + */ +/* + Copyright 1988, 1989 by Olivetti Advanced Technology Center, Inc., +Cupertino, 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 Olivetti +not be used in advertising or publicity pertaining to distribution +of the software without specific, written prior permission. + + OLIVETTI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, +IN NO EVENT SHALL OLIVETTI 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 OUR OF OR IN CONNECTION +WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/* $ Header: $ */ + + +#include + +/* + * Function: kd_slmwd() + * + * This function "slams" a word (char/attr) into the screen memory using + * a block fill operation on the 386. + * + */ + +#define start B_ARG0 +#define count B_ARG1 +#define value B_ARG2 + +ENTRY(kd_slmwd) + pushl %ebp + movl %esp, %ebp + pushl %edi + + movl start, %edi + movl count, %ecx + movw value, %ax + cld + rep + stosw + + popl %edi + leave + ret +#undef start +#undef count +#undef value + +/* + * "slam up" + */ + +#define from B_ARG0 +#define to B_ARG1 +#define count B_ARG2 +ENTRY(kd_slmscu) + pushl %ebp + movl %esp, %ebp + pushl %esi + pushl %edi + + movl from, %esi + movl to, %edi + movl count, %ecx + cmpl %edi, %esi + cld + rep + movsw + + popl %edi + popl %esi + leave + ret + +/* + * "slam down" + */ +ENTRY(kd_slmscd) + pushl %ebp + movl %esp, %ebp + pushl %esi + pushl %edi + + movl from, %esi + movl to, %edi + movl count, %ecx + cmpl %edi, %esi + std + rep + movsw + cld + + popl %edi + popl %esi + leave + ret +#undef from +#undef to +#undef count diff --git a/i386/i386at/kdsoft.h b/i386/i386at/kdsoft.h new file mode 100644 index 0000000..79bfdb0 --- /dev/null +++ b/i386/i386at/kdsoft.h @@ -0,0 +1,209 @@ +/* + * 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. + */ +/* ********************************************************************** + File: kdsoft.h + Description: Software structures for keyboard/display driver, shared with + drivers for specific graphics cards. + + $ Header: $ + + Copyright Ing. C. Olivetti & C. S.p.A. 1988, 1989. + All rights reserved. +********************************************************************** */ + +/* + Copyright 1988, 1989 by Olivetti Advanced Technology Center, Inc., +Cupertino, 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 Olivetti +not be used in advertising or publicity pertaining to distribution +of the software without specific, written prior permission. + + OLIVETTI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, +IN NO EVENT SHALL OLIVETTI 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 OUR OF OR IN CONNECTION +WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#ifndef _KDSOFT_H_ +#define _KDSOFT_H_ + +/* + * Globals used for both character-based controllers and bitmap-based + * controllers. + */ +typedef short csrpos_t; /* cursor position, ONE_SPACE bytes per char */ + +extern u_char *vid_start; /* VM start of video RAM or frame buffer */ +extern csrpos_t kd_curpos; /* should be set only by kd_setpos */ +extern short kd_lines; /* num lines in tty display */ +extern short kd_cols; +extern char kd_attr; /* current character attribute */ + + +/* + * Globals used only for bitmap-based controllers. + * XXX - probably needs reworking for color. + */ + +/* + * This driver handles two types of graphics cards. The first type + * (e.g., EGA, CGA), treats the screen as a page of characters and + * has a hardware cursor. The second type (e.g., the Blit) treats the + * screen as a bitmap. A hardware cursor may be present, but it is + * ignored in favor of a software cursor. + * + * + * Most of the driver uses the following abstraction for the display: + * + * The cursor position is simply an index into a (logical) linear char + * array that wraps around at the end of each line. Each character + * takes up ONE_SPACE bytes. Values in [0..ONE_PAGE) are positions in + * the displayed page. Values < 0 and >= ONE_PAGE are off the page + * and require some scrolling to put the cursor back on the page. + * + * The kd_dxxx routines handle the conversion from this abstraction to + * what the hardware requires. + * + * (*kd_dput)(pos, ch, chattr) + * csrpos_t pos; + * char ch, chattr; + * Displays a character at "pos", where "ch" = the character to + * be displayed and "chattr" is its attribute byte. + * + * (*kd_dmvup)(from, to, count) + * csrpos_t from, to; + * int count; + * Does a (relatively) fast block transfer of characters upward. + * "count" is the number of character positions (not bytes) to move. + * "from" is the character position to start moving from (at the start + * of the block to be moved). "to" is the character position to start + * moving to. + * + * (*kd_dmvdown)(from, to, count) + * csrpos_t from, to; + * int count; + * "count" is the number of character positions (not bytes) to move. + * "from" is the character position to start moving from (at the end + * of the block to be moved). "to" is the character position to + * start moving to. + * + * (*kd_dclear)(to, count, chattr) + * csrpos_t, to; + * int count; + * char chattr; + * Erases "count" character positions, starting with "to". + * + * (*kd_dsetcursor)(pos) + * Sets kd_curpos and moves the displayed cursor to track it. "pos" + * should be in the range [0..ONE_PAGE). + * + * (*kd_dreset)() + * In some cases, the boot program expects the display to be in a + * particular state, and doing a soft reset (i.e., + * software-controlled reboot) doesn't put it into that state. For + * these cases, the machine-specific driver should provide a "reset" + * procedure, which will be called just before the kd code causes the + * system to reboot. + */ + +extern void bmpput(csrpos_t, char, char); +extern void bmpmvup(csrpos_t, csrpos_t, int); +extern void bmpmvdown(csrpos_t, csrpos_t, int); +extern void bmpclear(csrpos_t, int, char); +extern void bmpsetcursor(csrpos_t); + +extern void (*kd_dput)(csrpos_t, char, char); /* put attributed char */ +extern void (*kd_dmvup)(csrpos_t, csrpos_t, int); /* block move up */ +extern void (*kd_dmvdown)(csrpos_t, csrpos_t, int); /* block move down */ +extern void (*kd_dclear)(csrpos_t, int, char); /* block clear */ +extern void (*kd_dsetcursor)(csrpos_t); /* set cursor position on displayed page */ +extern void (*kd_dreset)(void); /* prepare for reboot */ + + +/* + * The following font layout is assumed: + * + * The top scan line of all the characters comes first. Then the + * second scan line, then the third, etc. + * + * ------ ... ---------|-----N--------|-------------- ... ----------- + * ------ ... ---------|-----N--------|-------------- ... ----------- + * . + * . + * . + * ------ ... ---------|-----N--------|-------------- ... ----------- + * + * In the picture, each line is a scan line from the font. Each scan + * line is stored in memory immediately after the previous one. The + * bits between the vertical lines are the bits for a single character + * (e.g., the letter "N"). + * There are "char_height" scan lines. Each character is "char_width" + * bits wide. We make the simplifying assumption that characters are + * on byte boundaries. (We also assume that a byte is 8 bits.) + */ + +extern u_char *font_start; /* starting addr of font */ + +extern short fb_width; /* bits in frame buffer scan line */ +extern short fb_height; /* scan lines in frame buffer*/ +extern short char_width; /* bit width of 1 char */ +extern short char_height; /* bit height of 1 char */ +extern short chars_in_font; +extern short cursor_height; /* bit height of cursor */ + /* char_height + cursor_height = line_height */ + +extern u_char char_black; /* 8 black (off) bits */ +extern u_char char_white; /* 8 white (on) bits */ + + +/* + * The tty emulation does not usually require the entire frame buffer. + * (xstart, ystart) is the bit address for the upper left corner of the + * tty "screen". + */ + +extern short xstart, ystart; + + +/* + * Accelerators for bitmap displays. + */ + +extern short char_byte_width; /* char_width/8 */ +extern short fb_byte_width; /* fb_width/8 */ +extern short font_byte_width; /* num bytes in 1 scan line of font */ + +#endif /* _KDSOFT_H_ */ diff --git a/i386/i386at/lpr.c b/i386/i386at/lpr.c new file mode 100644 index 0000000..f8d42f3 --- /dev/null +++ b/i386/i386at/lpr.c @@ -0,0 +1,285 @@ +/* + * Mach Operating System + * Copyright (c) 1993-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. + */ +/* + * Parallel port printer driver v1.0 + * All rights reserved. + */ + +#if NLPR > 0 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* + * Driver information for auto-configuration stuff. + */ + +struct bus_device *lprinfo[NLPR]; /* ??? */ + +static vm_offset_t lpr_std[NLPR] = { 0 }; +static struct bus_device *lpr_info[NLPR]; +struct bus_driver lprdriver = { + lprprobe, 0, lprattach, 0, lpr_std, "lpr", lpr_info, 0, 0, 0}; + +struct tty lpr_tty[NLPR]; + +int lpr_alive[NLPR]; + +int +lprprobe(vm_offset_t port, struct bus_ctlr *dev) +{ + u_short addr = (u_short) dev->address; + int unit = dev->unit; + int ret; + + if ((unit < 0) || (unit >= NLPR)) { + printf("com %d out of range\n", unit); + return(0); + } + + outb(INTR_ENAB(addr),0x07); + outb(DATA(addr),0xaa); + ret = inb(DATA(addr)) == 0xaa; + if (ret) { + if (lpr_alive[unit]) { + printf("lpr: Multiple alive entries for unit %d.\n", unit); + printf("lpr: Ignoring entry with address = %x .\n", addr); + ret = 0; + } else + lpr_alive[unit]++; + } + return(ret); +} + +void lprattach(struct bus_device *dev) +{ + u_char unit = dev->unit; + u_short addr = (u_short) dev->address; + + if (unit >= NLPR) { + printf(", disabled by NLPR configuration\n"); + return; + } + + take_dev_irq(dev); + printf(", port = %zx, spl = %zd, pic = %d.", + dev->address, dev->sysdep, dev->sysdep1); + lprinfo[unit] = dev; + + outb(INTR_ENAB(addr), inb(INTR_ENAB(addr)) & 0x0f); + + return; +} + +int +lpropen(dev_t dev, int flag, io_req_t ior) +{ + int unit = minor(dev); + struct bus_device *isai; + struct tty *tp; + u_short addr; + + if (unit >= NLPR) + return D_NO_SUCH_DEVICE; + + isai = lprinfo[unit]; + if (isai == NULL || !isai->alive) + return D_NO_SUCH_DEVICE; + + tp = &lpr_tty[unit]; + addr = (u_short) isai->address; + tp->t_dev = dev; + tp->t_addr = (void*) (natural_t) addr; + tp->t_oproc = lprstart; + tp->t_state |= TS_WOPEN; + tp->t_stop = lprstop; + tp->t_getstat = lprgetstat; + tp->t_setstat = lprsetstat; + if ((tp->t_state & TS_ISOPEN) == 0) + ttychars(tp); + outb(INTR_ENAB(addr), inb(INTR_ENAB(addr)) | 0x10); + tp->t_state |= TS_CARR_ON; + return (char_open(dev, tp, flag, ior)); +} + +void +lprclose(dev_t dev, int flag) +{ +int unit = minor(dev); +struct tty *tp = &lpr_tty[unit]; +u_short addr = (u_short) lprinfo[unit]->address; + + ttyclose(tp); + if (tp->t_state&TS_HUPCLS || (tp->t_state&TS_ISOPEN)==0) { + outb(INTR_ENAB(addr), inb(INTR_ENAB(addr)) & 0x0f); + tp->t_state &= ~TS_BUSY; + } +} + +int +lprread(dev_t dev, io_req_t ior) +{ + return char_read(&lpr_tty[minor(dev)], ior); +} + +int +lprwrite(dev_t dev, io_req_t ior) +{ + return char_write(&lpr_tty[minor(dev)], ior); +} + +int +lprportdeath(dev_t dev, mach_port_t port) +{ + return (tty_portdeath(&lpr_tty[minor(dev)], (ipc_port_t)port)); +} + +io_return_t +lprgetstat( + dev_t dev, + dev_flavor_t flavor, + dev_status_t data, /* pointer to OUT array */ + mach_msg_type_number_t *count /* out */ + ) +{ + io_return_t result = D_SUCCESS; + int unit = minor(dev); + + switch (flavor) { + default: + result = tty_get_status(&lpr_tty[unit], flavor, data, count); + break; + } + return (result); +} + +io_return_t +lprsetstat( + dev_t dev, + dev_flavor_t flavor, + dev_status_t data, + mach_msg_type_number_t count) +{ + io_return_t result = D_SUCCESS; + int unit = minor(dev); + + switch (flavor) { + default: + result = tty_set_status(&lpr_tty[unit], flavor, data, count); +/* if (result == D_SUCCESS && flavor == TTY_STATUS) + lprparam(unit); +*/ return (result); + } + return (D_SUCCESS); +} + +void lprintr(int unit) +{ + struct tty *tp = &lpr_tty[unit]; + + if ((tp->t_state & TS_ISOPEN) == 0) + return; + + tp->t_state &= ~TS_BUSY; + if (tp->t_state&TS_FLUSH) + tp->t_state &=~TS_FLUSH; + tt_write_wakeup(tp); + lprstart(tp); +} + +void lprstart(struct tty *tp) +{ + spl_t s = spltty(); + u_short addr = (natural_t) tp->t_addr; + int status = inb(STATUS(addr)); + int nch; + + if (tp->t_state & (TS_TIMEOUT|TS_TTSTOP|TS_BUSY)) { + splx(s); + return; + } + + if (status & 0x20) { + printf("Printer out of paper!\n"); + splx(s); + return; + } + + if (tp->t_outq.c_cc <= TTLOWAT(tp)) { + tt_write_wakeup(tp); + } + if (tp->t_outq.c_cc == 0) { + splx(s); + return; + } + nch = getc(&tp->t_outq); + if (nch == -1) { + splx(s); + return; + } + if ((tp->t_flags & LITOUT) == 0 && (nch & 0200)) { + timeout((timer_func_t *)ttrstrt, (char *)tp, (nch & 0x7f) + 6); + tp->t_state |= TS_TIMEOUT; + return; + } + outb(DATA(addr), nch); + outb(INTR_ENAB(addr),inb(INTR_ENAB(addr)) | 0x01); + outb(INTR_ENAB(addr),inb(INTR_ENAB(addr)) & 0x1e); + tp->t_state |= TS_BUSY; + splx(s); + return; +} + +void +lprstop( + struct tty *tp, + int flags) +{ + if ((tp->t_state & TS_BUSY) && (tp->t_state & TS_TTSTOP) == 0) + tp->t_state |= TS_FLUSH; +} + +void +lprpr_addr(unsigned short addr) +{ + printf("DATA(%x) %x, STATUS(%x) %x, INTR_ENAB(%x) %x\n", + DATA(addr), inb(DATA(addr)), + STATUS(addr), inb(STATUS(addr)), + INTR_ENAB(addr), inb(INTR_ENAB(addr))); +} +#endif /* NLPR */ diff --git a/i386/i386at/lpr.h b/i386/i386at/lpr.h new file mode 100644 index 0000000..cab3016 --- /dev/null +++ b/i386/i386at/lpr.h @@ -0,0 +1,66 @@ +/* + * 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. + */ +/* + * Parallel port printer driver v1.0 + * All rights reserved. + */ + +#ifndef _LPRREG_H_ +#define _LPRREG_H_ + +#define DATA(addr) (addr + 0) +#define STATUS(addr) (addr + 1) +#define INTR_ENAB(addr) (addr + 2) + +extern void lprintr(int unit); +int lprprobe(vm_offset_t port, struct bus_ctlr *dev); +void lprstop(struct tty *tp, int flags); +void lprstart(struct tty *tp); +void lprattach(struct bus_device *dev); + +extern io_return_t +lprgetstat( + dev_t dev, + dev_flavor_t flavor, + dev_status_t data, + mach_msg_type_number_t *count); + +extern io_return_t +lprsetstat( + dev_t dev, + dev_flavor_t flavor, + dev_status_t data, + mach_msg_type_number_t count); + +void lprpr_addr(unsigned short addr); + +extern int lpropen(dev_t dev, int flag, io_req_t ior); +extern void lprclose(dev_t dev, int flag); +extern int lprread(dev_t dev, io_req_t ior); +extern int lprwrite(dev_t dev, io_req_t ior); +extern int lprportdeath(dev_t dev, mach_port_t port); + +#endif /* _LPRREG_H_ */ diff --git a/i386/i386at/mem.c b/i386/i386at/mem.c new file mode 100644 index 0000000..f46fc03 --- /dev/null +++ b/i386/i386at/mem.c @@ -0,0 +1,42 @@ +/* + * 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. + */ + +#include +#include +#include +#include + +/* This provides access to any memory that is not main RAM */ + +/*ARGSUSED*/ +vm_offset_t +memmmap(dev_t dev, vm_offset_t off, vm_prot_t prot) +{ + if (biosmem_addr_available(off)) + return -1; + + return i386_btop(off); +} diff --git a/i386/i386at/mem.h b/i386/i386at/mem.h new file mode 100644 index 0000000..a5b4aef --- /dev/null +++ b/i386/i386at/mem.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2013 Free Software Foundation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _MEM_H_ +#define _MEM_H_ + +extern vm_offset_t memmmap(dev_t dev, vm_offset_t off, vm_prot_t prot); + +#endif /* _MEM_H_ */ diff --git a/i386/i386at/model_dep.c b/i386/i386at/model_dep.c new file mode 100644 index 0000000..edb5b48 --- /dev/null +++ b/i386/i386at/model_dep.c @@ -0,0 +1,674 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989, 1988 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. + */ +/* + * File: model_dep.c + * Author: Avadis Tevanian, Jr., Michael Wayne Young + * + * Copyright (C) 1986, Avadis Tevanian, Jr., Michael Wayne Young + * + * Basic initialization for I386 - ISA bus machines. + */ + +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef MACH_XEN +#include +#include +#include +#include +#endif /* MACH_XEN */ + +#if ENABLE_IMMEDIATE_CONSOLE +#include "immc.h" +#endif /* ENABLE_IMMEDIATE_CONSOLE */ + +/* Location of the kernel's symbol table. + Both of these are 0 if none is available. */ +#if MACH_KDB +#include +#include + +/* ELF section header */ +static unsigned elf_shdr_num; +static vm_size_t elf_shdr_size; +static vm_offset_t elf_shdr_addr; +static unsigned elf_shdr_shndx; + +#endif /* MACH_KDB */ + +#define RESERVED_BIOS 0x10000 + +/* A copy of the multiboot info structure passed by the boot loader. */ +#ifdef MACH_XEN +struct start_info boot_info; +#ifdef MACH_PSEUDO_PHYS +unsigned long *mfn_list; +#if VM_MIN_KERNEL_ADDRESS != LINEAR_MIN_KERNEL_ADDRESS +unsigned long *pfn_list = (void*) PFN_LIST; +#endif +#endif /* MACH_PSEUDO_PHYS */ +#if VM_MIN_KERNEL_ADDRESS != LINEAR_MIN_KERNEL_ADDRESS +unsigned long la_shift = VM_MIN_KERNEL_ADDRESS; +#endif +#else /* MACH_XEN */ +struct multiboot_raw_info boot_info; +#endif /* MACH_XEN */ + +/* Command line supplied to kernel. */ +char *kernel_cmdline = ""; + +extern char version[]; + +/* Realmode temporary GDT */ +extern struct pseudo_descriptor gdt_descr_tmp; + +/* Realmode relocated jmp */ +extern uint32_t apboot_jmp_offset; + +/* If set, reboot the system on ctrl-alt-delete. */ +boolean_t rebootflag = FALSE; /* exported to kdintr */ + +#ifdef LINUX_DEV +extern void linux_init(void); +#endif + +/* + * Find devices. The system is alive. + */ +void machine_init(void) +{ + /* + * Make more free memory. + * + * This is particularly important for the Linux drivers which + * require available DMA memory. + */ + biosmem_free_usable(); + + /* + * Set up to use floating point. + */ + init_fpu(); + +#ifdef MACH_HYP + hyp_init(); +#else /* MACH_HYP */ +#if defined(APIC) + int err; + + err = acpi_apic_init(); + if (err) { + printf("acpi_apic_init failed with %d\n", err); + for (;;); + } +#endif +#if (NCPUS > 1) + smp_init(); +#endif +#if defined(APIC) + ioapic_configure(); +#endif + clkstart(); + + /* + * Initialize the console. + */ + cninit(); + +#ifdef LINUX_DEV + /* + * Initialize Linux drivers. + */ + linux_init(); +#endif + /* + * Find the devices + */ + probeio(); +#endif /* MACH_HYP */ + + /* + * Get the time + */ + inittodr(); + +#ifndef MACH_HYP + /* + * Tell the BIOS not to clear and test memory. + */ + *(unsigned short *)phystokv(0x472) = 0x1234; +#endif /* MACH_HYP */ + +#if VM_MIN_KERNEL_ADDRESS == 0 + /* + * Unmap page 0 to trap NULL references. + * + * Note that this breaks accessing some BIOS areas stored there. + */ + pmap_unmap_page_zero(); +#endif + +#if NCPUS > 1 + /* + * Patch the realmode gdt with the correct offset and the first jmp to + * protected mode with the correct target. + */ + gdt_descr_tmp.linear_base += apboot_addr; + apboot_jmp_offset += apboot_addr; + + /* + * Initialize the HPET + */ + hpet_init(); +#endif +} + +/* Conserve power on processor CPU. */ +void machine_idle (int cpu) +{ +#ifdef MACH_HYP + hyp_idle(); +#else /* MACH_HYP */ + assert (cpu == cpu_number ()); + asm volatile ("hlt" : : : "memory"); +#endif /* MACH_HYP */ +} + +void machine_relax (void) +{ + asm volatile ("rep; nop" : : : "memory"); +} + +/* + * Halt a cpu. + */ +void halt_cpu(void) +{ +#ifdef MACH_HYP + hyp_halt(); +#else /* MACH_HYP */ + asm volatile("cli"); + while (TRUE) + machine_idle (cpu_number ()); +#endif /* MACH_HYP */ +} + +/* + * Halt the system or reboot. + */ +void halt_all_cpus(boolean_t reboot) +{ + if (reboot) { +#ifdef MACH_HYP + hyp_reboot(); +#endif /* MACH_HYP */ + kdreboot(); + } + else { + rebootflag = TRUE; +#ifdef MACH_HYP + hyp_halt(); +#endif /* MACH_HYP */ + printf("Shutdown completed successfully, now in tight loop.\n"); + printf("You can safely power off the system or hit ctl-alt-del to reboot\n"); + (void) spl0(); + } + while (TRUE) + machine_idle (cpu_number ()); +} + +void db_halt_cpu(void) +{ + halt_all_cpus(0); +} + +void db_reset_cpu(void) +{ + halt_all_cpus(1); +} + +#ifndef MACH_HYP + +static void +register_boot_data(const struct multiboot_raw_info *mbi) +{ + struct multiboot_raw_module *mod; + struct elf_shdr *shdr; + unsigned long tmp; + unsigned int i; + + extern char _start[], _end[]; + + biosmem_register_boot_data(_kvtophys(&_start), _kvtophys(&_end), FALSE); + + /* cmdline and modules are moved to a safe place by i386at_init. */ + + if ((mbi->flags & MULTIBOOT_LOADER_CMDLINE) && (mbi->cmdline != 0)) { + biosmem_register_boot_data(mbi->cmdline, + mbi->cmdline + + strlen((void *)phystokv(mbi->cmdline)) + 1, TRUE); + } + + if (mbi->flags & MULTIBOOT_LOADER_MODULES && mbi->mods_count) { + i = mbi->mods_count * sizeof(struct multiboot_raw_module); + biosmem_register_boot_data(mbi->mods_addr, mbi->mods_addr + i, TRUE); + + tmp = phystokv(mbi->mods_addr); + + for (i = 0; i < mbi->mods_count; i++) { + mod = (struct multiboot_raw_module *)tmp + i; + if (mod->mod_end != mod->mod_start) + biosmem_register_boot_data(mod->mod_start, mod->mod_end, TRUE); + + if (mod->string != 0) { + biosmem_register_boot_data(mod->string, + mod->string + + strlen((void *)phystokv(mod->string)) + 1, + TRUE); + } + } + } + + if (mbi->flags & MULTIBOOT_LOADER_SHDR) { + tmp = mbi->shdr_num * mbi->shdr_size; + if (tmp != 0) + biosmem_register_boot_data(mbi->shdr_addr, mbi->shdr_addr + tmp, FALSE); + + tmp = phystokv(mbi->shdr_addr); + + for (i = 0; i < mbi->shdr_num; i++) { + shdr = (struct elf_shdr *)(tmp + (i * mbi->shdr_size)); + + if ((shdr->type != ELF_SHT_SYMTAB) + && (shdr->type != ELF_SHT_STRTAB)) + continue; + + if (shdr->size != 0) + biosmem_register_boot_data(shdr->addr, shdr->addr + shdr->size, FALSE); + } + } +} + +#endif /* MACH_HYP */ + +/* + * Basic PC VM initialization. + * Turns on paging and changes the kernel segments to use high linear addresses. + */ +static void +i386at_init(void) +{ + /* + * Initialize the PIC prior to any possible call to an spl. + */ +#ifndef MACH_HYP +# ifdef APIC + picdisable(); +# else + picinit(); +# endif +#else /* MACH_HYP */ + hyp_intrinit(); +#endif /* MACH_HYP */ + spl_init = 1; + + /* + * Read memory map and load it into the physical page allocator. + */ +#ifdef MACH_HYP + biosmem_xen_bootstrap(); +#else /* MACH_HYP */ + register_boot_data((struct multiboot_raw_info *) &boot_info); + biosmem_bootstrap((struct multiboot_raw_info *) &boot_info); +#endif /* MACH_HYP */ + +#ifdef MACH_XEN + kernel_cmdline = (char*) boot_info.cmd_line; +#else /* MACH_XEN */ + vm_offset_t addr; + + /* Copy content pointed by boot_info before losing access to it when it + * is too far in physical memory. + * Also avoids leaving them in precious areas such as DMA memory. */ + if (boot_info.flags & MULTIBOOT_CMDLINE) { + int len = strlen ((char*)phystokv(boot_info.cmdline)) + 1; + if (! init_alloc_aligned(round_page(len), &addr)) + panic("could not allocate memory for multiboot command line"); + kernel_cmdline = (char*) phystokv(addr); + memcpy(kernel_cmdline, (void *)phystokv(boot_info.cmdline), len); + boot_info.cmdline = addr; + } + + if (boot_info.flags & MULTIBOOT_MODS && boot_info.mods_count) { + struct multiboot_raw_module *m; + int i; + + if (! init_alloc_aligned( + round_page(boot_info.mods_count * sizeof(*m)), &addr)) + panic("could not allocate memory for multiboot modules"); + m = (void*) phystokv(addr); + memcpy(m, (void*) phystokv(boot_info.mods_addr), boot_info.mods_count * sizeof(*m)); + boot_info.mods_addr = addr; + + for (i = 0; i < boot_info.mods_count; i++) { + vm_size_t size = m[i].mod_end - m[i].mod_start; + if (! init_alloc_aligned(round_page(size), &addr)) + panic("could not allocate memory for multiboot " + "module %d", i); + memcpy((void*) phystokv(addr), (void*) phystokv(m[i].mod_start), size); + m[i].mod_start = addr; + m[i].mod_end = addr + size; + + size = strlen((char*) phystokv(m[i].string)) + 1; + if (! init_alloc_aligned(round_page(size), &addr)) + panic("could not allocate memory for multiboot " + "module command line %d", i); + memcpy((void*) phystokv(addr), (void*) phystokv(m[i].string), size); + m[i].string = addr; + } + } +#endif /* MACH_XEN */ + + /* + * Initialize kernel physical map, mapping the + * region from loadpt to avail_start. + * Kernel virtual address starts at VM_KERNEL_MIN_ADDRESS. + * XXX make the BIOS page (page 0) read-only. + */ + pmap_bootstrap(); + + /* + * Load physical segments into the VM system. + * The early allocation functions become unusable after + * this point. + */ + biosmem_setup(); + + pmap_make_temporary_mapping(); + +#ifndef MACH_HYP + /* Turn paging on. + * Also set the WP bit so that on 486 or better processors + * page-level write protection works in kernel mode. + */ + set_cr0(get_cr0() | CR0_PG | CR0_WP); + set_cr0(get_cr0() & ~(CR0_CD | CR0_NW)); + if (CPU_HAS_FEATURE(CPU_FEATURE_PGE)) + set_cr4(get_cr4() | CR4_PGE); +#endif /* MACH_HYP */ + flush_instr_queue(); +#ifdef MACH_PV_PAGETABLES + pmap_clear_bootstrap_pagetable((void *)boot_info.pt_base); +#endif /* MACH_PV_PAGETABLES */ + + /* + * Initialize and activate the real i386 protected-mode structures. + */ + gdt_init(); + idt_init(); +#ifndef MACH_HYP + int_init(); +#endif /* MACH_HYP */ + ldt_init(); + ktss_init(); + +#ifndef MACH_XEN + init_percpu(0); +#endif +#if NCPUS > 1 + /* Initialize SMP structures in the master processor */ + mp_desc_init(0); +#endif // NCPUS + + pmap_remove_temporary_mapping(); + +#ifdef MACH_XEN + hyp_p2m_init(); +#endif /* MACH_XEN */ + + interrupt_stack_alloc(); +} + +/* + * C boot entrypoint - called by boot_entry in boothdr.S. + * Running in flat mode, but without paging yet. + */ +void c_boot_entry(vm_offset_t bi) +{ +#if ENABLE_IMMEDIATE_CONSOLE + romputc = immc_romputc; +#endif /* ENABLE_IMMEDIATE_CONSOLE */ + + /* Stash the boot_image_info pointer. */ + boot_info = *(typeof(boot_info)*)phystokv(bi); + int cpu_type; + + /* Before we do _anything_ else, print the hello message. + If there are no initialized console devices yet, + it will be stored and printed at the first opportunity. */ + printf("%s", version); + printf("\n"); + +#ifdef MACH_XEN + printf("Running on %s.\n", boot_info.magic); + if (boot_info.flags & SIF_PRIVILEGED) + panic("Mach can't run as dom0."); +#ifdef MACH_PSEUDO_PHYS + mfn_list = (void*)boot_info.mfn_list; +#endif +#else /* MACH_XEN */ + +#if MACH_KDB + /* + * Locate the kernel's symbol table, if the boot loader provided it. + * We need to do this before i386at_init() + * so that the symbol table's memory won't be stomped on. + */ + if ((boot_info.flags & MULTIBOOT_ELF_SHDR) + && boot_info.shdr_num) + { + elf_shdr_num = boot_info.shdr_num; + elf_shdr_size = boot_info.shdr_size; + elf_shdr_addr = (vm_offset_t)phystokv(boot_info.shdr_addr); + elf_shdr_shndx = boot_info.shdr_strndx; + + printf("ELF section header table at %08" PRIxPTR "\n", elf_shdr_addr); + } +#endif /* MACH_KDB */ +#endif /* MACH_XEN */ + + cpu_type = discover_x86_cpu_type (); + + /* + * Do basic VM initialization + */ + i386at_init(); + +#if MACH_KDB + /* + * Initialize the kernel debugger's kernel symbol table. + */ + if (elf_shdr_num) + { + elf_db_sym_init(elf_shdr_num,elf_shdr_size, + elf_shdr_addr, elf_shdr_shndx, + "mach", NULL); + } +#endif /* MACH_KDB */ + + machine_slot[0].is_cpu = TRUE; + machine_slot[0].cpu_subtype = CPU_SUBTYPE_AT386; + +#if defined(__x86_64__) && !defined(USER32) + machine_slot[0].cpu_type = CPU_TYPE_X86_64; +#else + switch (cpu_type) + { + default: + printf("warning: unknown cpu type %d, assuming i386\n", cpu_type); + case 3: + machine_slot[0].cpu_type = CPU_TYPE_I386; + break; + case 4: + machine_slot[0].cpu_type = CPU_TYPE_I486; + break; + case 5: + machine_slot[0].cpu_type = CPU_TYPE_PENTIUM; + break; + case 6: + case 15: + machine_slot[0].cpu_type = CPU_TYPE_PENTIUMPRO; + break; + } +#endif + + /* + * Start the system. + */ + setup_main(); + +} + +#include +#include +#include + +vm_offset_t +timemmap(dev_t dev, vm_offset_t off, vm_prot_t prot) +{ + extern time_value_t *mtime; + + if (prot & VM_PROT_WRITE) return (-1); + + return (i386_btop(pmap_extract(pmap_kernel(), (vm_offset_t) mtime))); +} + +void +startrtclock(void) +{ +#ifdef APIC + unmask_irq(timer_pin); + calibrate_lapic_timer(); + if (cpu_number() != 0) { + lapic_enable_timer(); + } +#else + clkstart(); +#ifndef MACH_HYP + unmask_irq(0); +#endif +#endif +} + +void +inittodr(void) +{ + time_value64_t new_time; + uint64_t newsecs; + + (void) readtodc(&newsecs); + new_time.seconds = newsecs; + new_time.nanoseconds = 0; + + { + spl_t s = splhigh(); + time = new_time; + splx(s); + } +} + +void +resettodr(void) +{ + writetodc(); +} + +boolean_t +init_alloc_aligned(vm_size_t size, vm_offset_t *addrp) +{ + *addrp = biosmem_bootalloc(vm_page_atop(vm_page_round(size))); + + if (*addrp == 0) + return FALSE; + + return TRUE; +} + +/* Grab a physical page: + the standard memory allocation mechanism + during system initialization. */ +vm_offset_t +pmap_grab_page(void) +{ + vm_offset_t addr; + if (!init_alloc_aligned(PAGE_SIZE, &addr)) + panic("Not enough memory to initialize Mach"); + return addr; +} diff --git a/i386/i386at/model_dep.h b/i386/i386at/model_dep.h new file mode 100644 index 0000000..3d5b664 --- /dev/null +++ b/i386/i386at/model_dep.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2013 Free Software Foundation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _MODEL_DEP_H_ +#define _MODEL_DEP_H_ + +#include +#include + +/* + * Interrupt stack. + */ +extern vm_offset_t int_stack_top[NCPUS], int_stack_base[NCPUS]; + +/* Check whether P points to the per-cpu interrupt stack. */ +#define ON_INT_STACK(P, CPU) (((P) & ~(INTSTACK_SIZE-1)) == int_stack_base[CPU]) + +extern vm_offset_t timemmap(dev_t dev, vm_offset_t off, vm_prot_t prot); + +void inittodr(void); + +boolean_t init_alloc_aligned(vm_size_t size, vm_offset_t *addrp); + +#endif /* _MODEL_DEP_H_ */ diff --git a/i386/i386at/pic_isa.c b/i386/i386at/pic_isa.c new file mode 100644 index 0000000..1e5ac10 --- /dev/null +++ b/i386/i386at/pic_isa.c @@ -0,0 +1,56 @@ +/* + * 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. + */ + +#include +#include +#include +#include +#include +#include + +/* These interrupts are always present */ + +interrupt_handler_fn ivect[NINTR] = { + /* 00 */ (interrupt_handler_fn)hardclock, /* always */ + /* 01 */ kdintr, /* kdintr, ... */ + /* 02 */ intnull, + /* 03 */ intnull, /* lnpoll, comintr, ... */ + + /* 04 */ intnull, /* comintr, ... */ + /* 05 */ intnull, /* comintr, wtintr, ... */ + /* 06 */ intnull, /* fdintr, ... */ + /* 07 */ intnull, /* qdintr, ... */ + + /* 08 */ intnull, + /* 09 */ intnull, /* ether */ + /* 10 */ intnull, + /* 11 */ intnull, + + /* 12 */ intnull, + /* 13 */ fpintr, /* always */ + /* 14 */ intnull, /* hdintr, ... */ + /* 15 */ intnull, /* ??? */ +}; diff --git a/i386/i386at/rtc.c b/i386/i386at/rtc.c new file mode 100644 index 0000000..1930beb --- /dev/null +++ b/i386/i386at/rtc.c @@ -0,0 +1,242 @@ +/* + * 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. + */ + +/* + Copyright 1988, 1989 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 +#include +#include +#include +#include +#include + +/* time of day stored in RTC are currently between 1970 and 2070. Update that + * before 2070 please. */ +#define CENTURY_START 1970 + +static boolean_t first_rtcopen_ever = TRUE; + +static void +rtcinit(void) +{ + outb(RTC_ADDR, RTC_A); + outb(RTC_DATA, RTC_DIV2 | RTC_RATE6); + outb(RTC_ADDR, RTC_B); + outb(RTC_DATA, RTC_HM); +} + + +static int +rtcget(struct rtc_st *st) +{ + unsigned char *regs = (unsigned char *)st; + if (first_rtcopen_ever) { + rtcinit(); + first_rtcopen_ever = FALSE; + } + outb(RTC_ADDR, RTC_D); + if ((inb(RTC_DATA) & RTC_VRT) == 0) return(-1); + outb(RTC_ADDR, RTC_A); + while (inb(RTC_DATA) & RTC_UIP) /* busy wait */ + outb(RTC_ADDR, RTC_A); + load_rtc(regs); + return(0); +} + +static void +rtcput(struct rtc_st *st) +{ + unsigned char *regs = (unsigned char *)st; + unsigned char x; + + if (first_rtcopen_ever) { + rtcinit(); + first_rtcopen_ever = FALSE; + } + outb(RTC_ADDR, RTC_B); + x = inb(RTC_DATA); + outb(RTC_ADDR, RTC_B); + outb(RTC_DATA, x | RTC_SET); + save_rtc(regs); + outb(RTC_ADDR, RTC_B); + outb(RTC_DATA, x & ~RTC_SET); +} + + +static int month[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + +static int +yeartoday(int year) +{ + if (year%4) + /* Not divisible by 4, not bissextile */ + return 365; + + /* Divisible by 4 */ + if (year % 100) + /* Not divisible by 100, bissextile */ + return 366; + + /* Divisible by 100 */ + if (year % 400) + /* Not divisible by 400, not bissextile */ + return 365; + + /* Divisible by 400 */ + /* Rules for 2000 and further have not been officially decided yet. + * 2000 was made bissextile. */ + return 366; +} + +static int +hexdectodec(char n) +{ + return(((n>>4)&0x0F)*10 + (n&0x0F)); +} + +static char +dectohexdec(int n) +{ + return((char)(((n/10)<<4)&0xF0) | ((n%10)&0x0F)); +} + +int +readtodc(uint64_t *tp) +{ + struct rtc_st rtclk; + time_t n; + int sec, min, hr, dom, mon, yr; + int i, days = 0; + spl_t ospl; + + ospl = splclock(); + if (rtcget(&rtclk)) { + splx(ospl); + return(-1); + } + splx (ospl); + + sec = hexdectodec(rtclk.rtc_sec); + min = hexdectodec(rtclk.rtc_min); + hr = hexdectodec(rtclk.rtc_hr); + dom = hexdectodec(rtclk.rtc_dom); + mon = hexdectodec(rtclk.rtc_mon); + yr = hexdectodec(rtclk.rtc_yr); + yr = (yr < CENTURY_START%100) ? + yr+CENTURY_START-CENTURY_START%100+100 : + yr+CENTURY_START-CENTURY_START%100; + + if (yr >= CENTURY_START+90) { + printf("FIXME: we are approaching %u, update CENTURY_START\n", CENTURY_START); + } + + printf("RTC time is %04u-%02u-%02u %02u:%02u:%02u\n", yr, mon, dom, hr, min, sec); + + n = sec + 60 * min + 3600 * hr; + n += (dom - 1) * 3600 * 24; + + if (yeartoday(yr) == 366) + month[1] = 29; + for (i = mon - 2; i >= 0; i--) + days += month[i]; + month[1] = 28; + /* Epoch shall be 1970 January 1st */ + for (i = 1970; i < yr; i++) + days += yeartoday(i); + n += days * 3600 * 24; + + + *tp = n; + + return(0); +} + +int +writetodc(void) +{ + struct rtc_st rtclk; + time_t n; + int diff, i, j; + spl_t ospl; + + ospl = splclock(); + if (rtcget(&rtclk)) { + splx(ospl); + return(-1); + } + splx(ospl); + + diff = 0; + n = (time.seconds - diff) % (3600 * 24); /* hrs+mins+secs */ + rtclk.rtc_sec = dectohexdec(n%60); + n /= 60; + rtclk.rtc_min = dectohexdec(n%60); + rtclk.rtc_hr = dectohexdec(n/60); + + n = (time.seconds - diff) / (3600 * 24); /* days */ + rtclk.rtc_dow = (n + 4) % 7; /* 1/1/70 is Thursday */ + + /* Epoch shall be 1970 January 1st */ + for (j = 1970, i = yeartoday(j); n >= i; j++, i = yeartoday(j)) + n -= i; + + rtclk.rtc_yr = dectohexdec(j % 100); + + if (i == 366) + month[1] = 29; + for (i = 0; n >= month[i]; i++) + n -= month[i]; + month[1] = 28; + rtclk.rtc_mon = dectohexdec(++i); + + rtclk.rtc_dom = dectohexdec(++n); + + ospl = splclock(); + rtcput(&rtclk); + splx(ospl); + + return(0); +} diff --git a/i386/i386at/rtc.h b/i386/i386at/rtc.h new file mode 100644 index 0000000..5379722 --- /dev/null +++ b/i386/i386at/rtc.h @@ -0,0 +1,143 @@ +/* + * 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. + */ +/* + Copyright 1988, 1989 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. +*/ + +#ifndef _RTC_H_ +#define _RTC_H_ + +#define RTC_ADDR 0x70 /* I/O port address for register select */ +#define RTC_DATA 0x71 /* I/O port address for data read/write */ + +/* + * Register A definitions + */ +#define RTC_A 0x0a /* register A address */ +#define RTC_UIP 0x80 /* Update in progress bit */ +#define RTC_DIV0 0x00 /* Time base of 4.194304 MHz */ +#define RTC_DIV1 0x10 /* Time base of 1.048576 MHz */ +#define RTC_DIV2 0x20 /* Time base of 32.768 KHz */ +#define RTC_RATE6 0x06 /* interrupt rate of 976.562 */ + +/* + * Register B definitions + */ +#define RTC_B 0x0b /* register B address */ +#define RTC_SET 0x80 /* stop updates for time set */ +#define RTC_PIE 0x40 /* Periodic interrupt enable */ +#define RTC_AIE 0x20 /* Alarm interrupt enable */ +#define RTC_UIE 0x10 /* Update ended interrupt enable */ +#define RTC_SQWE 0x08 /* Square wave enable */ +#define RTC_DM 0x04 /* Date mode, 1 = binary, 0 = BCD */ +#define RTC_HM 0x02 /* hour mode, 1 = 24 hour, 0 = 12 hour */ +#define RTC_DSE 0x01 /* Daylight savings enable */ + +/* + * Register C definitions + */ +#define RTC_C 0x0c /* register C address */ +#define RTC_IRQF 0x80 /* IRQ flag */ +#define RTC_PF 0x40 /* PF flag bit */ +#define RTC_AF 0x20 /* AF flag bit */ +#define RTC_UF 0x10 /* UF flag bit */ + +/* + * Register D definitions + */ +#define RTC_D 0x0d /* register D address */ +#define RTC_VRT 0x80 /* Valid RAM and time bit */ + +#define RTC_NREG 0x0e /* number of RTC registers */ +#define RTC_NREGP 0x0a /* number of RTC registers to set time */ + +#define RTCRTIME _IOR('c', 0x01, struct rtc_st) /* Read time from RTC */ +#define RTCSTIME _IOW('c', 0x02, struct rtc_st) /* Set time into RTC */ + +struct rtc_st { + char rtc_sec; + char rtc_asec; + char rtc_min; + char rtc_amin; + char rtc_hr; + char rtc_ahr; + char rtc_dow; + char rtc_dom; + char rtc_mon; + char rtc_yr; + char rtc_statusa; + char rtc_statusb; + char rtc_statusc; + char rtc_statusd; +}; + +/* + * this macro reads contents of real time clock to specified buffer + */ +#define load_rtc(regs) \ +{\ + int i; \ + \ + for (i = 0; i < RTC_NREG; i++) { \ + outb(RTC_ADDR, i); \ + regs[i] = inb(RTC_DATA); \ + } \ +} + +/* + * this macro writes contents of specified buffer to real time clock + */ +#define save_rtc(regs) \ +{ \ + int i; \ + for (i = 0; i < RTC_NREGP; i++) { \ + outb(RTC_ADDR, i); \ + outb(RTC_DATA, regs[i]);\ + } \ +} + +extern int readtodc(uint64_t *tp); +extern int writetodc(void); + +#endif /* _RTC_H_ */ diff --git a/i386/include/mach/i386/asm.h b/i386/include/mach/i386/asm.h new file mode 100644 index 0000000..8ceae8c --- /dev/null +++ b/i386/include/mach/i386/asm.h @@ -0,0 +1,146 @@ +/* + * 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. + */ + +#ifndef _MACH_I386_ASM_H_ +#define _MACH_I386_ASM_H_ + +#ifdef __i386__ +#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) +#endif + +#ifdef __x86_64__ +#define S_ARG0 %rdi +#define S_ARG1 %rsi +#define S_ARG2 %rdx +#define S_ARG3 %rcx +#define S_ARG4 %r8 +#define S_ARG5 %r9 + +#define FRAME pushq %rbp; movq %rsp, %rbp +#define EMARF leave + +#define B_ARG0 S_ARG0 +#define B_ARG1 S_ARG1 +#define B_ARG2 S_ARG2 +#define B_ARG3 S_ARG3 + +#ifdef MACH_XEN +#define INT_FIX \ + popq %rcx ;\ + popq %r11 +#else +#define INT_FIX +#endif +#endif + +#ifdef i486 +#define TEXT_ALIGN 4 +#else +#define TEXT_ALIGN 2 +#endif +#define DATA_ALIGN 2 +#define ALIGN TEXT_ALIGN + +#define P2ALIGN(p2) .p2align p2 /* gas-specific */ + +#define LCL(x) x + +#define LB(x,n) n +#ifdef __STDC__ +#ifndef __ELF__ +#define EXT(x) _ ## x +#define LEXT(x) _ ## x ## : +#define SEXT(x) "_"#x +#else +#define EXT(x) x +#define LEXT(x) x ## : +#define SEXT(x) #x +#endif +#define LCLL(x) x ## : +#define gLB(n) n ## : +#define LBb(x,n) n ## b +#define LBf(x,n) n ## f +#else /* __STDC__ */ +#error XXX elf +#define EXT(x) _/**/x +#define LEXT(x) _/**/x/**/: +#define LCLL(x) x/**/: +#define gLB(n) n/**/: +#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 + +#define data16 .byte 0x66 +#define addr16 .byte 0x67 + + + +#ifdef GPROF + +#define MCOUNT .data; gLB(9) .long 0; .text; lea LBb(x, 9),%edx; call mcount +#define ENTRY(x) .globl EXT(x); .type EXT(x), @function; .p2align TEXT_ALIGN; LEXT(x) ; \ + pushl %ebp; movl %esp, %ebp; MCOUNT; popl %ebp; +#define ENTRY2(x,y) .globl EXT(x); .type EXT(x), @function; .globl EXT(y); .type EXT(y), @function; \ + .p2align TEXT_ALIGN; LEXT(x) LEXT(y) +#define ASENTRY(x) .globl x; .type x, @function; .p2align TEXT_ALIGN; gLB(x) ; \ + pushl %ebp; movl %esp, %ebp; MCOUNT; popl %ebp; +#define END(x) .size x,.-x +#else /* GPROF */ + +#define MCOUNT +#define ENTRY(x) .globl EXT(x); .type EXT(x), @function; .p2align TEXT_ALIGN; LEXT(x) +#define ENTRY2(x,y) .globl EXT(x); .type EXT(x), @function; .globl EXT(y); .type EXT(y), @function; \ + .p2align TEXT_ALIGN; LEXT(x) LEXT(y) +#define ASENTRY(x) .globl x; .type x, @function; .p2align TEXT_ALIGN; gLB(x) +#define END(x) .size x,.-x +#endif /* GPROF */ + +#define Entry(x) .globl EXT(x); .type EXT(x), @function; .p2align TEXT_ALIGN; LEXT(x) +#define DATA(x) .globl EXT(x); .p2align DATA_ALIGN; LEXT(x) + +#endif /* _MACH_I386_ASM_H_ */ diff --git a/i386/include/mach/i386/boolean.h b/i386/include/mach/i386/boolean.h new file mode 100644 index 0000000..a33d007 --- /dev/null +++ b/i386/include/mach/i386/boolean.h @@ -0,0 +1,37 @@ +/* + * 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. + */ +/* + * File: boolean.h + * + * Boolean type, for I386. + */ + +#ifndef _MACH_I386_BOOLEAN_H_ +#define _MACH_I386_BOOLEAN_H_ + +typedef int boolean_t; + +#endif /* _MACH_I386_BOOLEAN_H_ */ diff --git a/i386/include/mach/i386/eflags.h b/i386/include/mach/i386/eflags.h new file mode 100644 index 0000000..336a73a --- /dev/null +++ b/i386/include/mach/i386/eflags.h @@ -0,0 +1,53 @@ +/* + * 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. + */ + +#ifndef _MACH_I386_EFLAGS_H_ +#define _MACH_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_AC 0x00040000 /* alignment check */ +#define EFL_VI 0x00080000 /* virtual interrupt */ +#define EFL_VIP 0x00100000 /* virtual interrupt pending */ +#define EFL_ID 0x00200000 /* cpuid available */ + +#endif /* _MACH_I386_EFLAGS_H_ */ diff --git a/i386/include/mach/i386/exception.h b/i386/include/mach/i386/exception.h new file mode 100644 index 0000000..1aaf6c7 --- /dev/null +++ b/i386/include/mach/i386/exception.h @@ -0,0 +1,85 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988 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. + */ +/* + * Codes and subcodes for 80386 exceptions. + */ + +/* + * EXC_BAD_INSTRUCTION + */ + +#ifndef _MACH_I386_EXCEPTION_H_ +#define _MACH_I386_EXCEPTION_H_ + +#define EXC_I386_INVOP 1 + +/* + * EXC_ARITHMETIC + */ + +#define EXC_I386_DIV 1 +#define EXC_I386_INTO 2 +#define EXC_I386_NOEXT 3 +#define EXC_I386_EXTOVR 4 +#define EXC_I386_EXTERR 5 +#define EXC_I386_EMERR 6 +#define EXC_I386_BOUND 7 + +/* + * EXC_SOFTWARE + */ + +/* + * EXC_BAD_ACCESS + */ + +/* + * EXC_BREAKPOINT + */ + +#define EXC_I386_SGL 1 +#define EXC_I386_BPT 2 + +#define EXC_I386_DIVERR 0 /* divide by 0 eprror */ +#define EXC_I386_SGLSTP 1 /* single step */ +#define EXC_I386_NMIFLT 2 /* NMI */ +#define EXC_I386_BPTFLT 3 /* breakpoint fault */ +#define EXC_I386_INTOFLT 4 /* INTO overflow fault */ +#define EXC_I386_BOUNDFLT 5 /* BOUND instruction fault */ +#define EXC_I386_INVOPFLT 6 /* invalid opcode fault */ +#define EXC_I386_NOEXTFLT 7 /* extension not available fault*/ +#define EXC_I386_DBLFLT 8 /* double fault */ +#define EXC_I386_EXTOVRFLT 9 /* extension overrun fault */ +#define EXC_I386_INVTSSFLT 10 /* invalid TSS fault */ +#define EXC_I386_SEGNPFLT 11 /* segment not present fault */ +#define EXC_I386_STKFLT 12 /* stack fault */ +#define EXC_I386_GPFLT 13 /* general protection fault */ +#define EXC_I386_PGFLT 14 /* page fault */ +#define EXC_I386_EXTERRFLT 16 /* extension error fault */ +#define EXC_I386_ENDPERR 33 /* emulated extension error flt */ +#define EXC_I386_ENOEXTFLT 32 /* emulated ext not present */ + +#endif /* _MACH_I386_EXCEPTION_H_ */ diff --git a/i386/include/mach/i386/exec/elf.h b/i386/include/mach/i386/exec/elf.h new file mode 100644 index 0000000..60b1657 --- /dev/null +++ b/i386/include/mach/i386/exec/elf.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 1995-1994 The University of Utah and + * the Computer Systems Laboratory at the University of Utah (CSL). + * All rights reserved. + * + * Permission to use, copy, modify and distribute this software is hereby + * granted provided that (1) source code retains these copyright, permission, + * and disclaimer notices, and (2) redistributions including binaries + * reproduce the notices in supporting documentation, and (3) all advertising + * materials mentioning features or use of this software display the following + * acknowledgement: ``This product includes software developed by the + * Computer Systems Laboratory at the University of Utah.'' + * + * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS + * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF + * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * CSL requests users of this software to return to csl-dist@cs.utah.edu any + * improvements that they make and grant CSL redistribution rights. + * + * Author: Bryan Ford, University of Utah CSL + */ +#ifndef _MACH_I386_EXEC_ELF_H_ +#define _MACH_I386_EXEC_ELF_H_ + +typedef unsigned int Elf32_Addr; +typedef unsigned short Elf32_Half; +typedef unsigned int Elf32_Off; +typedef signed int Elf32_Sword; +typedef unsigned int Elf32_Word; + +typedef uint64_t Elf64_Addr; +typedef uint64_t Elf64_Off; +typedef int32_t Elf64_Shalf; +typedef int32_t Elf64_Sword; +typedef uint32_t Elf64_Word; +typedef int64_t Elf64_Sxword; +typedef uint64_t Elf64_Xword; +typedef uint16_t Elf64_Half; + + +/* Architecture identification parameters for x86. */ +#if defined(__x86_64__) && ! defined(USER32) +#define MY_ELF_CLASS ELFCLASS64 +#define MY_EI_DATA ELFDATA2LSB +#define MY_E_MACHINE EM_X86_64 +#else +#define MY_ELF_CLASS ELFCLASS32 +#define MY_EI_DATA ELFDATA2LSB +#define MY_E_MACHINE EM_386 +#endif + +#endif /* _MACH_I386_EXEC_ELF_H_ */ diff --git a/i386/include/mach/i386/fp_reg.h b/i386/include/mach/i386/fp_reg.h new file mode 100644 index 0000000..7ad0ade --- /dev/null +++ b/i386/include/mach/i386/fp_reg.h @@ -0,0 +1,140 @@ +/* + * Mach Operating System + * Copyright (c) 1992-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. + */ + +#ifndef _MACH_I386_FP_REG_H_ +#define _MACH_I386_FP_REG_H_ + +/* + * Floating point registers and status, as saved + * and restored by FP save/restore instructions. + */ +struct i386_fp_save { + unsigned short fp_control; /* control */ + unsigned short fp_unused_1; + unsigned short fp_status; /* status */ + unsigned short fp_unused_2; + unsigned short fp_tag; /* register tags */ + unsigned short fp_unused_3; + unsigned int fp_eip; /* eip at failed instruction */ + unsigned short fp_cs; /* cs at failed instruction */ + unsigned short fp_opcode; /* opcode of failed instruction */ + unsigned int fp_dp; /* data address */ + unsigned short fp_ds; /* data segment */ + unsigned short fp_unused_4; +}; + +struct i386_fp_regs { + unsigned short fp_reg_word[8][5]; + /* space for 8 80-bit FP registers */ +}; + +#define XSAVE_XCOMP_BV_COMPACT (((unsigned long long)1) << 63) +struct i386_xfp_xstate_header { + unsigned long long xfp_features; + unsigned long long xcomp_bv; + unsigned long long reserved[6]; +} __attribute__((packed, aligned(64))); + +struct i386_xfp_save { + unsigned short fp_control; /* control */ + unsigned short fp_status; /* status */ + unsigned short fp_tag; /* register tags */ + unsigned short fp_opcode; /* opcode of failed instruction */ + unsigned int fp_eip; /* eip at failed instruction */ + unsigned short fp_cs; /* cs at failed instruction / eip high */ + unsigned short fp_eip3; /* eip higher */ + unsigned int fp_dp; /* data address */ + unsigned short fp_ds; /* data segment / dp high */ + unsigned short fp_dp3; /* dp higher */ + unsigned int fp_mxcsr; /* MXCSR */ + unsigned int fp_mxcsr_mask; /* MXCSR_MASK */ + unsigned char fp_reg_word[8][16]; + /* space for 8 128-bit FP registers */ + unsigned char fp_xreg_word[16][16]; + /* space for 16 128-bit XMM registers */ + unsigned int padding[24]; + struct i386_xfp_xstate_header header; + unsigned char extended[0]; /* Extended region */ +} __attribute__((packed, aligned(64))); + +/* + * Control register + */ +#define FPC_IE 0x0001 /* enable invalid operation + exception */ +#define FPC_IM FPC_IE +#define FPC_DE 0x0002 /* enable denormalized operation + exception */ +#define FPC_DM FPC_DE +#define FPC_ZE 0x0004 /* enable zero-divide exception */ +#define FPC_ZM FPC_ZE +#define FPC_OE 0x0008 /* enable overflow exception */ +#define FPC_OM FPC_OE +#define FPC_UE 0x0010 /* enable underflow exception */ +#define FPC_PE 0x0020 /* enable precision exception */ +#define FPC_PC 0x0300 /* precision control: */ +#define FPC_PC_24 0x0000 /* 24 bits */ +#define FPC_PC_53 0x0200 /* 53 bits */ +#define FPC_PC_64 0x0300 /* 64 bits */ +#define FPC_RC 0x0c00 /* rounding control: */ +#define FPC_RC_RN 0x0000 /* round to nearest or even */ +#define FPC_RC_RD 0x0400 /* round down */ +#define FPC_RC_RU 0x0800 /* round up */ +#define FPC_RC_CHOP 0x0c00 /* chop */ +#define FPC_IC 0x1000 /* infinity control (obsolete) */ +#define FPC_IC_PROJ 0x0000 /* projective infinity */ +#define FPC_IC_AFF 0x1000 /* affine infinity (std) */ + +/* + * Status register + */ +#define FPS_IE 0x0001 /* invalid operation */ +#define FPS_DE 0x0002 /* denormalized operand */ +#define FPS_ZE 0x0004 /* divide by zero */ +#define FPS_OE 0x0008 /* overflow */ +#define FPS_UE 0x0010 /* underflow */ +#define FPS_PE 0x0020 /* precision */ +#define FPS_SF 0x0040 /* stack flag */ +#define FPS_ES 0x0080 /* error summary */ +#define FPS_C0 0x0100 /* condition code bit 0 */ +#define FPS_C1 0x0200 /* condition code bit 1 */ +#define FPS_C2 0x0400 /* condition code bit 2 */ +#define FPS_TOS 0x3800 /* top-of-stack pointer */ +#define FPS_TOS_SHIFT 11 +#define FPS_C3 0x4000 /* condition code bit 3 */ +#define FPS_BUSY 0x8000 /* FPU busy */ + +/* + * Kind of floating-point support provided by kernel. + */ +#define FP_NO 0 /* no floating point */ +#define FP_SOFT 1 /* software FP emulator */ +#define FP_287 2 /* 80287 */ +#define FP_387 3 /* 80387 or 80486 */ +#define FP_387FX 4 /* FXSAVE/RSTOR-capable */ +#define FP_387X 5 /* XSAVE/RSTOR-capable */ + +#endif /* _MACH_I386_FP_REG_H_ */ diff --git a/i386/include/mach/i386/ioccom.h b/i386/include/mach/i386/ioccom.h new file mode 100644 index 0000000..17566a3 --- /dev/null +++ b/i386/include/mach/i386/ioccom.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 1982, 1986 Regents of the University of California. + * All rights reserved. The Berkeley software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#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 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 255 bytes. + */ +#define _IOCPARM_MASK 0xff /* parameters must be < 256 bytes */ +#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) +/* the 0x20000000 is so we can distinguish new ioctl's from old */ +#define _IO(x,y) (_IOC_VOID|('x'<<8)|y) +#define _IOR(x,y,t) (_IOC_OUT|((sizeof(t)&_IOCPARM_MASK)<<16)|('x'<<8)|y) +#define _IORN(x,y,t) (_IOC_OUT|(((t)&_IOCPARM_MASK)<<16)|('x'<<8)|y) +#define _IOW(x,y,t) (_IOC_IN|((sizeof(t)&_IOCPARM_MASK)<<16)|('x'<<8)|y) +#define _IOWN(x,y,t) (_IOC_IN|(((t)&_IOCPARM_MASK)<<16)|('x'<<8)|y) +/* this should be _IORW, but stdio got there first */ +#define _IOWR(x,y,t) (_IOC_INOUT|((sizeof(t)&_IOCPARM_MASK)<<16)|('x'<<8)|y) +#define _IOWRN(x,y,t) (_IOC_INOUT|(((t)&_IOCPARM_MASK)<<16)|('x'<<8)|y) + +#endif /* !__sys_ioccom_h */ diff --git a/i386/include/mach/i386/kern_return.h b/i386/include/mach/i386/kern_return.h new file mode 100644 index 0000000..8df41ca --- /dev/null +++ b/i386/include/mach/i386/kern_return.h @@ -0,0 +1,40 @@ +/* + * 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. + */ +/* + * File: kern_return.h + * Author: Avadis Tevanian, Jr., Michael Wayne Young + * Date: 1985 + * + * Machine-dependent kernel return definitions. + */ + +#ifndef _MACH_I386_KERN_RETURN_H_ +#define _MACH_I386_KERN_RETURN_H_ + +#ifndef __ASSEMBLER__ +typedef int kern_return_t; +#endif /* __ASSEMBLER__ */ +#endif /* _MACH_I386_KERN_RETURN_H_ */ diff --git a/i386/include/mach/i386/mach_i386.defs b/i386/include/mach/i386/mach_i386.defs new file mode 100644 index 0000000..965d5c3 --- /dev/null +++ b/i386/include/mach/i386/mach_i386.defs @@ -0,0 +1,113 @@ +/* + * Mach Operating System + * Copyright (c) 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. + */ +/* + * Special functions for i386. + */ + +subsystem +#if KERNEL_SERVER + KernelServer +#endif /* KERNEL_SERVER */ + mach_i386 3800; + +#include +#include + +#ifdef MACH_I386_IMPORTS +MACH_I386_IMPORTS +#endif + +type descriptor_t = struct[2] of uint32_t; +type descriptor_list_t = array[*] of descriptor_t; + +import ; + +#if KERNEL_SERVER +simport ; +#endif + +type io_port_t = MACH_MSG_TYPE_INTEGER_16; +type io_perm_t = mach_port_t + ctype: mach_port_t +#if KERNEL_SERVER + intran: io_perm_t convert_port_to_io_perm(mach_port_t) + outtran: mach_port_t convert_io_perm_to_port(io_perm_t) + destructor: io_perm_deallocate(io_perm_t) +#endif /* KERNEL_SERVER */ + ; + +skip; /* i386_io_port_add */ +skip; /* i386_io_port_remove */ +skip; /* i386_io_port_list */ + +routine i386_set_ldt( + target_thread : thread_t; + first_selector : int; + desc_list : descriptor_list_t, serverCopy); + +routine i386_get_ldt( + target_thread : thread_t; + first_selector : int; + selector_count : int; + out desc_list : descriptor_list_t); + +/* Request a new port IO_PERM that represents the capability to access + the I/O ports [FROM; TO] directly. MASTER_PORT is the master device port. + + The function returns KERN_INVALID_ARGUMENT if TARGET_TASK is not a task, + or FROM is greater than TO. */ +routine i386_io_perm_create( + master_port : mach_port_t; + from : io_port_t; + to : io_port_t; + out io_perm : io_perm_t); + +/* Modify the I/O permissions for TARGET_TASK. If ENABLE is TRUE, the + permission to access the I/O ports specified by IO_PERM is granted, + otherwise it is withdrawn. + + The function returns KERN_INVALID_ARGUMENT if TARGET_TASK is not a valid + task or IO_PERM not a valid I/O permission port. */ +routine i386_io_perm_modify( + target_task : task_t; + io_perm : io_perm_t; + enable : boolean_t); + +/* Modify one of a few available thread-specific segment descriptor slots. + The SELECTOR must be a value from a previous call (on any thread), + or -1 to allocate an available slot and return the segment selector for it. + These slots are copied into the CPU on each thread switch. + Returns KERN_NO_SPACE when there are no more slots available. */ +routine i386_set_gdt( + target_thread : thread_t; + inout selector : int; + desc : descriptor_t); + +/* Fetch a segment descriptor set with a prior i386_set_gdt call. */ +routine i386_get_gdt( + target_thread : thread_t; + selector : int; + out desc : descriptor_t); diff --git a/i386/include/mach/i386/mach_i386_types.h b/i386/include/mach/i386/mach_i386_types.h new file mode 100644 index 0000000..f5177fb --- /dev/null +++ b/i386/include/mach/i386/mach_i386_types.h @@ -0,0 +1,57 @@ +/* + * Mach Operating System + * Copyright (c) 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. + */ +/* + * Type definitions for i386 interface routines. + */ + +#ifndef _MACH_MACH_I386_TYPES_H_ +#define _MACH_MACH_I386_TYPES_H_ + +#ifndef __ASSEMBLER__ +/* + * i386 segment descriptor. + */ +struct descriptor { + unsigned int low_word; + unsigned int high_word; +}; + +typedef struct descriptor descriptor_t; +typedef struct descriptor *descriptor_list_t; +typedef const struct descriptor *const_descriptor_list_t; + +#endif /* !__ASSEMBLER__ */ + +/* + * i386 I/O port + */ + +#ifndef MACH_KERNEL +typedef unsigned short io_port_t; +typedef mach_port_t io_perm_t; +#endif /* !MACH_KERNEL */ + +#endif /* _MACH_MACH_I386_TYPES_H_ */ diff --git a/i386/include/mach/i386/machine_types.defs b/i386/include/mach/i386/machine_types.defs new file mode 100755 index 0000000..76c7dcf --- /dev/null +++ b/i386/include/mach/i386/machine_types.defs @@ -0,0 +1,107 @@ +/* + * Mach Operating System + * Copyright (c) 1992 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. + */ +/* + * File: mach/machine/machine_types.defs + * Author: Alessandro Forin + * Date: 7/92 + * + * Header file for the basic, machine-dependent data types. + * Version for 32 bit architectures. + * + */ + +#ifndef _MACHINE_MACHINE_TYPES_DEFS_ +#define _MACHINE_MACHINE_TYPES_DEFS_ 1 + +/* + * A natural_t is the type for the native + * unsigned integer type, usually 32 bits. It is suitable for + * most counters with a small chance of overflow. + * While historically natural_t was meant to be the same + * as a pointer, that is not the case here. + */ +type natural_t = uint32_t; + +/* + * An integer_t is the signed counterpart + * of the natural_t type. Both types are + * only supposed to be used to define + * other types in a machine-independent + * way. + */ +type integer_t = int32_t; + +/* + * long_natural_t and long_integer_t for kernel <-> userland interfaces as the + * size depends on the architecture of both kernel and userland. + */ +#if defined(KERNEL_SERVER) && defined(USER32) +type rpc_long_natural_t = uint32_t; +type rpc_long_integer_t = int32_t; +#else /* KERNEL and USER32 */ +#if defined(__x86_64__) +type rpc_long_natural_t = uint64_t; +type rpc_long_integer_t = int64_t; +#else +type rpc_long_natural_t = uint32_t; +type rpc_long_integer_t = int32_t; +#endif /* __x86_64__ */ +#endif /* KERNEL_SERVER and USER32 */ + +/* + * A long_natural_t is a possibly larger unsigned integer type than natural_t. + * Should be used instead of natural_t when we want the data to be less subject + * to overflows. + */ +type long_natural_t = rpc_long_natural_t +#if defined(KERNEL_SERVER) + intran: long_natural_t convert_long_natural_from_user(rpc_long_natural_t) + outtran: rpc_long_natural_t convert_long_natural_to_user(long_natural_t) +#elif defined(KERNEL_USER) + ctype: rpc_long_natural_t +#endif + ; + +/* + * Larger version of integer_t. Only used when we want to hold possibly larger + * values than what is possible with integer_t. + */ +type long_integer_t = rpc_long_integer_t +#if defined(KERNEL_SERVER) + intran: long_integer_t convert_long_integer_from_user(rpc_long_integer_t) + outtran: rpc_long_integer_t convert_long_integer_to_user(long_integer_t) +#elif defined(KERNEL_USER) + ctype: rpc_long_integer_t +#endif + ; + +/* + * Physical address size + */ +type rpc_phys_addr_t = uint64_t; +type rpc_phys_addr_array_t = array[] of rpc_phys_addr_t; + +#endif /* _MACHINE_MACHINE_TYPES_DEFS_ */ diff --git a/i386/include/mach/i386/multiboot.h b/i386/include/mach/i386/multiboot.h new file mode 100644 index 0000000..c3538c1 --- /dev/null +++ b/i386/include/mach/i386/multiboot.h @@ -0,0 +1,208 @@ +/* + * Copyright (c) 1995-1994 The University of Utah and + * the Computer Systems Laboratory at the University of Utah (CSL). + * All rights reserved. + * + * Permission to use, copy, modify and distribute this software is hereby + * granted provided that (1) source code retains these copyright, permission, + * and disclaimer notices, and (2) redistributions including binaries + * reproduce the notices in supporting documentation, and (3) all advertising + * materials mentioning features or use of this software display the following + * acknowledgement: ``This product includes software developed by the + * Computer Systems Laboratory at the University of Utah.'' + * + * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS + * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF + * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * CSL requests users of this software to return to csl-dist@cs.utah.edu any + * improvements that they make and grant CSL redistribution rights. + * + * Author: Bryan Ford, University of Utah CSL + */ +#ifndef _MACH_I386_MULTIBOOT_H_ +#define _MACH_I386_MULTIBOOT_H_ + +#include + +/* The entire multiboot_header must be contained + within the first MULTIBOOT_SEARCH bytes of the kernel image. */ +#define MULTIBOOT_SEARCH 8192 + +/* Magic value identifying the multiboot_header. */ +#define MULTIBOOT_MAGIC 0x1badb002 + +/* Features flags for 'flags'. + If a boot loader sees a flag in MULTIBOOT_MUSTKNOW set + and it doesn't understand it, it must fail. */ +#define MULTIBOOT_MUSTKNOW 0x0000ffff + +/* Align all boot modules on page (4KB) boundaries. */ +#define MULTIBOOT_PAGE_ALIGN 0x00000001 + +/* Must be provided memory information in multiboot_raw_info structure */ +#define MULTIBOOT_MEMORY_INFO 0x00000002 + +/* Use the load address fields above instead of the ones in the a.out header + to figure out what to load where, and what to do afterwards. + This should only be needed for a.out kernel images + (ELF and other formats can generally provide the needed information). */ +#define MULTIBOOT_AOUT_KLUDGE 0x00010000 + +/* The boot loader passes this value in register EAX to signal the kernel + that the multiboot method is being used */ +#define MULTIBOOT_VALID 0x2badb002 + + + +#define MULTIBOOT_MEMORY 0x00000001 +#define MULTIBOOT_BOOT_DEVICE 0x00000002 +#define MULTIBOOT_CMDLINE 0x00000004 +#define MULTIBOOT_MODS 0x00000008 +#define MULTIBOOT_AOUT_SYMS 0x00000010 +#define MULTIBOOT_ELF_SHDR 0x00000020 +#define MULTIBOOT_MEM_MAP 0x00000040 + + +/* The mods_addr field above contains the physical address of the first + of 'mods_count' multiboot_module structures. */ +struct multiboot_module +{ + /* Physical start and end addresses of the module data itself. */ + vm_offset_t mod_start; + vm_offset_t mod_end; + + /* Arbitrary ASCII string associated with the module. */ + vm_offset_t string; + + /* Boot loader must set to 0; OS must ignore. */ + unsigned reserved; +}; + +#ifdef __x86_64__ +/* The mods_addr field above contains the physical address of the first + of 'mods_count' multiboot_module structures. */ +struct multiboot32_module +{ + /* Physical start and end addresses of the module data itself. */ + unsigned mod_start; + unsigned mod_end; + + /* Arbitrary ASCII string associated with the module. */ + unsigned string; + + /* Boot loader must set to 0; OS must ignore. */ + unsigned reserved; +}; +#endif + +/* usable memory "Type", all others are reserved. */ +#define MB_ARD_MEMORY 1 + +/* + * Copyright (c) 2010, 2012 Richard Braun. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * Versions used by the biosmem module. + */ + +#include + +/* + * Magic number provided by the OS to the boot loader. + */ +#define MULTIBOOT_OS_MAGIC 0x1badb002 + +/* + * Multiboot flags requesting services from the boot loader. + */ +#define MULTIBOOT_OS_MEMORY_INFO 0x2 + +#define MULTIBOOT_OS_FLAGS MULTIBOOT_OS_MEMORY_INFO + +/* + * Magic number to identify a multiboot compliant boot loader. + */ +#define MULTIBOOT_LOADER_MAGIC 0x2badb002 + +/* + * Multiboot flags set by the boot loader. + */ +#define MULTIBOOT_LOADER_MEMORY 0x01 +#define MULTIBOOT_LOADER_CMDLINE 0x04 +#define MULTIBOOT_LOADER_MODULES 0x08 +#define MULTIBOOT_LOADER_SHDR 0x20 +#define MULTIBOOT_LOADER_MMAP 0x40 + +/* + * A multiboot module. + */ +struct multiboot_raw_module { + uint32_t mod_start; + uint32_t mod_end; + uint32_t string; + uint32_t reserved; +} __packed; + +/* + * Memory map entry. + */ +struct multiboot_raw_mmap_entry { + uint32_t size; + uint64_t base_addr; + uint64_t length; + uint32_t type; +} __packed; + +/* + * Multiboot information structure as passed by the boot loader. + */ +struct multiboot_raw_info { + uint32_t flags; + uint32_t mem_lower; + uint32_t mem_upper; + uint32_t unused0; + uint32_t cmdline; + uint32_t mods_count; + uint32_t mods_addr; + uint32_t shdr_num; + uint32_t shdr_size; + uint32_t shdr_addr; + uint32_t shdr_strndx; + uint32_t mmap_length; + uint32_t mmap_addr; + uint32_t unused1[9]; +} __packed; + +/* + * Versions of the multiboot structures suitable for use with 64-bit pointers. + */ + +struct multiboot_os_module { + void *mod_start; + void *mod_end; + char *string; +}; + +struct multiboot_os_info { + uint32_t flags; + char *cmdline; + struct multiboot_module *mods_addr; + uint32_t mods_count; +}; + +#endif /* _MACH_I386_MULTIBOOT_H_ */ diff --git a/i386/include/mach/i386/syscall_sw.h b/i386/include/mach/i386/syscall_sw.h new file mode 100644 index 0000000..9eeb293 --- /dev/null +++ b/i386/include/mach/i386/syscall_sw.h @@ -0,0 +1,39 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988 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. + */ + +#ifndef _MACH_I386_SYSCALL_SW_H_ +#define _MACH_I386_SYSCALL_SW_H_ + +#include + +#define kernel_trap(trap_name,trap_number,number_args) \ +ENTRY(trap_name) \ + movl $ trap_number,%eax; \ + SVC; \ + ret; \ +END(trap_name) + +#endif /* _MACH_I386_SYSCALL_SW_H_ */ diff --git a/i386/include/mach/i386/thread_status.h b/i386/include/mach/i386/thread_status.h new file mode 100644 index 0000000..94596a7 --- /dev/null +++ b/i386/include/mach/i386/thread_status.h @@ -0,0 +1,190 @@ +/* + * 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. + */ +/* + * File: thread_status.h + * Author: Avadis Tevanian, Jr. + * Date: 1985 + * + * This file contains the structure definitions for the thread + * state as applied to I386 processors. + */ + +#ifndef _MACH_I386_THREAD_STATUS_H_ +#define _MACH_I386_THREAD_STATUS_H_ + +#include +/* + * i386_thread_state this is the structure that is exported + * to user threads for use in status/mutate + * calls. This structure should never + * change. + * + * i386_float_state exported to use threads for access to + * floating point registers. Try not to + * change this one, either. + * + * i386_isa_port_map_state exported to user threads to allow + * selective in/out operations + * + */ + +#define i386_THREAD_STATE 1 +#define i386_FLOAT_STATE 2 +#define i386_ISA_PORT_MAP_STATE 3 +#define i386_V86_ASSIST_STATE 4 +#define i386_REGS_SEGS_STATE 5 +#define i386_DEBUG_STATE 6 +#define i386_FSGS_BASE_STATE 7 + +/* + * This structure is used for both + * i386_THREAD_STATE and i386_REGS_SEGS_STATE. + */ +struct i386_thread_state { +#if defined(__x86_64__) && !defined(USER32) + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + uint64_t rdi; + uint64_t rsi; + uint64_t rbp; + uint64_t rsp; + uint64_t rbx; + uint64_t rdx; + uint64_t rcx; + uint64_t rax; + uint64_t rip; +#else + unsigned int gs; + unsigned int fs; + unsigned int es; + unsigned int ds; + + unsigned int edi; + unsigned int esi; + unsigned int ebp; + unsigned int esp; + unsigned int ebx; + unsigned int edx; + unsigned int ecx; + unsigned int eax; + unsigned int eip; +#endif /* __x86_64__ && !USER32 */ + + unsigned int cs; +#if defined(__x86_64__) && !defined(USER32) + uint64_t rfl; + uint64_t ursp; +#else + unsigned int efl; + unsigned int uesp; +#endif /* __x86_64__ and !USER32 */ + + unsigned int ss; +}; +#define i386_THREAD_STATE_COUNT (sizeof (struct i386_thread_state)/sizeof(unsigned int)) + +/* + * Floating point state. + * + * fpkind tells in what way floating point operations are supported. + * See the values for fp_kind in . + * + * If the kind is FP_NO, then calls to set the state will fail, and + * thread_getstatus will return garbage for the rest of the state. + * If "initialized" is false, then the rest of the state is garbage. + * Clients can set "initialized" to false to force the coprocessor to + * be reset. + * "exc_status" is non-zero if the thread has noticed (but not + * proceeded from) a coprocessor exception. It contains the status + * word with the exception bits set. The status word in "fp_status" + * will have the exception bits turned off. If an exception bit in + * "fp_status" is turned on, then "exc_status" should be zero. This + * happens when the coprocessor exception is noticed after the system + * has context switched to some other thread. + * + * If kind is FP_387, then "state" is a i387_state. Other kinds might + * also use i387_state, but somebody will have to verify it (XXX). + * Note that the registers are ordered from top-of-stack down, not + * according to physical register number. + */ + +#define FP_STATE_BYTES \ + (sizeof (struct i386_fp_save) + sizeof (struct i386_fp_regs)) + +struct i386_float_state { + int fpkind; /* FP_NO..FP_387X (readonly) */ + int initialized; + unsigned char hw_state[FP_STATE_BYTES]; /* actual "hardware" state */ + int exc_status; /* exception status (readonly) */ +}; +#define i386_FLOAT_STATE_COUNT (sizeof(struct i386_float_state)/sizeof(unsigned int)) + + +#define PORT_MAP_BITS 0x400 +struct i386_isa_port_map_state { + unsigned char pm[PORT_MAP_BITS>>3]; +}; + +#define i386_ISA_PORT_MAP_STATE_COUNT (sizeof(struct i386_isa_port_map_state)/sizeof(unsigned int)) + +/* + * V8086 assist supplies a pointer to an interrupt + * descriptor table in task space. + */ +struct i386_v86_assist_state { + unsigned int int_table; /* interrupt table address */ + int int_count; /* interrupt table size */ +}; + +struct v86_interrupt_table { + unsigned int count; /* count of pending interrupts */ + unsigned short mask; /* ignore this interrupt if true */ + unsigned short vec; /* vector to take */ +}; + +#define i386_V86_ASSIST_STATE_COUNT \ + (sizeof(struct i386_v86_assist_state)/sizeof(unsigned int)) + +struct i386_debug_state { + unsigned int dr[8]; +}; +#define i386_DEBUG_STATE_COUNT \ + (sizeof(struct i386_debug_state)/sizeof(unsigned int)) + +struct i386_fsgs_base_state { + unsigned long fs_base; + unsigned long gs_base; +}; +#define i386_FSGS_BASE_STATE_COUNT \ + (sizeof(struct i386_fsgs_base_state)/sizeof(unsigned int)) + +#endif /* _MACH_I386_THREAD_STATUS_H_ */ diff --git a/i386/include/mach/i386/trap.h b/i386/include/mach/i386/trap.h new file mode 100644 index 0000000..70b28fe --- /dev/null +++ b/i386/include/mach/i386/trap.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 "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. + */ + +#ifndef _MACH_I386_TRAP_H_ +#define _MACH_I386_TRAP_H_ + +/* + * Hardware trap vectors for i386. + */ +#define T_DIVIDE_ERROR 0 +#define T_DEBUG 1 +#define T_NMI 2 /* non-maskable interrupt */ +#define T_INT3 3 /* int 3 instruction */ +#define T_OVERFLOW 4 /* overflow test */ +#define T_OUT_OF_BOUNDS 5 /* bounds check */ +#define T_INVALID_OPCODE 6 /* invalid op code */ +#define T_NO_FPU 7 /* no floating point */ +#define T_DOUBLE_FAULT 8 /* double fault */ +#define T_FPU_FAULT 9 +/* 10 */ +#define T_SEGMENT_NOT_PRESENT 11 +#define T_STACK_FAULT 12 +#define T_GENERAL_PROTECTION 13 +#define T_PAGE_FAULT 14 +/* 15 */ +#define T_FLOATING_POINT_ERROR 16 +#define T_WATCHPOINT 17 + +/* + * Page-fault trap codes. + */ +#define T_PF_PROT 0x1 /* protection violation */ +#define T_PF_WRITE 0x2 /* write access */ +#define T_PF_USER 0x4 /* from user state */ + + +#endif /* _MACH_I386_TRAP_H_ */ diff --git a/i386/include/mach/i386/vm_param.h b/i386/include/mach/i386/vm_param.h new file mode 100644 index 0000000..3e5c18c --- /dev/null +++ b/i386/include/mach/i386/vm_param.h @@ -0,0 +1,90 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988 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. + */ +/* + * File: vm_param.h + * Author: Avadis Tevanian, Jr. + * Date: 1985 + * + * I386 machine dependent virtual memory parameters. + * Most of the declarations are preceded by I386_ (or i386_) + * which is OK because only I386 specific code will be using + * them. + */ + +#ifndef _MACH_I386_VM_PARAM_H_ +#define _MACH_I386_VM_PARAM_H_ + +#include + +#define BYTE_SIZE 8 /* byte size in bits */ + +#define I386_PGBYTES 4096 /* bytes per 80386 page */ +#define I386_PGSHIFT 12 /* number of bits to shift for pages */ + +/* Virtual page size is the same as real page size - 4K is big enough. */ +#define PAGE_SHIFT I386_PGSHIFT + +/* + * Convert bytes to pages and convert pages to bytes. + * No rounding is used. + */ + +#define i386_btop(x) (((phys_addr_t)(x)) >> I386_PGSHIFT) +#define i386_ptob(x) (((phys_addr_t)(x)) << I386_PGSHIFT) + +/* + * Round off or truncate to the nearest page. These will work + * for either addresses or counts. (i.e. 1 byte rounds to 1 page + * bytes.) + */ + +#define i386_round_page(x) ((((phys_addr_t)(x)) + I386_PGBYTES - 1) & \ + ~(I386_PGBYTES-1)) +#define i386_trunc_page(x) (((phys_addr_t)(x)) & ~(I386_PGBYTES-1)) + +/* User address spaces are 3GB each on a 32-bit kernel, starting at + virtual and linear address 0. + On a 64-bit krenel we split the address space in half, with the + lower 128TB for the user address space and the upper 128TB for the + kernel address space. + + On a 32-bit kernel VM_MAX_ADDRESS can be reduced to leave more + space for the kernel, but must not be increased to more than 3GB as + glibc and hurd servers would not cope with that. + */ +#define VM_MIN_ADDRESS (0ULL) + +#ifdef __x86_64__ +#if defined(KERNEL) && defined(USER32) +#define VM_MAX_ADDRESS (0xc0000000UL) +#else /* defined(KERNEL) && defined(USER32) */ +#define VM_MAX_ADDRESS (0x800000000000ULL) +#endif /* defined(KERNEL) && defined(USER32) */ +#else /* __x86_64__ */ +#define VM_MAX_ADDRESS (0xc0000000UL) +#endif /* __x86_64__ */ + +#endif /* _MACH_I386_VM_PARAM_H_ */ diff --git a/i386/include/mach/i386/vm_types.h b/i386/include/mach/i386/vm_types.h new file mode 100644 index 0000000..8f528ae --- /dev/null +++ b/i386/include/mach/i386/vm_types.h @@ -0,0 +1,173 @@ +/* + * Mach Operating System + * Copyright (c) 1992,1991,1990,1989,1988 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. + */ +/* + * File: vm_types.h + * Author: Avadis Tevanian, Jr. + * Date: 1985 + * + * Header file for VM data types. I386 version. + */ + +#ifndef _MACHINE_VM_TYPES_H_ +#define _MACHINE_VM_TYPES_H_ 1 + +#ifdef __ASSEMBLER__ +#else /* __ASSEMBLER__ */ + +#include + +#ifdef MACH_KERNEL +#include +#endif + +/* + * A natural_t is the type for the native + * unsigned integer type, usually 32 bits. It is suitable for + * most counters with a small chance of overflow. + * While historically natural_t was meant to be the same + * as a pointer, that is not the case here. + */ +typedef unsigned int natural_t; + +/* + * An integer_t is the signed counterpart + * of the natural_t type. Both types are + * only supposed to be used to define + * other types in a machine-independent + * way. + */ +typedef int integer_t; + +/* + * A long_natural_t is a possibly larger unsigned integer type than natural_t. + * Should be used instead of natural_t when we want the data to be less subject + * to overflows. + */ +typedef unsigned long long_natural_t; + +/* + * Larger version of integer_t. Only used when we want to hold possibly larger + * values than what is possible with integer_t. + */ +typedef long long_integer_t; + +/* + * A vm_offset_t is a type-neutral pointer, + * e.g. an offset into a virtual memory space. + */ +typedef uintptr_t vm_offset_t; +typedef vm_offset_t * vm_offset_array_t; + +/* + * A type for physical addresses. + */ +#ifdef MACH_KERNEL +#ifdef PAE +typedef unsigned long long phys_addr_t; +#else /* PAE */ +typedef unsigned long phys_addr_t; +#endif /* PAE */ +#else +typedef unsigned long long phys_addr_t; +#endif +typedef unsigned long long rpc_phys_addr_t; +typedef rpc_phys_addr_t *rpc_phys_addr_array_t; + +/* + * A vm_size_t is the proper type for e.g. + * expressing the difference between two + * vm_offset_t entities. + */ +typedef uintptr_t vm_size_t; +typedef vm_size_t * vm_size_array_t; + +/* + * rpc_types are for user/kernel interfaces. On kernel side they may differ from + * the native types, while on user space they shall be the same. + * These three types are always of the same size, so we can reuse the conversion + * functions. + */ +#if defined(MACH_KERNEL) && defined(USER32) +typedef uint32_t rpc_uintptr_t; +typedef uint32_t rpc_vm_address_t; +typedef uint32_t rpc_vm_offset_t; +typedef uint32_t rpc_vm_size_t; + +static inline uint64_t convert_vm_from_user(uint32_t uaddr) +{ + return (uint64_t)uaddr; +} +static inline uint32_t convert_vm_to_user(uint64_t kaddr) +{ + assert(kaddr <= 0xFFFFFFFF); + return (uint32_t)kaddr; +} + +typedef uint32_t rpc_long_natural_t; +typedef int32_t rpc_long_integer_t; + +static inline int64_t convert_long_integer_from_user(int32_t i) +{ + return (int64_t)i; +} +static inline int32_t convert_long_integer_to_user(int64_t i) +{ + assert(i <= 0x7FFFFFFF); + return (int32_t)i; +} +typedef uint32_t rpc_long_natural_t; +typedef int32_t rpc_long_integer_t; +#else /* MACH_KERNEL */ +typedef uintptr_t rpc_uintptr_t; +typedef vm_offset_t rpc_vm_address_t; +typedef vm_offset_t rpc_vm_offset_t; +typedef vm_size_t rpc_vm_size_t; + +#define convert_vm_to_user null_conversion +#define convert_vm_from_user null_conversion + +typedef long_natural_t rpc_long_natural_t; +typedef long_integer_t rpc_long_integer_t; + +#define convert_long_integer_to_user null_conversion +#define convert_long_integer_from_user null_conversion +#endif /* MACH_KERNEL */ + +#define convert_long_natural_to_user convert_vm_to_user +#define convert_long_natural_from_user convert_vm_from_user + +typedef rpc_vm_size_t * rpc_vm_size_array_t; +typedef rpc_vm_offset_t * rpc_vm_offset_array_t; + +#endif /* __ASSEMBLER__ */ + +/* + * If composing messages by hand (please dont) + */ + +#define MACH_MSG_TYPE_INTEGER_T MACH_MSG_TYPE_INTEGER_32 + +#endif /* _MACHINE_VM_TYPES_H_ */ diff --git a/i386/include/mach/sa/stdarg.h b/i386/include/mach/sa/stdarg.h new file mode 100644 index 0000000..550fec4 --- /dev/null +++ b/i386/include/mach/sa/stdarg.h @@ -0,0 +1,58 @@ +/* + * Mach Operating System + * Copyright (c) 1993 Carnegie Mellon University. + * Copyright (c) 1994 The University of Utah and + * the Center for Software Science (CSS). + * 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, THE UNIVERSITY OF UTAH AND CSS ALLOW FREE USE OF + * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM 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. + */ +#ifndef _MACH_SA_STDARG_H_ +#define _MACH_SA_STDARG_H_ + +#if __GNUC__ >= 3 + +typedef __builtin_va_list va_list; + +#define va_start(v,l) __builtin_va_start(v,l) +#define va_end(v) __builtin_va_end(v) +#define va_arg(v,l) __builtin_va_arg(v,l) + +#else + +#define __va_size(type) ((sizeof(type)+sizeof(unsigned long)-1) & ~(sizeof(unsigned long)-1)) + +#ifndef _VA_LIST_ +#define _VA_LIST_ +typedef char *va_list; +#endif + +#define va_start(pvar, lastarg) \ + ((pvar) = (char*)(void*)&(lastarg) + __va_size(lastarg)) +#define va_end(pvar) +#define va_arg(pvar,type) \ + ((pvar) += __va_size(type), \ + *((type *)((pvar) - __va_size(type)))) + +#endif + +#endif /* _MACH_SA_STDARG_H_ */ diff --git a/i386/intel/pmap.c b/i386/intel/pmap.c new file mode 100644 index 0000000..e43b06c --- /dev/null +++ b/i386/intel/pmap.c @@ -0,0 +1,3325 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988 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. + */ +/* + * File: pmap.c + * Author: Avadis Tevanian, Jr., Michael Wayne Young + * (These guys wrote the Vax version) + * + * Physical Map management code for Intel i386, and i486. + * + * 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 + +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if NCPUS > 1 +#include +#endif + +#include +#include + +#ifdef MACH_PSEUDO_PHYS +#define WRITE_PTE(pte_p, pte_entry) *(pte_p) = pte_entry?pa_to_ma(pte_entry):0; +#else /* MACH_PSEUDO_PHYS */ +#define WRITE_PTE(pte_p, pte_entry) *(pte_p) = (pte_entry); +#endif /* MACH_PSEUDO_PHYS */ + +/* + * Private data structures. + */ + +/* + * 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 the pv_table. + */ + +typedef struct pv_entry { + struct pv_entry *next; /* next pv_entry */ + pmap_t pmap; /* pmap where mapping lies */ + vm_offset_t va; /* virtual address for mapping */ +} *pv_entry_t; + +#define PV_ENTRY_NULL ((pv_entry_t) 0) + +pv_entry_t pv_head_table; /* array of entries, one per page */ + +/* + * pv_list entries are kept on a list that can only be accessed + * with the pmap system locked (at SPLVM, not in the cpus_active set). + * The list is refilled from the pv_list_cache if it becomes empty. + */ +pv_entry_t pv_free_list; /* free list at SPLVM */ +def_simple_lock_data(static, pv_free_list_lock) + +#define PV_ALLOC(pv_e) { \ + simple_lock(&pv_free_list_lock); \ + if ((pv_e = pv_free_list) != 0) { \ + pv_free_list = pv_e->next; \ + } \ + simple_unlock(&pv_free_list_lock); \ +} + +#define PV_FREE(pv_e) { \ + simple_lock(&pv_free_list_lock); \ + pv_e->next = pv_free_list; \ + pv_free_list = pv_e; \ + simple_unlock(&pv_free_list_lock); \ +} + +struct kmem_cache pv_list_cache; /* cache of pv_entry structures */ + +/* + * Each entry in the pv_head_table is locked by a bit in the + * pv_lock_table. The lock bits are accessed by the physical + * address of the page they lock. + */ + +char *pv_lock_table; /* pointer to array of bits */ +#define pv_lock_table_size(n) (((n)+BYTE_SIZE-1)/BYTE_SIZE) + +/* Has pmap_init completed? */ +boolean_t pmap_initialized = FALSE; + +/* + * Range of kernel virtual addresses available for kernel memory mapping. + * Does not include the virtual addresses used to map physical memory 1-1. + * Initialized by pmap_bootstrap. + */ +vm_offset_t kernel_virtual_start; +vm_offset_t kernel_virtual_end; + +/* + * Index into pv_head table, its lock bits, and the modify/reference + * bits. + */ +#define pa_index(pa) vm_page_table_index(pa) + +#define pai_to_pvh(pai) (&pv_head_table[pai]) +#define lock_pvh_pai(pai) (bit_lock(pai, pv_lock_table)) +#define unlock_pvh_pai(pai) (bit_unlock(pai, pv_lock_table)) + +/* + * Array of physical page attributes for managed pages. + * One byte per physical page. + */ +char *pmap_phys_attributes; + +/* + * Physical page attributes. Copy bits from PTE definition. + */ +#define PHYS_MODIFIED INTEL_PTE_MOD /* page modified */ +#define PHYS_REFERENCED INTEL_PTE_REF /* page referenced */ + +/* + * Amount of virtual memory mapped by one + * page-directory entry. + */ +#define PDE_MAPPED_SIZE (pdenum2lin(1)) + +/* + * We allocate page table pages directly from the VM system + * through this object. It maps physical memory. + */ +vm_object_t pmap_object = VM_OBJECT_NULL; + +/* + * Locking and TLB invalidation + */ + +/* + * Locking Protocols: + * + * There are two structures in the pmap module that need locking: + * the pmaps themselves, and the per-page pv_lists (which are locked + * by locking the pv_lock_table entry that corresponds to the pv_head + * for the list in question.) Most routines want to lock a pmap and + * then do operations in it that require pv_list locking -- however + * pmap_remove_all and pmap_copy_on_write operate on a physical page + * basis and want to do the locking in the reverse order, i.e. lock + * a pv_list and then go through all the pmaps referenced by that list. + * To protect against deadlock between these two cases, the pmap_lock + * is used. There are three different locking protocols as a result: + * + * 1. pmap operations only (pmap_extract, pmap_access, ...) Lock only + * the pmap. + * + * 2. pmap-based operations (pmap_enter, pmap_remove, ...) Get a read + * lock on the pmap_lock (shared read), then lock the pmap + * and finally the pv_lists as needed [i.e. pmap lock before + * pv_list lock.] + * + * 3. pv_list-based operations (pmap_remove_all, pmap_copy_on_write, ...) + * Get a write lock on the pmap_lock (exclusive write); this + * also guaranteees exclusive access to the pv_lists. Lock the + * pmaps as needed. + * + * At no time may any routine hold more than one pmap lock or more than + * one pv_list lock. Because interrupt level routines can allocate + * mbufs and cause pmap_enter's, the pmap_lock and the lock on the + * kernel_pmap can only be held at splvm. + */ + +#if NCPUS > 1 +/* + * We raise the interrupt level to splvm, to block interprocessor + * interrupts during pmap operations. We must take the CPU out of + * the cpus_active set while interrupts are blocked. + */ +#define SPLVM(spl) { \ + spl = splvm(); \ + i_bit_clear(cpu_number(), &cpus_active); \ +} + +#define SPLX(spl) { \ + i_bit_set(cpu_number(), &cpus_active); \ + splx(spl); \ +} + +/* + * Lock on pmap system + */ +lock_data_t pmap_system_lock; + +#define PMAP_READ_LOCK(pmap, spl) { \ + SPLVM(spl); \ + lock_read(&pmap_system_lock); \ + simple_lock(&(pmap)->lock); \ +} + +#define PMAP_WRITE_LOCK(spl) { \ + SPLVM(spl); \ + lock_write(&pmap_system_lock); \ +} + +#define PMAP_READ_UNLOCK(pmap, spl) { \ + simple_unlock(&(pmap)->lock); \ + lock_read_done(&pmap_system_lock); \ + SPLX(spl); \ +} + +#define PMAP_WRITE_UNLOCK(spl) { \ + lock_write_done(&pmap_system_lock); \ + SPLX(spl); \ +} + +#define PMAP_WRITE_TO_READ_LOCK(pmap) { \ + simple_lock(&(pmap)->lock); \ + lock_write_to_read(&pmap_system_lock); \ +} + +#define LOCK_PVH(index) (lock_pvh_pai(index)) + +#define UNLOCK_PVH(index) (unlock_pvh_pai(index)) + +#define PMAP_UPDATE_TLBS(pmap, s, e) \ +{ \ + cpu_set cpu_mask = 1 << cpu_number(); \ + cpu_set users; \ + \ + /* Since the pmap is locked, other updates are locked */ \ + /* out, and any pmap_activate has finished. */ \ + \ + /* find other cpus using the pmap */ \ + users = (pmap)->cpus_using & ~cpu_mask; \ + if (users) { \ + /* signal them, and wait for them to finish */ \ + /* using the pmap */ \ + signal_cpus(users, (pmap), (s), (e)); \ + while ((pmap)->cpus_using & cpus_active & ~cpu_mask) \ + cpu_pause(); \ + } \ + \ + /* invalidate our own TLB if pmap is in use */ \ + if ((pmap)->cpus_using & cpu_mask) { \ + INVALIDATE_TLB((pmap), (s), (e)); \ + } \ +} + +#else /* NCPUS > 1 */ + +#define SPLVM(spl) ((void)(spl)) +#define SPLX(spl) ((void)(spl)) + +#define PMAP_READ_LOCK(pmap, spl) SPLVM(spl) +#define PMAP_WRITE_LOCK(spl) SPLVM(spl) +#define PMAP_READ_UNLOCK(pmap, spl) SPLX(spl) +#define PMAP_WRITE_UNLOCK(spl) SPLX(spl) +#define PMAP_WRITE_TO_READ_LOCK(pmap) + +#define LOCK_PVH(index) +#define UNLOCK_PVH(index) + +#define PMAP_UPDATE_TLBS(pmap, s, e) { \ + /* invalidate our own TLB if pmap is in use */ \ + if ((pmap)->cpus_using) { \ + INVALIDATE_TLB((pmap), (s), (e)); \ + } \ +} + +#endif /* NCPUS > 1 */ + +#ifdef MACH_PV_PAGETABLES +#define INVALIDATE_TLB(pmap, s, e) do { \ + if (__builtin_constant_p((e) - (s)) \ + && (e) - (s) == PAGE_SIZE) \ + hyp_invlpg((pmap) == kernel_pmap ? kvtolin(s) : (s)); \ + else \ + hyp_mmuext_op_void(MMUEXT_TLB_FLUSH_LOCAL); \ +} while(0) +#else /* MACH_PV_PAGETABLES */ +/* It is hard to know when a TLB flush becomes less expensive than a bunch of + * invlpgs. But it surely is more expensive than just one invlpg. */ +#define INVALIDATE_TLB(pmap, s, e) do { \ + if (__builtin_constant_p((e) - (s)) \ + && (e) - (s) == PAGE_SIZE) \ + invlpg_linear((pmap) == kernel_pmap ? kvtolin(s) : (s)); \ + else \ + flush_tlb(); \ +} while (0) +#endif /* MACH_PV_PAGETABLES */ + + +#if NCPUS > 1 +/* + * Structures to keep track of pending TLB invalidations + */ + +#define UPDATE_LIST_SIZE 4 + +struct pmap_update_item { + pmap_t pmap; /* pmap to invalidate */ + vm_offset_t start; /* start address to invalidate */ + vm_offset_t end; /* end address to invalidate */ +} ; + +typedef struct pmap_update_item *pmap_update_item_t; + +/* + * List of pmap updates. If the list overflows, + * the last entry is changed to invalidate all. + */ +struct pmap_update_list { + decl_simple_lock_data(, lock) + int count; + struct pmap_update_item item[UPDATE_LIST_SIZE]; +} ; +typedef struct pmap_update_list *pmap_update_list_t; + +struct pmap_update_list cpu_update_list[NCPUS]; + +cpu_set cpus_active; +cpu_set cpus_idle; +volatile +boolean_t cpu_update_needed[NCPUS]; + +#endif /* NCPUS > 1 */ + +/* + * Other useful macros. + */ +#define current_pmap() (vm_map_pmap(current_thread()->task->map)) +#define pmap_in_use(pmap, cpu) (((pmap)->cpus_using & (1 << (cpu))) != 0) + +struct pmap kernel_pmap_store; +pmap_t kernel_pmap; + +struct kmem_cache pmap_cache; /* cache of pmap structures */ +struct kmem_cache pt_cache; /* cache of page tables */ +struct kmem_cache pd_cache; /* cache of page directories */ +#if PAE +struct kmem_cache pdpt_cache; /* cache of page directory pointer tables */ +#ifdef __x86_64__ +struct kmem_cache l4_cache; /* cache of L4 tables */ +#endif /* __x86_64__ */ +#endif /* PAE */ + +boolean_t pmap_debug = FALSE; /* flag for debugging prints */ + +#if 0 +int ptes_per_vm_page; /* number of hardware ptes needed + to map one VM page. */ +#else +#define ptes_per_vm_page 1 +#endif + +unsigned int inuse_ptepages_count = 0; /* debugging */ + +/* + * Pointer to the basic page directory for the kernel. + * Initialized by pmap_bootstrap(). + */ +pt_entry_t *kernel_page_dir; + +/* + * Two slots for temporary physical page mapping, to allow for + * physical-to-physical transfers. + */ +static pmap_mapwindow_t mapwindows[PMAP_NMAPWINDOWS * NCPUS]; +#define MAPWINDOW_SIZE (PMAP_NMAPWINDOWS * NCPUS * PAGE_SIZE) + +#ifdef __x86_64__ +static inline pt_entry_t * +pmap_l4base(const pmap_t pmap, vm_offset_t lin_addr) +{ + return &pmap->l4base[lin2l4num(lin_addr)]; +} +#endif + +#ifdef PAE +static inline pt_entry_t * +pmap_ptp(const pmap_t pmap, vm_offset_t lin_addr) +{ + pt_entry_t *pdp_table; +#ifdef __x86_64__ + pt_entry_t *l4_table; + l4_table = pmap_l4base(pmap, lin_addr); + if (l4_table == PT_ENTRY_NULL) + return(PT_ENTRY_NULL); + pt_entry_t pdp = *l4_table; + if ((pdp & INTEL_PTE_VALID) == 0) + return PT_ENTRY_NULL; + pdp_table = (pt_entry_t *) ptetokv(pdp); +#else /* __x86_64__ */ + pdp_table = pmap->pdpbase; +#endif /* __x86_64__ */ + return &pdp_table[lin2pdpnum(lin_addr)]; +} +#endif + +static inline pt_entry_t * +pmap_pde(const pmap_t pmap, vm_offset_t addr) +{ + pt_entry_t *page_dir; + if (pmap == kernel_pmap) + addr = kvtolin(addr); +#if PAE + pt_entry_t *pdp_table; + pdp_table = pmap_ptp(pmap, addr); + if (pdp_table == PT_ENTRY_NULL) + return(PT_ENTRY_NULL); + pt_entry_t pde = *pdp_table; + if ((pde & INTEL_PTE_VALID) == 0) + return PT_ENTRY_NULL; + page_dir = (pt_entry_t *) ptetokv(pde); +#else /* PAE */ + page_dir = pmap->dirbase; +#endif /* PAE */ + return &page_dir[lin2pdenum(addr)]; +} + +/* + * Given an offset and a map, compute the address of the + * pte. If the address is invalid with respect to the map + * then PT_ENTRY_NULL is returned (and the map may need to grow). + * + * This is only used internally. + */ +pt_entry_t * +pmap_pte(const pmap_t pmap, vm_offset_t addr) +{ + pt_entry_t *ptp; + pt_entry_t pte; + +#ifdef __x86_64__ + if (pmap->l4base == 0) + return(PT_ENTRY_NULL); +#elif PAE + if (pmap->pdpbase == 0) + return(PT_ENTRY_NULL); +#else + if (pmap->dirbase == 0) + return(PT_ENTRY_NULL); +#endif + ptp = pmap_pde(pmap, addr); + if (ptp == 0) + return(PT_ENTRY_NULL); + pte = *ptp; + if ((pte & INTEL_PTE_VALID) == 0) + return(PT_ENTRY_NULL); + ptp = (pt_entry_t *)ptetokv(pte); + return(&ptp[ptenum(addr)]); +} + +#define DEBUG_PTE_PAGE 0 + +#if DEBUG_PTE_PAGE +void ptep_check(ptep_t ptep) +{ + pt_entry_t *pte, *epte; + int ctu, ctw; + + /* check the use and wired counts */ + if (ptep == PTE_PAGE_NULL) + return; + pte = pmap_pte(ptep->pmap, ptep->va); + epte = pte + INTEL_PGBYTES/sizeof(pt_entry_t); + ctu = 0; + ctw = 0; + while (pte < epte) { + if (pte->pfn != 0) { + ctu++; + if (pte->wired) + ctw++; + } + pte += ptes_per_vm_page; + } + + if (ctu != ptep->use_count || ctw != ptep->wired_count) { + printf("use %d wired %d - actual use %d wired %d\n", + ptep->use_count, ptep->wired_count, ctu, ctw); + panic("pte count"); + } +} +#endif /* DEBUG_PTE_PAGE */ + +/* + * Back-door routine for mapping kernel VM at initialization. + * Useful for mapping memory outside the range of direct mapped + * physical memory (i.e., devices). + */ +vm_offset_t pmap_map_bd( + vm_offset_t virt, + phys_addr_t start, + phys_addr_t end, + vm_prot_t prot) +{ + pt_entry_t template; + pt_entry_t *pte; + int spl; +#ifdef MACH_PV_PAGETABLES + int n, i = 0; + struct mmu_update update[HYP_BATCH_MMU_UPDATES]; +#endif /* MACH_PV_PAGETABLES */ + + template = pa_to_pte(start) + | INTEL_PTE_NCACHE|INTEL_PTE_WTHRU + | INTEL_PTE_VALID; + if (CPU_HAS_FEATURE(CPU_FEATURE_PGE)) + template |= INTEL_PTE_GLOBAL; + if (prot & VM_PROT_WRITE) + template |= INTEL_PTE_WRITE; + + PMAP_READ_LOCK(kernel_pmap, spl); + while (start < end) { + pte = pmap_pte(kernel_pmap, virt); + if (pte == PT_ENTRY_NULL) + panic("pmap_map_bd: Invalid kernel address\n"); +#ifdef MACH_PV_PAGETABLES + update[i].ptr = kv_to_ma(pte); + update[i].val = pa_to_ma(template); + i++; + if (i == HYP_BATCH_MMU_UPDATES) { + hyp_mmu_update(kvtolin(&update), i, kvtolin(&n), DOMID_SELF); + if (n != i) + panic("couldn't pmap_map_bd\n"); + i = 0; + } +#else /* MACH_PV_PAGETABLES */ + WRITE_PTE(pte, template) +#endif /* MACH_PV_PAGETABLES */ + pte_increment_pa(template); + virt += PAGE_SIZE; + start += PAGE_SIZE; + } +#ifdef MACH_PV_PAGETABLES + if (i > HYP_BATCH_MMU_UPDATES) + panic("overflowed array in pmap_map_bd"); + hyp_mmu_update(kvtolin(&update), i, kvtolin(&n), DOMID_SELF); + if (n != i) + panic("couldn't pmap_map_bd\n"); +#endif /* MACH_PV_PAGETABLES */ + PMAP_READ_UNLOCK(kernel_pmap, spl); + return(virt); +} + +#ifdef PAE +static void pmap_bootstrap_pae(void) +{ + vm_offset_t addr; + pt_entry_t *pdp_kernel; + +#ifdef __x86_64__ +#ifdef MACH_HYP + kernel_pmap->user_l4base = NULL; + kernel_pmap->user_pdpbase = NULL; +#endif + kernel_pmap->l4base = (pt_entry_t*)phystokv(pmap_grab_page()); + memset(kernel_pmap->l4base, 0, INTEL_PGBYTES); +#else + const int PDPNUM_KERNEL = PDPNUM; +#endif /* x86_64 */ + + init_alloc_aligned(PDPNUM_KERNEL * INTEL_PGBYTES, &addr); + kernel_page_dir = (pt_entry_t*)phystokv(addr); + memset(kernel_page_dir, 0, PDPNUM_KERNEL * INTEL_PGBYTES); + + pdp_kernel = (pt_entry_t*)phystokv(pmap_grab_page()); + memset(pdp_kernel, 0, INTEL_PGBYTES); + for (int i = 0; i < PDPNUM_KERNEL; i++) { + int pdp_index = i; +#ifdef __x86_64__ + pdp_index += lin2pdpnum(VM_MIN_KERNEL_ADDRESS); +#endif + WRITE_PTE(&pdp_kernel[pdp_index], + pa_to_pte(_kvtophys((void *) kernel_page_dir + + i * INTEL_PGBYTES)) + | INTEL_PTE_VALID +#if (defined(__x86_64__) && !defined(MACH_HYP)) || defined(MACH_PV_PAGETABLES) + | INTEL_PTE_WRITE +#endif + ); + } + +#ifdef __x86_64__ + /* only fill the kernel pdpte during bootstrap */ + WRITE_PTE(&kernel_pmap->l4base[lin2l4num(VM_MIN_KERNEL_ADDRESS)], + pa_to_pte(_kvtophys(pdp_kernel)) | INTEL_PTE_VALID | INTEL_PTE_WRITE); +#ifdef MACH_PV_PAGETABLES + pmap_set_page_readonly_init(kernel_pmap->l4base); +#endif /* MACH_PV_PAGETABLES */ +#else /* x86_64 */ + kernel_pmap->pdpbase = pdp_kernel; +#endif /* x86_64 */ +} +#endif /* PAE */ + +#ifdef MACH_PV_PAGETABLES +#ifdef PAE +#define NSUP_L1 4 +#else +#define NSUP_L1 1 +#endif +static void pmap_bootstrap_xen(pt_entry_t *l1_map[NSUP_L1]) +{ + /* We don't actually deal with the CR3 register content at all */ + hyp_vm_assist(VMASST_CMD_enable, VMASST_TYPE_pae_extended_cr3); + /* + * Xen may only provide as few as 512KB extra bootstrap linear memory, + * which is far from enough to map all available memory, so we need to + * map more bootstrap linear memory. We here map 1 (resp. 4 for PAE) + * other L1 table(s), thus 4MiB extra memory (resp. 8MiB), which is + * enough for a pagetable mapping 4GiB. + */ + vm_offset_t la; + int n_l1map; + for (n_l1map = 0, la = VM_MIN_KERNEL_ADDRESS; la >= VM_MIN_KERNEL_ADDRESS; la += NPTES * PAGE_SIZE) { + pt_entry_t *base = (pt_entry_t*) boot_info.pt_base; +#ifdef PAE +#ifdef __x86_64__ + base = (pt_entry_t*) ptetokv(base[0]); +#endif /* x86_64 */ + pt_entry_t *l2_map = (pt_entry_t*) ptetokv(base[lin2pdpnum(la)]); +#else /* PAE */ + pt_entry_t *l2_map = base; +#endif /* PAE */ + /* Like lin2pdenum, but works with non-contiguous boot L3 */ + l2_map += (la >> PDESHIFT) & PDEMASK; + if (!(*l2_map & INTEL_PTE_VALID)) { + struct mmu_update update; + unsigned j, n; + + l1_map[n_l1map] = (pt_entry_t*) phystokv(pmap_grab_page()); + for (j = 0; j < NPTES; j++) + l1_map[n_l1map][j] = (((pt_entry_t)pfn_to_mfn(lin2pdenum(la - VM_MIN_KERNEL_ADDRESS) * NPTES + j)) << PAGE_SHIFT) | INTEL_PTE_VALID | INTEL_PTE_WRITE; + pmap_set_page_readonly_init(l1_map[n_l1map]); + if (!hyp_mmuext_op_mfn (MMUEXT_PIN_L1_TABLE, kv_to_mfn (l1_map[n_l1map]))) + panic("couldn't pin page %p(%lx)", l1_map[n_l1map], (vm_offset_t) kv_to_ma (l1_map[n_l1map])); + update.ptr = kv_to_ma(l2_map); + update.val = kv_to_ma(l1_map[n_l1map]) | INTEL_PTE_VALID | INTEL_PTE_WRITE; + hyp_mmu_update(kv_to_la(&update), 1, kv_to_la(&n), DOMID_SELF); + if (n != 1) + panic("couldn't complete bootstrap map"); + /* added the last L1 table, can stop */ + if (++n_l1map >= NSUP_L1) + break; + } + } +} +#endif /* MACH_PV_PAGETABLES */ + +/* + * Bootstrap the system enough to run with virtual memory. + * Allocate the kernel page directory and page tables, + * and direct-map all physical memory. + * Called with mapping off. + */ +void pmap_bootstrap(void) +{ + /* + * Mapping is turned off; we must reference only physical addresses. + * The load image of the system is to be mapped 1-1 physical = virtual. + */ + + /* + * Set ptes_per_vm_page for general use. + */ +#if 0 + ptes_per_vm_page = PAGE_SIZE / INTEL_PGBYTES; +#endif + + /* + * 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; + +#if NCPUS > 1 + lock_init(&pmap_system_lock, FALSE); /* NOT a sleep lock */ +#endif /* NCPUS > 1 */ + + simple_lock_init(&kernel_pmap->lock); + + kernel_pmap->ref_count = 1; + + /* + * Determine the kernel virtual address range. + * It starts at the end of the physical memory + * mapped into the kernel address space, + * and extends to a stupid arbitrary limit beyond that. + */ + kernel_virtual_start = phystokv(biosmem_directmap_end()); + kernel_virtual_end = kernel_virtual_start + VM_KERNEL_MAP_SIZE; + + if (kernel_virtual_end < kernel_virtual_start + || kernel_virtual_end > VM_MAX_KERNEL_ADDRESS - PAGE_SIZE) + kernel_virtual_end = VM_MAX_KERNEL_ADDRESS - PAGE_SIZE; + + /* + * Allocate and clear a kernel page directory. + */ + /* Note: initial Xen mapping holds at least 512kB free mapped page. + * We use that for directly building our linear mapping. */ +#if PAE + pmap_bootstrap_pae(); +#else /* PAE */ + kernel_pmap->dirbase = kernel_page_dir = (pt_entry_t*)phystokv(pmap_grab_page()); + { + unsigned i; + for (i = 0; i < NPDES; i++) + kernel_page_dir[i] = 0; + } +#endif /* PAE */ + +#ifdef MACH_PV_PAGETABLES + pt_entry_t *l1_map[NSUP_L1]; + pmap_bootstrap_xen(l1_map); +#endif /* MACH_PV_PAGETABLES */ + + /* + * Allocate and set up the kernel page tables. + */ + { + vm_offset_t va; + pt_entry_t global = CPU_HAS_FEATURE(CPU_FEATURE_PGE) ? INTEL_PTE_GLOBAL : 0; + + /* + * Map virtual memory for all directly mappable physical memory, 1-1, + * Make any mappings completely in the kernel's text segment read-only. + * + * Also allocate some additional all-null page tables afterwards + * for kernel virtual memory allocation, + * because this PMAP module is too stupid + * to allocate new kernel page tables later. + * XX fix this + */ + for (va = phystokv(0); va >= phystokv(0) && va < kernel_virtual_end; ) + { + pt_entry_t *pde = kernel_page_dir + lin2pdenum_cont(kvtolin(va)); + pt_entry_t *ptable = (pt_entry_t*)phystokv(pmap_grab_page()); + pt_entry_t *pte; + + /* Initialize the page directory entry. */ + WRITE_PTE(pde, pa_to_pte((vm_offset_t)_kvtophys(ptable)) + | INTEL_PTE_VALID | INTEL_PTE_WRITE); + + /* Initialize the page table. */ + for (pte = ptable; (va < phystokv(biosmem_directmap_end())) && (pte < ptable+NPTES); pte++) + { + if ((pte - ptable) < ptenum(va)) + { + WRITE_PTE(pte, 0); + } + else +#ifdef MACH_PV_PAGETABLES + if (va == (vm_offset_t) &hyp_shared_info) + { + *pte = boot_info.shared_info | INTEL_PTE_VALID | INTEL_PTE_WRITE; + va += INTEL_PGBYTES; + } + else +#endif /* MACH_PV_PAGETABLES */ + { + extern char _start[], etext[]; + + if (((va >= (vm_offset_t) _start) + && (va + INTEL_PGBYTES <= (vm_offset_t)etext)) +#ifdef MACH_PV_PAGETABLES + || (va >= (vm_offset_t) boot_info.pt_base + && (va + INTEL_PGBYTES <= + (vm_offset_t) ptable + INTEL_PGBYTES)) +#endif /* MACH_PV_PAGETABLES */ + ) + { + WRITE_PTE(pte, pa_to_pte(_kvtophys(va)) + | INTEL_PTE_VALID | global); + } + else + { +#ifdef MACH_PV_PAGETABLES + /* Keep supplementary L1 pages read-only */ + int i; + for (i = 0; i < NSUP_L1; i++) + if (va == (vm_offset_t) l1_map[i]) { + WRITE_PTE(pte, pa_to_pte(_kvtophys(va)) + | INTEL_PTE_VALID | global); + break; + } + if (i == NSUP_L1) +#endif /* MACH_PV_PAGETABLES */ + WRITE_PTE(pte, pa_to_pte(_kvtophys(va)) + | INTEL_PTE_VALID | INTEL_PTE_WRITE | global) + + } + va += INTEL_PGBYTES; + } + } + for (; pte < ptable+NPTES; pte++) + { + if (va >= kernel_virtual_end - MAPWINDOW_SIZE && va < kernel_virtual_end) + { + pmap_mapwindow_t *win = &mapwindows[atop(va - (kernel_virtual_end - MAPWINDOW_SIZE))]; + win->entry = pte; + win->vaddr = va; + } + WRITE_PTE(pte, 0); + va += INTEL_PGBYTES; + } +#ifdef MACH_PV_PAGETABLES + pmap_set_page_readonly_init(ptable); + if (!hyp_mmuext_op_mfn (MMUEXT_PIN_L1_TABLE, kv_to_mfn (ptable))) + panic("couldn't pin page %p(%lx)\n", ptable, (vm_offset_t) kv_to_ma (ptable)); +#endif /* MACH_PV_PAGETABLES */ + } + } + + /* Architecture-specific code will turn on paging + soon after we return from here. */ +} + +#ifdef MACH_PV_PAGETABLES +/* These are only required because of Xen security policies */ + +/* Set back a page read write */ +void pmap_set_page_readwrite(void *_vaddr) { + vm_offset_t vaddr = (vm_offset_t) _vaddr; + phys_addr_t paddr = kvtophys(vaddr); + vm_offset_t canon_vaddr = phystokv(paddr); + if (hyp_do_update_va_mapping (kvtolin(vaddr), pa_to_pte (pa_to_ma(paddr)) | INTEL_PTE_VALID | INTEL_PTE_WRITE, UVMF_NONE)) + panic("couldn't set hiMMU readwrite for addr %lx(%lx)\n", vaddr, (vm_offset_t) pa_to_ma (paddr)); + if (canon_vaddr != vaddr) + if (hyp_do_update_va_mapping (kvtolin(canon_vaddr), pa_to_pte (pa_to_ma(paddr)) | INTEL_PTE_VALID | INTEL_PTE_WRITE, UVMF_NONE)) + panic("couldn't set hiMMU readwrite for paddr %lx(%lx)\n", canon_vaddr, (vm_offset_t) pa_to_ma (paddr)); +} + +/* Set a page read only (so as to pin it for instance) */ +void pmap_set_page_readonly(void *_vaddr) { + vm_offset_t vaddr = (vm_offset_t) _vaddr; + phys_addr_t paddr = kvtophys(vaddr); + vm_offset_t canon_vaddr = phystokv(paddr); + if (*pmap_pde(kernel_pmap, vaddr) & INTEL_PTE_VALID) { + if (hyp_do_update_va_mapping (kvtolin(vaddr), pa_to_pte (pa_to_ma(paddr)) | INTEL_PTE_VALID, UVMF_NONE)) + panic("couldn't set hiMMU readonly for vaddr %lx(%lx)\n", vaddr, (vm_offset_t) pa_to_ma (paddr)); + } + if (canon_vaddr != vaddr && + *pmap_pde(kernel_pmap, canon_vaddr) & INTEL_PTE_VALID) { + if (hyp_do_update_va_mapping (kvtolin(canon_vaddr), pa_to_pte (pa_to_ma(paddr)) | INTEL_PTE_VALID, UVMF_NONE)) + panic("couldn't set hiMMU readonly for vaddr %lx canon_vaddr %lx paddr %lx (%lx)\n", vaddr, canon_vaddr, paddr, (vm_offset_t) pa_to_ma (paddr)); + } +} + +/* This needs to be called instead of pmap_set_page_readonly as long as RC3 + * still points to the bootstrap dirbase, to also fix the bootstrap table. */ +void pmap_set_page_readonly_init(void *_vaddr) { + vm_offset_t vaddr = (vm_offset_t) _vaddr; +#if PAE + pt_entry_t *pdpbase = (void*) boot_info.pt_base; +#ifdef __x86_64__ + pdpbase = (pt_entry_t *) ptetokv(pdpbase[lin2l4num(vaddr)]); +#endif + /* The bootstrap table does not necessarily use contiguous pages for the pde tables */ + pt_entry_t *dirbase = (void*) ptetokv(pdpbase[lin2pdpnum(vaddr)]); +#else + pt_entry_t *dirbase = (void*) boot_info.pt_base; +#endif + pt_entry_t *pte = &dirbase[lin2pdenum(vaddr) & PTEMASK]; + /* Modify our future kernel map (can't use update_va_mapping for this)... */ + if (*pmap_pde(kernel_pmap, vaddr) & INTEL_PTE_VALID) { + if (!hyp_mmu_update_la (kvtolin(vaddr), pa_to_pte (kv_to_ma(vaddr)) | INTEL_PTE_VALID)) + panic("couldn't set hiMMU readonly for vaddr %lx(%lx)\n", vaddr, (vm_offset_t) kv_to_ma (vaddr)); + } + /* ... and the bootstrap map. */ + if (*pte & INTEL_PTE_VALID) { + if (hyp_do_update_va_mapping (vaddr, pa_to_pte (kv_to_ma(vaddr)) | INTEL_PTE_VALID, UVMF_NONE)) + panic("couldn't set MMU readonly for vaddr %lx(%lx)\n", vaddr, (vm_offset_t) kv_to_ma (vaddr)); + } +} + +void pmap_clear_bootstrap_pagetable(pt_entry_t *base) { + unsigned i; + pt_entry_t *dir; + vm_offset_t va = 0; +#ifdef __x86_64__ + int l4i, l3i; +#else +#if PAE + unsigned j; +#endif /* PAE */ +#endif + if (!hyp_mmuext_op_mfn (MMUEXT_UNPIN_TABLE, kv_to_mfn(base))) + panic("pmap_clear_bootstrap_pagetable: couldn't unpin page %p(%lx)\n", base, (vm_offset_t) kv_to_ma(base)); +#ifdef __x86_64__ + /* 4-level page table */ + for (l4i = 0; l4i < NPTES && va < HYP_VIRT_START && va < 0x0000800000000000UL; l4i++) { + pt_entry_t l4e = base[l4i]; + pt_entry_t *l3; + if (!(l4e & INTEL_PTE_VALID)) { + va += NPTES * NPTES * NPTES * INTEL_PGBYTES; + continue; + } + l3 = (pt_entry_t *) ptetokv(l4e); + + for (l3i = 0; l3i < NPTES && va < HYP_VIRT_START; l3i++) { + pt_entry_t l3e = l3[l3i]; + if (!(l3e & INTEL_PTE_VALID)) { + va += NPTES * NPTES * INTEL_PGBYTES; + continue; + } + dir = (pt_entry_t *) ptetokv(l3e); +#else +#if PAE + /* 3-level page table */ + for (j = 0; j < PDPNUM && va < HYP_VIRT_START; j++) + { + pt_entry_t pdpe = base[j]; + if (!(pdpe & INTEL_PTE_VALID)) { + va += NPTES * NPTES * INTEL_PGBYTES; + continue; + } + dir = (pt_entry_t *) ptetokv(pdpe); +#else /* PAE */ + /* 2-level page table */ + dir = base; +#endif /* PAE */ +#endif + for (i = 0; i < NPTES && va < HYP_VIRT_START; i++) { + pt_entry_t pde = dir[i]; + unsigned long pfn = atop(pte_to_pa(pde)); + void *pgt = (void*) phystokv(ptoa(pfn)); + if (pde & INTEL_PTE_VALID) + hyp_free_page(pfn, pgt); + va += NPTES * INTEL_PGBYTES; + } +#ifndef __x86_64__ +#if PAE + hyp_free_page(atop(_kvtophys(dir)), dir); + } +#endif /* PAE */ +#else + hyp_free_page(atop(_kvtophys(dir)), dir); + } + hyp_free_page(atop(_kvtophys(l3)), l3); + } +#endif + hyp_free_page(atop(_kvtophys(base)), base); +} +#endif /* MACH_PV_PAGETABLES */ + +/* + * Create a temporary mapping for a given physical entry + * + * This can be used to access physical pages which are not mapped 1:1 by + * phystokv(). + */ +pmap_mapwindow_t *pmap_get_mapwindow(pt_entry_t entry) +{ + pmap_mapwindow_t *map; + int cpu = cpu_number(); + + assert(entry != 0); + + /* Find an empty one. */ + for (map = &mapwindows[cpu * PMAP_NMAPWINDOWS]; map < &mapwindows[(cpu+1) * PMAP_NMAPWINDOWS]; map++) + if (!(*map->entry)) + break; + assert(map < &mapwindows[(cpu+1) * PMAP_NMAPWINDOWS]); + +#ifdef MACH_PV_PAGETABLES + if (!hyp_mmu_update_pte(kv_to_ma(map->entry), pa_to_ma(entry))) + panic("pmap_get_mapwindow"); +#else /* MACH_PV_PAGETABLES */ + WRITE_PTE(map->entry, entry); +#endif /* MACH_PV_PAGETABLES */ + INVALIDATE_TLB(kernel_pmap, map->vaddr, map->vaddr + PAGE_SIZE); + return map; +} + +/* + * Destroy a temporary mapping for a physical entry + */ +void pmap_put_mapwindow(pmap_mapwindow_t *map) +{ +#ifdef MACH_PV_PAGETABLES + if (!hyp_mmu_update_pte(kv_to_ma(map->entry), 0)) + panic("pmap_put_mapwindow"); +#else /* MACH_PV_PAGETABLES */ + WRITE_PTE(map->entry, 0); +#endif /* MACH_PV_PAGETABLES */ + INVALIDATE_TLB(kernel_pmap, map->vaddr, map->vaddr + PAGE_SIZE); +} + +void pmap_virtual_space( + vm_offset_t *startp, + vm_offset_t *endp) +{ + *startp = kernel_virtual_start; + *endp = kernel_virtual_end - MAPWINDOW_SIZE; +} + +/* + * Initialize the pmap module. + * Called by vm_init, to initialize any structures that the pmap + * system needs to map virtual memory. + */ +void pmap_init(void) +{ + unsigned long npages; + vm_offset_t addr; + vm_size_t s; +#if NCPUS > 1 + int i; +#endif /* NCPUS > 1 */ + + /* + * Allocate memory for the pv_head_table and its lock bits, + * the modify bit array, and the pte_page table. + */ + + npages = vm_page_table_size(); + s = (vm_size_t) (sizeof(struct pv_entry) * npages + + pv_lock_table_size(npages) + + npages); + + s = round_page(s); + if (kmem_alloc_wired(kernel_map, &addr, s) != KERN_SUCCESS) + panic("pmap_init"); + memset((void *) addr, 0, s); + + /* + * Allocate the structures first to preserve word-alignment. + */ + pv_head_table = (pv_entry_t) addr; + addr = (vm_offset_t) (pv_head_table + npages); + + pv_lock_table = (char *) addr; + addr = (vm_offset_t) (pv_lock_table + pv_lock_table_size(npages)); + + pmap_phys_attributes = (char *) addr; + + /* + * Create the cache of physical maps, + * and of the physical-to-virtual entries. + */ + s = (vm_size_t) sizeof(struct pmap); + kmem_cache_init(&pmap_cache, "pmap", s, 0, NULL, 0); + kmem_cache_init(&pt_cache, "pmap_L1", + INTEL_PGBYTES, INTEL_PGBYTES, NULL, + KMEM_CACHE_PHYSMEM); + kmem_cache_init(&pd_cache, "pmap_L2", + INTEL_PGBYTES, INTEL_PGBYTES, NULL, + KMEM_CACHE_PHYSMEM); +#if PAE + kmem_cache_init(&pdpt_cache, "pmap_L3", + INTEL_PGBYTES, INTEL_PGBYTES, NULL, + KMEM_CACHE_PHYSMEM); +#ifdef __x86_64__ + kmem_cache_init(&l4_cache, "pmap_L4", + INTEL_PGBYTES, INTEL_PGBYTES, NULL, + KMEM_CACHE_PHYSMEM); +#endif /* __x86_64__ */ +#endif /* PAE */ + s = (vm_size_t) sizeof(struct pv_entry); + kmem_cache_init(&pv_list_cache, "pv_entry", s, 0, NULL, 0); + +#if NCPUS > 1 + /* + * Set up the pmap request lists + */ + for (i = 0; i < NCPUS; i++) { + pmap_update_list_t up = &cpu_update_list[i]; + + simple_lock_init(&up->lock); + up->count = 0; + } +#endif /* NCPUS > 1 */ + + /* + * Indicate that the PMAP module is now fully initialized. + */ + pmap_initialized = TRUE; +} + +static inline boolean_t +valid_page(phys_addr_t addr) +{ + struct vm_page *p; + + if (!pmap_initialized) + return FALSE; + + p = vm_page_lookup_pa(addr); + return (p != NULL); +} + +/* + * Routine: pmap_page_table_page_alloc + * + * Allocates a new physical page to be used as a page-table page. + * + * Must be called with the pmap system and the pmap unlocked, + * since these must be unlocked to use vm_page_grab. + */ +static vm_offset_t +pmap_page_table_page_alloc(void) +{ + vm_page_t m; + phys_addr_t pa; + + check_simple_locks(); + + /* + * We cannot allocate the pmap_object in pmap_init, + * because it is called before the cache package is up. + * Allocate it now if it is missing. + */ + if (pmap_object == VM_OBJECT_NULL) + pmap_object = vm_object_allocate(vm_page_table_size() * PAGE_SIZE); + + /* + * Allocate a VM page for the level 2 page table entries. + */ + while ((m = vm_page_grab(VM_PAGE_DIRECTMAP)) == VM_PAGE_NULL) + VM_PAGE_WAIT((void (*)()) 0); + + /* + * Map the page to its physical address so that it + * can be found later. + */ + pa = m->phys_addr; + assert(pa == (vm_offset_t) pa); + vm_object_lock(pmap_object); + vm_page_insert(m, pmap_object, pa); + vm_page_lock_queues(); + vm_page_wire(m); + inuse_ptepages_count++; + vm_page_unlock_queues(); + vm_object_unlock(pmap_object); + + /* + * Zero the page. + */ + memset((void *)phystokv(pa), 0, PAGE_SIZE); + + return pa; +} + +#ifdef MACH_XEN +void pmap_map_mfn(void *_addr, unsigned long mfn) { + vm_offset_t addr = (vm_offset_t) _addr; + pt_entry_t *pte, *pdp; + vm_offset_t ptp; + pt_entry_t ma = ((pt_entry_t) mfn) << PAGE_SHIFT; + + /* Add a ptp if none exist yet for this pte */ + if ((pte = pmap_pte(kernel_pmap, addr)) == PT_ENTRY_NULL) { + ptp = phystokv(pmap_page_table_page_alloc()); +#ifdef MACH_PV_PAGETABLES + pmap_set_page_readonly((void*) ptp); + if (!hyp_mmuext_op_mfn (MMUEXT_PIN_L1_TABLE, pa_to_mfn(ptp))) + panic("couldn't pin page %lx(%lx)\n",ptp,(vm_offset_t) kv_to_ma(ptp)); +#endif /* MACH_PV_PAGETABLES */ + pdp = pmap_pde(kernel_pmap, addr); + +#ifdef MACH_PV_PAGETABLES + if (!hyp_mmu_update_pte(kv_to_ma(pdp), + pa_to_pte(kv_to_ma(ptp)) | INTEL_PTE_VALID + | INTEL_PTE_USER + | INTEL_PTE_WRITE)) + panic("%s:%d could not set pde %llx(%lx) to %lx(%lx)\n",__FILE__,__LINE__,kvtophys((vm_offset_t)pdp),(vm_offset_t) kv_to_ma(pdp), ptp, (vm_offset_t) pa_to_ma(ptp)); +#else /* MACH_PV_PAGETABLES */ + *pdp = pa_to_pte(kvtophys(ptp)) | INTEL_PTE_VALID + | INTEL_PTE_USER + | INTEL_PTE_WRITE; +#endif /* MACH_PV_PAGETABLES */ + pte = pmap_pte(kernel_pmap, addr); + } + +#ifdef MACH_PV_PAGETABLES + if (!hyp_mmu_update_pte(kv_to_ma(pte), ma | INTEL_PTE_VALID | INTEL_PTE_WRITE)) + panic("%s:%d could not set pte %p(%lx) to %llx(%llx)\n",__FILE__,__LINE__,pte,(vm_offset_t) kv_to_ma(pte), ma, ma_to_pa(ma)); +#else /* MACH_PV_PAGETABLES */ + /* Note: in this case, mfn is actually a pfn. */ + WRITE_PTE(pte, ma | INTEL_PTE_VALID | INTEL_PTE_WRITE); +#endif /* MACH_PV_PAGETABLES */ +} +#endif /* MACH_XEN */ + +/* + * Deallocate a page-table page. + * The page-table page must have all mappings removed, + * and be removed from its page directory. + */ +static void +pmap_page_table_page_dealloc(vm_offset_t pa) +{ + vm_page_t m; + + vm_object_lock(pmap_object); + m = vm_page_lookup(pmap_object, pa); + vm_page_lock_queues(); +#ifdef MACH_PV_PAGETABLES + if (!hyp_mmuext_op_mfn (MMUEXT_UNPIN_TABLE, pa_to_mfn(pa))) + panic("couldn't unpin page %llx(%lx)\n", pa, (vm_offset_t) kv_to_ma(pa)); + pmap_set_page_readwrite((void*) phystokv(pa)); +#endif /* MACH_PV_PAGETABLES */ + vm_page_free(m); + inuse_ptepages_count--; + vm_page_unlock_queues(); + vm_object_unlock(pmap_object); +} + +/* + * 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. + */ +pmap_t pmap_create(vm_size_t size) +{ +#ifdef __x86_64__ + // needs to be reworked if we want to dynamically allocate PDPs for kernel + const int PDPNUM = PDPNUM_KERNEL; +#endif + pt_entry_t *page_dir[PDPNUM]; + int i; + pmap_t p; + pmap_statistics_t stats; + + /* + * A software use-only map doesn't even need a map. + */ + + if (size != 0) { + return(PMAP_NULL); + } + +/* + * Allocate a pmap struct from the pmap_cache. Then allocate + * the page descriptor table. + */ + + p = (pmap_t) kmem_cache_alloc(&pmap_cache); + if (p == PMAP_NULL) + return PMAP_NULL; + + for (i = 0; i < PDPNUM; i++) { + page_dir[i] = (pt_entry_t *) kmem_cache_alloc(&pd_cache); + if (page_dir[i] == NULL) { + i -= 1; + while (i >= 0) { + kmem_cache_free(&pd_cache, + (vm_address_t) page_dir[i]); + i -= 1; + } + kmem_cache_free(&pmap_cache, (vm_address_t) p); + return PMAP_NULL; + } + memcpy(page_dir[i], + (void *) kernel_page_dir + i * INTEL_PGBYTES, + INTEL_PGBYTES); + } + +#ifdef LINUX_DEV +#if VM_MIN_KERNEL_ADDRESS != 0 + /* Do not map BIOS in user tasks */ + page_dir +#if PAE + [lin2pdpnum(LINEAR_MIN_KERNEL_ADDRESS - VM_MIN_KERNEL_ADDRESS)] +#else + [0] +#endif + [lin2pdenum(LINEAR_MIN_KERNEL_ADDRESS - VM_MIN_KERNEL_ADDRESS)] + = 0; +#endif +#endif /* LINUX_DEV */ + +#ifdef MACH_PV_PAGETABLES + { + for (i = 0; i < PDPNUM; i++) + pmap_set_page_readonly((void *) page_dir[i]); + } +#endif /* MACH_PV_PAGETABLES */ + +#if PAE + pt_entry_t *pdp_kernel = (pt_entry_t *) kmem_cache_alloc(&pdpt_cache); + if (pdp_kernel == NULL) { + for (i = 0; i < PDPNUM; i++) + kmem_cache_free(&pd_cache, (vm_address_t) page_dir[i]); + kmem_cache_free(&pmap_cache, (vm_address_t) p); + return PMAP_NULL; + } + + memset(pdp_kernel, 0, INTEL_PGBYTES); + { + for (i = 0; i < PDPNUM; i++) { + int pdp_index = i; +#ifdef __x86_64__ + pdp_index += lin2pdpnum(VM_MIN_KERNEL_ADDRESS); +#endif + WRITE_PTE(&pdp_kernel[pdp_index], + pa_to_pte(kvtophys((vm_offset_t) page_dir[i])) + | INTEL_PTE_VALID +#if (defined(__x86_64__) && !defined(MACH_HYP)) || defined(MACH_PV_PAGETABLES) + | INTEL_PTE_WRITE +#ifdef __x86_64__ + | INTEL_PTE_USER +#endif /* __x86_64__ */ +#endif + ); + } + } +#ifdef __x86_64__ + p->l4base = (pt_entry_t *) kmem_cache_alloc(&l4_cache); + if (p->l4base == NULL) + panic("pmap_create"); + memset(p->l4base, 0, INTEL_PGBYTES); + WRITE_PTE(&p->l4base[lin2l4num(VM_MIN_KERNEL_ADDRESS)], + pa_to_pte(kvtophys((vm_offset_t) pdp_kernel)) | INTEL_PTE_VALID | INTEL_PTE_WRITE); +#ifdef MACH_PV_PAGETABLES + // FIXME: use kmem_cache_alloc instead + if (kmem_alloc_wired(kernel_map, + (vm_offset_t *)&p->user_pdpbase, INTEL_PGBYTES) + != KERN_SUCCESS) + panic("pmap_create"); + memset(p->user_pdpbase, 0, INTEL_PGBYTES); + { + int i; + for (i = 0; i < lin2pdpnum(VM_MAX_USER_ADDRESS); i++) + WRITE_PTE(&p->user_pdpbase[i], pa_to_pte(kvtophys((vm_offset_t) page_dir[i])) | INTEL_PTE_VALID | INTEL_PTE_WRITE); + } + // FIXME: use kmem_cache_alloc instead + if (kmem_alloc_wired(kernel_map, + (vm_offset_t *)&p->user_l4base, INTEL_PGBYTES) + != KERN_SUCCESS) + panic("pmap_create"); + memset(p->user_l4base, 0, INTEL_PGBYTES); + WRITE_PTE(&p->user_l4base[0], pa_to_pte(kvtophys((vm_offset_t) p->user_pdpbase)) | INTEL_PTE_VALID | INTEL_PTE_WRITE); +#endif /* MACH_PV_PAGETABLES */ +#else /* _x86_64 */ + p->pdpbase = pdp_kernel; +#endif /* _x86_64 */ +#ifdef MACH_PV_PAGETABLES +#ifdef __x86_64__ + pmap_set_page_readonly(p->l4base); + pmap_set_page_readonly(p->user_l4base); + pmap_set_page_readonly(p->user_pdpbase); +#else + pmap_set_page_readonly(p->pdpbase); +#endif +#endif /* MACH_PV_PAGETABLES */ +#else /* PAE */ + p->dirbase = page_dir[0]; +#endif /* PAE */ + + p->ref_count = 1; + + simple_lock_init(&p->lock); + p->cpus_using = 0; + + /* + * Initialize statistics. + */ + + stats = &p->stats; + stats->resident_count = 0; + stats->wired_count = 0; + + return(p); +} + +/* + * Retire the given physical map from service. + * Should only be called if the map contains + * no valid mappings. + */ + +void pmap_destroy(pmap_t p) +{ + int c, s; + + if (p == PMAP_NULL) + return; + + SPLVM(s); + simple_lock(&p->lock); + c = --p->ref_count; + simple_unlock(&p->lock); + SPLX(s); + + if (c != 0) { + return; /* still in use */ + } + + /* + * Free the page table tree. + */ +#if PAE +#ifdef __x86_64__ + for (int l4i = 0; l4i < NPTES; l4i++) { + pt_entry_t pdp = (pt_entry_t) p->l4base[l4i]; + if (!(pdp & INTEL_PTE_VALID)) + continue; + pt_entry_t *pdpbase = (pt_entry_t*) ptetokv(pdp); +#else /* __x86_64__ */ + pt_entry_t *pdpbase = p->pdpbase; +#endif /* __x86_64__ */ + for (int l3i = 0; l3i < NPTES; l3i++) { + pt_entry_t pde = (pt_entry_t) pdpbase[l3i]; + if (!(pde & INTEL_PTE_VALID)) + continue; + pt_entry_t *pdebase = (pt_entry_t*) ptetokv(pde); + if ( +#ifdef __x86_64__ + l4i < lin2l4num(VM_MAX_USER_ADDRESS) || + (l4i == lin2l4num(VM_MAX_USER_ADDRESS) && l3i < lin2pdpnum(VM_MAX_USER_ADDRESS)) +#else /* __x86_64__ */ + l3i < lin2pdpnum(VM_MAX_USER_ADDRESS) +#endif /* __x86_64__ */ + ) + for (int l2i = 0; l2i < NPTES; l2i++) +#else /* PAE */ + pt_entry_t *pdebase = p->dirbase; + for (int l2i = 0; l2i < lin2pdenum(VM_MAX_USER_ADDRESS); l2i++) +#endif /* PAE */ + { + pt_entry_t pte = (pt_entry_t) pdebase[l2i]; + if (!(pte & INTEL_PTE_VALID)) + continue; + kmem_cache_free(&pt_cache, (vm_offset_t)ptetokv(pte)); + } + kmem_cache_free(&pd_cache, (vm_offset_t)pdebase); +#if PAE + } + kmem_cache_free(&pdpt_cache, (vm_offset_t)pdpbase); +#ifdef __x86_64__ + } + kmem_cache_free(&l4_cache, (vm_offset_t) p->l4base); +#endif /* __x86_64__ */ +#endif /* PAE */ + + /* Finally, free the pmap itself */ + kmem_cache_free(&pmap_cache, (vm_offset_t) p); +} + +/* + * Add a reference to the specified pmap. + */ + +void pmap_reference(pmap_t p) +{ + int s; + if (p != PMAP_NULL) { + SPLVM(s); + simple_lock(&p->lock); + p->ref_count++; + simple_unlock(&p->lock); + SPLX(s); + } +} + +/* + * Remove a range of hardware page-table entries. + * The entries given are the first (inclusive) + * and last (exclusive) entries for the VM pages. + * The virtual address is the va for the first pte. + * + * The pmap must be locked. + * If the pmap is not the kernel pmap, the range must lie + * entirely within one pte-page. This is NOT checked. + * Assumes that the pte-page exists. + */ + +static +void pmap_remove_range( + pmap_t pmap, + vm_offset_t va, + pt_entry_t *spte, + pt_entry_t *epte) +{ + pt_entry_t *cpte; + unsigned long num_removed, num_unwired; + unsigned long pai; + phys_addr_t pa; +#ifdef MACH_PV_PAGETABLES + int n, ii = 0; + struct mmu_update update[HYP_BATCH_MMU_UPDATES]; +#endif /* MACH_PV_PAGETABLES */ + + if (pmap == kernel_pmap && (va < kernel_virtual_start || va + (epte-spte)*PAGE_SIZE > kernel_virtual_end)) + panic("pmap_remove_range(%lx-%lx) falls in physical memory area!\n", (unsigned long) va, (unsigned long) va + (epte-spte)*PAGE_SIZE); + +#if DEBUG_PTE_PAGE + if (pmap != kernel_pmap) + ptep_check(get_pte_page(spte)); +#endif /* DEBUG_PTE_PAGE */ + num_removed = 0; + num_unwired = 0; + + for (cpte = spte; cpte < epte; + cpte += ptes_per_vm_page, va += PAGE_SIZE) { + + if (*cpte == 0) + continue; + + assert(*cpte & INTEL_PTE_VALID); + + pa = pte_to_pa(*cpte); + + num_removed++; + if (*cpte & INTEL_PTE_WIRED) + num_unwired++; + + if (!valid_page(pa)) { + + /* + * Outside range of managed physical memory. + * Just remove the mappings. + */ + int i = ptes_per_vm_page; + pt_entry_t *lpte = cpte; + do { +#ifdef MACH_PV_PAGETABLES + update[ii].ptr = kv_to_ma(lpte); + update[ii].val = 0; + ii++; + if (ii == HYP_BATCH_MMU_UPDATES) { + hyp_mmu_update(kvtolin(&update), ii, kvtolin(&n), DOMID_SELF); + if (n != ii) + panic("couldn't pmap_remove_range\n"); + ii = 0; + } +#else /* MACH_PV_PAGETABLES */ + *lpte = 0; +#endif /* MACH_PV_PAGETABLES */ + lpte++; + } while (--i > 0); + continue; + } + + pai = pa_index(pa); + LOCK_PVH(pai); + + /* + * Get the modify and reference bits. + */ + { + int i; + pt_entry_t *lpte; + + i = ptes_per_vm_page; + lpte = cpte; + do { + pmap_phys_attributes[pai] |= + *lpte & (PHYS_MODIFIED|PHYS_REFERENCED); +#ifdef MACH_PV_PAGETABLES + update[ii].ptr = kv_to_ma(lpte); + update[ii].val = 0; + ii++; + if (ii == HYP_BATCH_MMU_UPDATES) { + hyp_mmu_update(kvtolin(&update), ii, kvtolin(&n), DOMID_SELF); + if (n != ii) + panic("couldn't pmap_remove_range\n"); + ii = 0; + } +#else /* MACH_PV_PAGETABLES */ + *lpte = 0; +#endif /* MACH_PV_PAGETABLES */ + lpte++; + } while (--i > 0); + } + + /* + * Remove the mapping from the pvlist for + * this physical page. + */ + { + pv_entry_t pv_h, prev, cur; + + pv_h = pai_to_pvh(pai); + if (pv_h->pmap == PMAP_NULL) { + panic("pmap_remove: null pv_list for pai %lx at va %lx!", pai, (unsigned long) va); + } + if (pv_h->va == va && pv_h->pmap == pmap) { + /* + * Header is the pv_entry. Copy the next one + * to header and free the next one (we cannot + * free the header) + */ + cur = pv_h->next; + if (cur != PV_ENTRY_NULL) { + *pv_h = *cur; + PV_FREE(cur); + } + else { + pv_h->pmap = PMAP_NULL; + } + } + else { + cur = pv_h; + do { + prev = cur; + if ((cur = prev->next) == PV_ENTRY_NULL) { + panic("pmap-remove: mapping not in pv_list!"); + } + } while (cur->va != va || cur->pmap != pmap); + prev->next = cur->next; + PV_FREE(cur); + } + UNLOCK_PVH(pai); + } + } + +#ifdef MACH_PV_PAGETABLES + if (ii > HYP_BATCH_MMU_UPDATES) + panic("overflowed array in pmap_remove_range"); + hyp_mmu_update(kvtolin(&update), ii, kvtolin(&n), DOMID_SELF); + if (n != ii) + panic("couldn't pmap_remove_range\n"); +#endif /* MACH_PV_PAGETABLES */ + + /* + * Update the counts + */ + pmap->stats.resident_count -= num_removed; + pmap->stats.wired_count -= num_unwired; +} + +/* + * Remove the given range of addresses + * from the specified map. + * + * It is assumed that the start and end are properly + * rounded to the hardware page size. + */ + +void pmap_remove( + pmap_t map, + vm_offset_t s, + vm_offset_t e) +{ + int spl; + pt_entry_t *spte, *epte; + vm_offset_t l; + vm_offset_t _s = s; + + if (map == PMAP_NULL) + return; + + PMAP_READ_LOCK(map, spl); + + while (s < e) { + pt_entry_t *pde = pmap_pde(map, s); + + l = (s + PDE_MAPPED_SIZE) & ~(PDE_MAPPED_SIZE-1); + if (l > e || l < s) + l = e; + if (pde && (*pde & INTEL_PTE_VALID)) { + spte = (pt_entry_t *)ptetokv(*pde); + spte = &spte[ptenum(s)]; + epte = &spte[intel_btop(l-s)]; + pmap_remove_range(map, s, spte, epte); + } + s = l; + } + PMAP_UPDATE_TLBS(map, _s, e); + + PMAP_READ_UNLOCK(map, spl); +} + +/* + * Routine: pmap_page_protect + * + * Function: + * Lower the permission for all mappings to a given + * page. + */ +void pmap_page_protect( + phys_addr_t phys, + vm_prot_t prot) +{ + pv_entry_t pv_h, prev; + pv_entry_t pv_e; + pt_entry_t *pte; + unsigned long pai; + pmap_t pmap; + int spl; + boolean_t remove; + + assert(phys != vm_page_fictitious_addr); + if (!valid_page(phys)) { + /* + * Not a managed page. + */ + return; + } + + /* + * Determine the new protection. + */ + switch (prot) { + case VM_PROT_READ: + case VM_PROT_READ|VM_PROT_EXECUTE: + remove = FALSE; + break; + case VM_PROT_ALL: + return; /* nothing to do */ + default: + remove = TRUE; + break; + } + + /* + * Lock the pmap system first, since we will be changing + * several pmaps. + */ + + PMAP_WRITE_LOCK(spl); + + pai = pa_index(phys); + pv_h = pai_to_pvh(pai); + + /* + * Walk down PV list, changing or removing all mappings. + * We do not have to lock the pv_list because we have + * the entire pmap system locked. + */ + if (pv_h->pmap != PMAP_NULL) { + + prev = pv_e = pv_h; + do { + vm_offset_t va; + + pmap = pv_e->pmap; + /* + * Lock the pmap to block pmap_extract and similar routines. + */ + simple_lock(&pmap->lock); + + va = pv_e->va; + pte = pmap_pte(pmap, va); + + /* + * Consistency checks. + */ + assert(*pte & INTEL_PTE_VALID); + assert(pte_to_pa(*pte) == phys); + + /* + * Remove the mapping if new protection is NONE + * or if write-protecting a kernel mapping. + */ + if (remove || pmap == kernel_pmap) { + /* + * Remove the mapping, collecting any modify bits. + */ + + if (*pte & INTEL_PTE_WIRED) { + pmap->stats.wired_count--; + } + + { + int i = ptes_per_vm_page; + + do { + pmap_phys_attributes[pai] |= + *pte & (PHYS_MODIFIED|PHYS_REFERENCED); +#ifdef MACH_PV_PAGETABLES + if (!hyp_mmu_update_pte(kv_to_ma(pte++), 0)) + panic("%s:%d could not clear pte %p\n",__FILE__,__LINE__,pte-1); +#else /* MACH_PV_PAGETABLES */ + *pte++ = 0; +#endif /* MACH_PV_PAGETABLES */ + } while (--i > 0); + } + + pmap->stats.resident_count--; + + /* + * Remove the pv_entry. + */ + if (pv_e == pv_h) { + /* + * Fix up head later. + */ + pv_h->pmap = PMAP_NULL; + } + else { + /* + * Delete this entry. + */ + prev->next = pv_e->next; + PV_FREE(pv_e); + } + } + else { + /* + * Write-protect. + */ + int i = ptes_per_vm_page; + + do { +#ifdef MACH_PV_PAGETABLES + if (!hyp_mmu_update_pte(kv_to_ma(pte), *pte & ~INTEL_PTE_WRITE)) + panic("%s:%d could not disable write on pte %p\n",__FILE__,__LINE__,pte); +#else /* MACH_PV_PAGETABLES */ + *pte &= ~INTEL_PTE_WRITE; +#endif /* MACH_PV_PAGETABLES */ + pte++; + } while (--i > 0); + + /* + * Advance prev. + */ + prev = pv_e; + } + PMAP_UPDATE_TLBS(pmap, va, va + PAGE_SIZE); + + simple_unlock(&pmap->lock); + + } while ((pv_e = prev->next) != PV_ENTRY_NULL); + + /* + * If pv_head mapping was removed, fix it up. + */ + if (pv_h->pmap == PMAP_NULL) { + pv_e = pv_h->next; + if (pv_e != PV_ENTRY_NULL) { + *pv_h = *pv_e; + PV_FREE(pv_e); + } + } + } + + PMAP_WRITE_UNLOCK(spl); +} + +/* + * Set the physical protection on the + * specified range of this map as requested. + * Will not increase permissions. + */ +void pmap_protect( + pmap_t map, + vm_offset_t s, + vm_offset_t e, + vm_prot_t prot) +{ + pt_entry_t *spte, *epte; + vm_offset_t l; + int spl; + vm_offset_t _s = s; + + if (map == PMAP_NULL) + return; + + /* + * Determine the new protection. + */ + switch (prot) { + case VM_PROT_READ: + case VM_PROT_READ|VM_PROT_EXECUTE: + break; + case VM_PROT_READ|VM_PROT_WRITE: + case VM_PROT_ALL: + return; /* nothing to do */ + default: + pmap_remove(map, s, e); + return; + } + +#if !(__i486__ || __i586__ || __i686__) + /* + * If write-protecting in the kernel pmap, + * remove the mappings; the i386 ignores + * the write-permission bit in kernel mode. + */ + if (map == kernel_pmap) { + pmap_remove(map, s, e); + return; + } +#endif + + SPLVM(spl); + simple_lock(&map->lock); + + while (s < e) { + pt_entry_t *pde = pde = pmap_pde(map, s); + + l = (s + PDE_MAPPED_SIZE) & ~(PDE_MAPPED_SIZE-1); + if (l > e || l < s) + l = e; + if (pde && (*pde & INTEL_PTE_VALID)) { + spte = (pt_entry_t *)ptetokv(*pde); + spte = &spte[ptenum(s)]; + epte = &spte[intel_btop(l-s)]; + +#ifdef MACH_PV_PAGETABLES + int n, i = 0; + struct mmu_update update[HYP_BATCH_MMU_UPDATES]; +#endif /* MACH_PV_PAGETABLES */ + + while (spte < epte) { + if (*spte & INTEL_PTE_VALID) { +#ifdef MACH_PV_PAGETABLES + update[i].ptr = kv_to_ma(spte); + update[i].val = *spte & ~INTEL_PTE_WRITE; + i++; + if (i == HYP_BATCH_MMU_UPDATES) { + hyp_mmu_update(kvtolin(&update), i, kvtolin(&n), DOMID_SELF); + if (n != i) + panic("couldn't pmap_protect\n"); + i = 0; + } +#else /* MACH_PV_PAGETABLES */ + *spte &= ~INTEL_PTE_WRITE; +#endif /* MACH_PV_PAGETABLES */ + } + spte++; + } +#ifdef MACH_PV_PAGETABLES + if (i > HYP_BATCH_MMU_UPDATES) + panic("overflowed array in pmap_protect"); + hyp_mmu_update(kvtolin(&update), i, kvtolin(&n), DOMID_SELF); + if (n != i) + panic("couldn't pmap_protect\n"); +#endif /* MACH_PV_PAGETABLES */ + } + s = l; + } + PMAP_UPDATE_TLBS(map, _s, e); + + simple_unlock(&map->lock); + SPLX(spl); +} + +typedef pt_entry_t* (*pmap_level_getter_t)(const pmap_t pmap, vm_offset_t addr); +/* +* Expand one single level of the page table tree +*/ +static inline pt_entry_t* pmap_expand_level(pmap_t pmap, vm_offset_t v, int spl, + pmap_level_getter_t pmap_level, + pmap_level_getter_t pmap_level_upper, + int n_per_vm_page, + struct kmem_cache *cache) +{ + pt_entry_t *pte; + + /* + * Expand pmap to include this pte. Assume that + * pmap is always expanded to include enough hardware + * pages to map one VM page. + */ + while ((pte = pmap_level(pmap, v)) == PT_ENTRY_NULL) { + /* + * Need to allocate a new page-table page. + */ + vm_offset_t ptp; + pt_entry_t *pdp; + int i; + + if (pmap == kernel_pmap) { + /* + * Would have to enter the new page-table page in + * EVERY pmap. + */ + panic("pmap_expand kernel pmap to %#zx", v); + } + + /* + * Unlock the pmap and allocate a new page-table page. + */ + PMAP_READ_UNLOCK(pmap, spl); + + while (!(ptp = kmem_cache_alloc(cache))) + VM_PAGE_WAIT((void (*)()) 0); + memset((void *)ptp, 0, PAGE_SIZE); + + /* + * Re-lock the pmap and check that another thread has + * not already allocated the page-table page. If it + * has, discard the new page-table page (and try + * again to make sure). + */ + PMAP_READ_LOCK(pmap, spl); + + if (pmap_level(pmap, v) != PT_ENTRY_NULL) { + /* + * Oops... + */ + PMAP_READ_UNLOCK(pmap, spl); + kmem_cache_free(cache, ptp); + PMAP_READ_LOCK(pmap, spl); + continue; + } + + /* + * Enter the new page table page in the page directory. + */ + i = n_per_vm_page; + pdp = pmap_level_upper(pmap, v); + do { +#ifdef MACH_PV_PAGETABLES + pmap_set_page_readonly((void *) ptp); + if (!hyp_mmuext_op_mfn (MMUEXT_PIN_L1_TABLE, kv_to_mfn(ptp))) + panic("couldn't pin page %lx(%lx)\n",ptp,(vm_offset_t) kv_to_ma(ptp)); + if (!hyp_mmu_update_pte(pa_to_ma(kvtophys((vm_offset_t)pdp)), + pa_to_pte(pa_to_ma(kvtophys(ptp))) | INTEL_PTE_VALID + | INTEL_PTE_USER + | INTEL_PTE_WRITE)) + panic("%s:%d could not set pde %p(%llx,%lx) to %lx(%llx,%lx) %lx\n",__FILE__,__LINE__, pdp, kvtophys((vm_offset_t)pdp), (vm_offset_t) pa_to_ma(kvtophys((vm_offset_t)pdp)), ptp, kvtophys(ptp), (vm_offset_t) pa_to_ma(kvtophys(ptp)), (vm_offset_t) pa_to_pte(kv_to_ma(ptp))); +#else /* MACH_PV_PAGETABLES */ + *pdp = pa_to_pte(kvtophys(ptp)) | INTEL_PTE_VALID + | INTEL_PTE_USER + | INTEL_PTE_WRITE; +#endif /* MACH_PV_PAGETABLES */ + pdp++; /* Note: This is safe b/c we stay in one page. */ + ptp += INTEL_PGBYTES; + } while (--i > 0); + + /* + * Now, get the address of the page-table entry. + */ + continue; + } + return pte; +} + +/* + * Expand, if required, the PMAP to include the virtual address V. + * PMAP needs to be locked, and it will be still locked on return. It + * can temporarily unlock the PMAP, during allocation or deallocation + * of physical pages. + */ +static inline pt_entry_t* pmap_expand(pmap_t pmap, vm_offset_t v, int spl) +{ +#ifdef PAE +#ifdef __x86_64__ + pmap_expand_level(pmap, v, spl, pmap_ptp, pmap_l4base, 1, &pdpt_cache); +#endif /* __x86_64__ */ + pmap_expand_level(pmap, v, spl, pmap_pde, pmap_ptp, 1, &pd_cache); +#endif /* PAE */ + return pmap_expand_level(pmap, v, spl, pmap_pte, pmap_pde, ptes_per_vm_page, &pt_cache); +} + +/* + * 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_t pmap, + vm_offset_t v, + phys_addr_t pa, + vm_prot_t prot, + boolean_t wired) +{ + boolean_t is_physmem; + pt_entry_t *pte; + pv_entry_t pv_h; + unsigned long i, pai; + pv_entry_t pv_e; + pt_entry_t template; + int spl; + phys_addr_t old_pa; + + assert(pa != vm_page_fictitious_addr); + if (pmap_debug) printf("pmap(%zx, %llx)\n", v, (unsigned long long) pa); + if (pmap == PMAP_NULL) + return; + + if (pmap == kernel_pmap && (v < kernel_virtual_start || v >= kernel_virtual_end)) + panic("pmap_enter(%lx, %llx) falls in physical memory area!\n", (unsigned long) v, (unsigned long long) pa); +#if !(__i486__ || __i586__ || __i686__) + if (pmap == kernel_pmap && (prot & VM_PROT_WRITE) == 0 + && !wired /* hack for io_wire */ ) { + /* + * Because the 386 ignores write protection in kernel mode, + * we cannot enter a read-only kernel mapping, and must + * remove an existing mapping if changing it. + */ + PMAP_READ_LOCK(pmap, spl); + + pte = pmap_pte(pmap, v); + if (pte != PT_ENTRY_NULL && *pte != 0) { + /* + * Invalidate the translation buffer, + * then remove the mapping. + */ + pmap_remove_range(pmap, v, pte, + pte + ptes_per_vm_page); + PMAP_UPDATE_TLBS(pmap, v, v + PAGE_SIZE); + } + PMAP_READ_UNLOCK(pmap, spl); + return; + } +#endif + + /* + * Must allocate a new pvlist entry while we're unlocked; + * Allocating may cause pageout (which will lock the pmap system). + * If we determine we need a pvlist entry, we will unlock + * and allocate one. Then we will retry, throughing away + * the allocated entry later (if we no longer need it). + */ + pv_e = PV_ENTRY_NULL; +Retry: + PMAP_READ_LOCK(pmap, spl); + + pte = pmap_expand(pmap, v, spl); + + if (vm_page_ready()) + is_physmem = (vm_page_lookup_pa(pa) != NULL); + else + is_physmem = (pa < biosmem_directmap_end()); + + /* + * Special case if the physical page is already mapped + * at this address. + */ + old_pa = pte_to_pa(*pte); + if (*pte && old_pa == pa) { + /* + * May be changing its wired attribute or protection + */ + + if (wired && !(*pte & INTEL_PTE_WIRED)) + pmap->stats.wired_count++; + else if (!wired && (*pte & INTEL_PTE_WIRED)) + pmap->stats.wired_count--; + + template = pa_to_pte(pa) | INTEL_PTE_VALID; + if (pmap != kernel_pmap) + template |= INTEL_PTE_USER; + if (prot & VM_PROT_WRITE) + template |= INTEL_PTE_WRITE; + if (machine_slot[cpu_number()].cpu_type >= CPU_TYPE_I486 + && !is_physmem) + template |= INTEL_PTE_NCACHE|INTEL_PTE_WTHRU; + if (wired) + template |= INTEL_PTE_WIRED; + i = ptes_per_vm_page; + do { + if (*pte & INTEL_PTE_MOD) + template |= INTEL_PTE_MOD; +#ifdef MACH_PV_PAGETABLES + if (!hyp_mmu_update_pte(kv_to_ma(pte), pa_to_ma(template))) + panic("%s:%d could not set pte %p to %llx\n",__FILE__,__LINE__,pte,template); +#else /* MACH_PV_PAGETABLES */ + WRITE_PTE(pte, template) +#endif /* MACH_PV_PAGETABLES */ + pte++; + pte_increment_pa(template); + } while (--i > 0); + PMAP_UPDATE_TLBS(pmap, v, v + PAGE_SIZE); + } + else { + + /* + * Remove old mapping from the PV list if necessary. + */ + if (*pte) { + /* + * Don't free the pte page if removing last + * mapping - we will immediately replace it. + */ + pmap_remove_range(pmap, v, pte, + pte + ptes_per_vm_page); + PMAP_UPDATE_TLBS(pmap, v, v + PAGE_SIZE); + } + + if (valid_page(pa)) { + + /* + * Enter the mapping in the PV list for this + * physical page. + */ + + pai = pa_index(pa); + LOCK_PVH(pai); + pv_h = pai_to_pvh(pai); + + if (pv_h->pmap == PMAP_NULL) { + /* + * No mappings yet + */ + pv_h->va = v; + pv_h->pmap = pmap; + pv_h->next = PV_ENTRY_NULL; + } + else { +#if DEBUG + { + /* check that this mapping is not already there */ + pv_entry_t e = pv_h; + while (e != PV_ENTRY_NULL) { + if (e->pmap == pmap && e->va == v) + panic("pmap_enter: already in pv_list"); + e = e->next; + } + } +#endif /* DEBUG */ + + /* + * Add new pv_entry after header. + */ + if (pv_e == PV_ENTRY_NULL) { + PV_ALLOC(pv_e); + if (pv_e == PV_ENTRY_NULL) { + UNLOCK_PVH(pai); + PMAP_READ_UNLOCK(pmap, spl); + + /* + * Refill from cache. + */ + pv_e = (pv_entry_t) kmem_cache_alloc(&pv_list_cache); + goto Retry; + } + } + pv_e->va = v; + pv_e->pmap = pmap; + pv_e->next = pv_h->next; + pv_h->next = pv_e; + /* + * Remember that we used the pvlist entry. + */ + pv_e = PV_ENTRY_NULL; + } + UNLOCK_PVH(pai); + } + + /* + * And count the mapping. + */ + + pmap->stats.resident_count++; + if (wired) + pmap->stats.wired_count++; + + /* + * Build a template to speed up entering - + * only the pfn changes. + */ + template = pa_to_pte(pa) | INTEL_PTE_VALID; + if (pmap != kernel_pmap) + template |= INTEL_PTE_USER; + if (prot & VM_PROT_WRITE) + template |= INTEL_PTE_WRITE; + if (machine_slot[cpu_number()].cpu_type >= CPU_TYPE_I486 + && !is_physmem) + template |= INTEL_PTE_NCACHE|INTEL_PTE_WTHRU; + if (wired) + template |= INTEL_PTE_WIRED; + i = ptes_per_vm_page; + do { +#ifdef MACH_PV_PAGETABLES + if (!(hyp_mmu_update_pte(kv_to_ma(pte), pa_to_ma(template)))) + panic("%s:%d could not set pte %p to %llx\n",__FILE__,__LINE__,pte,template); +#else /* MACH_PV_PAGETABLES */ + WRITE_PTE(pte, template) +#endif /* MACH_PV_PAGETABLES */ + pte++; + pte_increment_pa(template); + } while (--i > 0); + } + + if (pv_e != PV_ENTRY_NULL) { + PV_FREE(pv_e); + } + + PMAP_READ_UNLOCK(pmap, spl); +} + +/* + * 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_t map, + vm_offset_t v, + boolean_t wired) +{ + pt_entry_t *pte; + int i; + int spl; + + /* + * We must grab the pmap system lock because we may + * change a pte_page queue. + */ + PMAP_READ_LOCK(map, spl); + + if ((pte = pmap_pte(map, v)) == PT_ENTRY_NULL) + panic("pmap_change_wiring: pte missing"); + + if (wired && !(*pte & INTEL_PTE_WIRED)) { + /* + * wiring down mapping + */ + map->stats.wired_count++; + i = ptes_per_vm_page; + do { + *pte++ |= INTEL_PTE_WIRED; + } while (--i > 0); + } + else if (!wired && (*pte & INTEL_PTE_WIRED)) { + /* + * unwiring mapping + */ + map->stats.wired_count--; + i = ptes_per_vm_page; + do { +#ifdef MACH_PV_PAGETABLES + if (!(hyp_mmu_update_pte(kv_to_ma(pte), *pte & ~INTEL_PTE_WIRED))) + panic("%s:%d could not wire down pte %p\n",__FILE__,__LINE__,pte); +#else /* MACH_PV_PAGETABLES */ + *pte &= ~INTEL_PTE_WIRED; +#endif /* MACH_PV_PAGETABLES */ + pte++; + } while (--i > 0); + } + + PMAP_READ_UNLOCK(map, spl); +} + +/* + * Routine: pmap_extract + * Function: + * Extract the physical page address associated + * with the given map/virtual_address pair. + */ + +phys_addr_t pmap_extract( + pmap_t pmap, + vm_offset_t va) +{ + pt_entry_t *pte; + phys_addr_t pa; + int spl; + + SPLVM(spl); + simple_lock(&pmap->lock); + if ((pte = pmap_pte(pmap, va)) == PT_ENTRY_NULL) + pa = 0; + else if (!(*pte & INTEL_PTE_VALID)) + pa = 0; + else + pa = pte_to_pa(*pte) + (va & INTEL_OFFMASK); + simple_unlock(&pmap->lock); + SPLX(spl); + return(pa); +} + +/* + * 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. + */ +#if 0 +void pmap_copy( + pmap_t dst_pmap, + pmap_t src_pmap, + vm_offset_t dst_addr, + vm_size_t len, + vm_offset_t src_addr) +{ +} +#endif /* 0 */ + +/* + * Routine: pmap_collect + * Function: + * Garbage collects the physical map system for + * pages which are no longer used. + * Success need not be guaranteed -- that is, there + * may well be pages which are not referenced, but + * others may be collected. + * Usage: + * Called by the pageout daemon when pages are scarce. + */ +void pmap_collect(pmap_t p) +{ + pt_entry_t *ptp; + pt_entry_t *eptp; + phys_addr_t pa; + int spl, wired; + + if (p == PMAP_NULL) + return; + + if (p == kernel_pmap) + return; + + /* + * Free the page table tree. + */ + PMAP_READ_LOCK(p, spl); +#if PAE +#ifdef __x86_64__ + for (int l4i = 0; l4i < lin2l4num(VM_MAX_USER_ADDRESS); l4i++) { + pt_entry_t pdp = (pt_entry_t) p->l4base[l4i]; + if (!(pdp & INTEL_PTE_VALID)) + continue; + pt_entry_t *pdpbase = (pt_entry_t*) ptetokv(pdp); + for (int l3i = 0; l3i < NPTES; l3i++) +#else /* __x86_64__ */ + pt_entry_t *pdpbase = p->pdpbase; + for (int l3i = 0; l3i < lin2pdpnum(VM_MAX_USER_ADDRESS); l3i++) +#endif /* __x86_64__ */ + { + pt_entry_t pde = (pt_entry_t ) pdpbase[l3i]; + if (!(pde & INTEL_PTE_VALID)) + continue; + pt_entry_t *pdebase = (pt_entry_t*) ptetokv(pde); + for (int l2i = 0; l2i < NPTES; l2i++) +#else /* PAE */ + pt_entry_t *pdebase = p->dirbase; + for (int l2i = 0; l2i < lin2pdenum(VM_MAX_USER_ADDRESS); l2i++) +#endif /* PAE */ + { + pt_entry_t pte = (pt_entry_t) pdebase[l2i]; + if (!(pte & INTEL_PTE_VALID)) + continue; + + pa = pte_to_pa(pte); + ptp = (pt_entry_t *)phystokv(pa); + eptp = ptp + NPTES*ptes_per_vm_page; + + /* + * If the pte page has any wired mappings, we cannot + * free it. + */ + wired = 0; + { + pt_entry_t *ptep; + for (ptep = ptp; ptep < eptp; ptep++) { + if (*ptep & INTEL_PTE_WIRED) { + wired = 1; + break; + } + } + } + if (!wired) { + /* + * Remove the virtual addresses mapped by this pte page. + */ + { /*XXX big hack*/ + vm_offset_t va = pagenum2lin(l4i, l3i, l2i, 0); + if (p == kernel_pmap) + va = lintokv(va); + pmap_remove_range(p, va, ptp, eptp); + } + + /* + * Invalidate the page directory pointer. + */ + { + int i = ptes_per_vm_page; + pt_entry_t *pdep = &pdebase[l2i]; + do { +#ifdef MACH_PV_PAGETABLES + unsigned long pte = *pdep; + void *ptable = (void*) ptetokv(pte); + if (!(hyp_mmu_update_pte(pa_to_ma(kvtophys((vm_offset_t)pdep++)), 0))) + panic("%s:%d could not clear pde %p\n",__FILE__,__LINE__,pdep-1); + if (!hyp_mmuext_op_mfn (MMUEXT_UNPIN_TABLE, kv_to_mfn(ptable))) + panic("couldn't unpin page %p(%lx)\n", ptable, (vm_offset_t) pa_to_ma(kvtophys((vm_offset_t)ptable))); + pmap_set_page_readwrite(ptable); +#else /* MACH_PV_PAGETABLES */ + *pdep++ = 0; +#endif /* MACH_PV_PAGETABLES */ + } while (--i > 0); + } + + PMAP_READ_UNLOCK(p, spl); + + /* + * And free the pte page itself. + */ + kmem_cache_free(&pt_cache, (vm_offset_t)ptetokv(pte)); + + PMAP_READ_LOCK(p, spl); + + } + } +#if PAE + // TODO check l2 + } +#ifdef __x86_64__ + // TODO check l3 + } +#endif /* __x86_64__ */ +#endif /* PAE */ + + PMAP_UPDATE_TLBS(p, VM_MIN_USER_ADDRESS, VM_MAX_USER_ADDRESS); + + PMAP_READ_UNLOCK(p, spl); + return; + +} + +#if MACH_KDB +/* + * Routine: pmap_whatis + * Function: + * Check whether this address is within a pmap + * Usage: + * Called from debugger + */ +int pmap_whatis(pmap_t p, vm_offset_t a) +{ + pt_entry_t *ptp; + phys_addr_t pa; + int spl; + int ret = 0; + + if (p == PMAP_NULL) + return 0; + + PMAP_READ_LOCK(p, spl); +#if PAE +#ifdef __x86_64__ + if (a >= (vm_offset_t) p->l4base && a < (vm_offset_t) (&p->l4base[NPTES])) { + db_printf("L4 for pmap %p\n", p); + ret = 1; + } + for (int l4i = 0; l4i < NPTES; l4i++) { + pt_entry_t pdp = (pt_entry_t) p->l4base[l4i]; + if (!(pdp & INTEL_PTE_VALID)) + continue; + pt_entry_t *pdpbase = (pt_entry_t*) ptetokv(pdp); +#else /* __x86_64__ */ + int l4i = 0; + pt_entry_t *pdpbase = p->pdpbase; +#endif /* __x86_64__ */ + if (a >= (vm_offset_t) pdpbase && a < (vm_offset_t) (&pdpbase[NPTES])) { + db_printf("PDP %d for pmap %p\n", l4i, p); + ret = 1; + } + for (int l3i = 0; l3i < NPTES; l3i++) + { + pt_entry_t pde = (pt_entry_t ) pdpbase[l3i]; + if (!(pde & INTEL_PTE_VALID)) + continue; + pt_entry_t *pdebase = (pt_entry_t*) ptetokv(pde); +#else /* PAE */ + int l4i = 0, l3i = 0; + pt_entry_t *pdebase = p->dirbase; +#endif /* PAE */ + if (a >= (vm_offset_t) pdebase && a < (vm_offset_t) (&pdebase[NPTES])) { + db_printf("PDE %d %d for pmap %p\n", l4i, l3i, p); + ret = 1; + } + for (int l2i = 0; l2i < NPTES; l2i++) + { + pt_entry_t pte = (pt_entry_t) pdebase[l2i]; + if (!(pte & INTEL_PTE_VALID)) + continue; + + pa = pte_to_pa(pte); + ptp = (pt_entry_t *)phystokv(pa); + + if (a >= (vm_offset_t) ptp && a < (vm_offset_t) (&ptp[NPTES*ptes_per_vm_page])) { + db_printf("PTP %d %d %d for pmap %p\n", l4i, l3i, l2i, p); + ret = 1; + } + } +#if PAE + } +#ifdef __x86_64__ + } +#endif /* __x86_64__ */ +#endif /* PAE */ + PMAP_READ_UNLOCK(p, spl); + + if (p == kernel_pmap) { + phys_addr_t pa; + if (DB_VALID_KERN_ADDR(a)) + pa = kvtophys(a); + else + pa = pmap_extract(current_task()->map->pmap, a); + + if (valid_page(pa)) { + unsigned long pai; + pv_entry_t pv_h; + + pai = pa_index(pa); + for (pv_h = pai_to_pvh(pai); + pv_h && pv_h->pmap; + pv_h = pv_h->next) + db_printf("pmap %p at %llx\n", pv_h->pmap, pv_h->va); + } + } + + return ret; +} +#endif /* MACH_KDB */ + +/* + * Routine: pmap_activate + * Function: + * Binds the given physical map to the given + * processor, and returns a hardware map description. + */ +#if 0 +void pmap_activate(pmap_t my_pmap, thread_t th, int my_cpu) +{ + PMAP_ACTIVATE(my_pmap, th, my_cpu); +} +#endif /* 0 */ + +/* + * Routine: pmap_deactivate + * Function: + * Indicates that the given physical map is no longer + * in use on the specified processor. (This is a macro + * in pmap.h) + */ +#if 0 +void pmap_deactivate(pmap_t pmap, thread_t th, int which_cpu) +{ + PMAP_DEACTIVATE(pmap, th, which_cpu); +} +#endif /* 0 */ + +/* + * Routine: pmap_kernel + * Function: + * Returns the physical map handle for the kernel. + */ +#if 0 +pmap_t pmap_kernel() +{ + return (kernel_pmap); +} +#endif /* 0 */ + +/* + * pmap_zero_page zeros the specified (machine independent) page. + * See machine/phys.c or machine/phys.s for implementation. + */ +#if 0 +pmap_zero_page(vm_offset_t phys) +{ + int i; + + assert(phys != vm_page_fictitious_addr); + i = PAGE_SIZE / INTEL_PGBYTES; + phys = intel_pfn(phys); + + while (i--) + zero_phys(phys++); +} +#endif /* 0 */ + +/* + * pmap_copy_page copies the specified (machine independent) page. + * See machine/phys.c or machine/phys.s for implementation. + */ +#if 0 +pmap_copy_page(vm_offset_t src, vm_offset_t dst) +{ + int i; + + assert(src != vm_page_fictitious_addr); + assert(dst != vm_page_fictitious_addr); + i = PAGE_SIZE / INTEL_PGBYTES; + + while (i--) { + copy_phys(intel_pfn(src), intel_pfn(dst)); + src += INTEL_PGBYTES; + dst += INTEL_PGBYTES; + } +} +#endif /* 0 */ + +/* + * 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_t pmap, + vm_offset_t start, + vm_offset_t end, + boolean_t pageable) +{ +} + +/* + * Clear specified attribute bits. + */ +static void +phys_attribute_clear( + phys_addr_t phys, + int bits) +{ + pv_entry_t pv_h; + pv_entry_t pv_e; + pt_entry_t *pte; + unsigned long pai; + pmap_t pmap; + int spl; + + assert(phys != vm_page_fictitious_addr); + if (!valid_page(phys)) { + /* + * Not a managed page. + */ + return; + } + + /* + * Lock the pmap system first, since we will be changing + * several pmaps. + */ + + PMAP_WRITE_LOCK(spl); + + pai = pa_index(phys); + pv_h = pai_to_pvh(pai); + + /* + * Walk down PV list, clearing all modify or reference bits. + * We do not have to lock the pv_list because we have + * the entire pmap system locked. + */ + if (pv_h->pmap != PMAP_NULL) { + /* + * There are some mappings. + */ + for (pv_e = pv_h; pv_e != PV_ENTRY_NULL; pv_e = pv_e->next) { + vm_offset_t va; + + pmap = pv_e->pmap; + /* + * Lock the pmap to block pmap_extract and similar routines. + */ + simple_lock(&pmap->lock); + + va = pv_e->va; + pte = pmap_pte(pmap, va); + + /* + * Consistency checks. + */ + assert(*pte & INTEL_PTE_VALID); + assert(pte_to_pa(*pte) == phys); + + /* + * Clear modify or reference bits. + */ + { + int i = ptes_per_vm_page; + do { +#ifdef MACH_PV_PAGETABLES + if (!(hyp_mmu_update_pte(kv_to_ma(pte), *pte & ~bits))) + panic("%s:%d could not clear bits %x from pte %p\n",__FILE__,__LINE__,bits,pte); +#else /* MACH_PV_PAGETABLES */ + *pte &= ~bits; +#endif /* MACH_PV_PAGETABLES */ + } while (--i > 0); + } + PMAP_UPDATE_TLBS(pmap, va, va + PAGE_SIZE); + simple_unlock(&pmap->lock); + } + } + + pmap_phys_attributes[pai] &= ~bits; + + PMAP_WRITE_UNLOCK(spl); +} + +/* + * Check specified attribute bits. + */ +static boolean_t +phys_attribute_test( + phys_addr_t phys, + int bits) +{ + pv_entry_t pv_h; + pv_entry_t pv_e; + pt_entry_t *pte; + unsigned long pai; + pmap_t pmap; + int spl; + + assert(phys != vm_page_fictitious_addr); + if (!valid_page(phys)) { + /* + * Not a managed page. + */ + return (FALSE); + } + + /* + * Lock the pmap system first, since we will be checking + * several pmaps. + */ + + PMAP_WRITE_LOCK(spl); + + pai = pa_index(phys); + pv_h = pai_to_pvh(pai); + + if (pmap_phys_attributes[pai] & bits) { + PMAP_WRITE_UNLOCK(spl); + return (TRUE); + } + + /* + * Walk down PV list, checking all mappings. + * We do not have to lock the pv_list because we have + * the entire pmap system locked. + */ + if (pv_h->pmap != PMAP_NULL) { + /* + * There are some mappings. + */ + for (pv_e = pv_h; pv_e != PV_ENTRY_NULL; pv_e = pv_e->next) { + + pmap = pv_e->pmap; + /* + * Lock the pmap to block pmap_extract and similar routines. + */ + simple_lock(&pmap->lock); + + { + vm_offset_t va; + + va = pv_e->va; + pte = pmap_pte(pmap, va); + + /* + * Consistency checks. + */ + assert(*pte & INTEL_PTE_VALID); + assert(pte_to_pa(*pte) == phys); + } + + /* + * Check modify or reference bits. + */ + { + int i = ptes_per_vm_page; + + do { + if (*pte & bits) { + simple_unlock(&pmap->lock); + PMAP_WRITE_UNLOCK(spl); + return (TRUE); + } + } while (--i > 0); + } + simple_unlock(&pmap->lock); + } + } + PMAP_WRITE_UNLOCK(spl); + return (FALSE); +} + +/* + * Clear the modify bits on the specified physical page. + */ + +void pmap_clear_modify(phys_addr_t phys) +{ + phys_attribute_clear(phys, PHYS_MODIFIED); +} + +/* + * pmap_is_modified: + * + * Return whether or not the specified physical page is modified + * by any physical maps. + */ + +boolean_t pmap_is_modified(phys_addr_t phys) +{ + return (phys_attribute_test(phys, PHYS_MODIFIED)); +} + +/* + * pmap_clear_reference: + * + * Clear the reference bit on the specified physical page. + */ + +void pmap_clear_reference(phys_addr_t phys) +{ + phys_attribute_clear(phys, PHYS_REFERENCED); +} + +/* + * pmap_is_referenced: + * + * Return whether or not the specified physical page is referenced + * by any physical maps. + */ + +boolean_t pmap_is_referenced(phys_addr_t phys) +{ + return (phys_attribute_test(phys, PHYS_REFERENCED)); +} + +#if NCPUS > 1 +/* +* TLB Coherence Code (TLB "shootdown" code) +* +* Threads that belong to the same task share the same address space and +* hence share a pmap. However, they may run on distinct cpus and thus +* have distinct TLBs that cache page table entries. In order to guarantee +* the TLBs are consistent, whenever a pmap is changed, all threads that +* are active in that pmap must have their TLB updated. To keep track of +* this information, the set of cpus that are currently using a pmap is +* maintained within each pmap structure (cpus_using). Pmap_activate() and +* pmap_deactivate add and remove, respectively, a cpu from this set. +* Since the TLBs are not addressable over the bus, each processor must +* flush its own TLB; a processor that needs to invalidate another TLB +* needs to interrupt the processor that owns that TLB to signal the +* update. +* +* Whenever a pmap is updated, the lock on that pmap is locked, and all +* cpus using the pmap are signaled to invalidate. All threads that need +* to activate a pmap must wait for the lock to clear to await any updates +* in progress before using the pmap. They must ACQUIRE the lock to add +* their cpu to the cpus_using set. An implicit assumption made +* throughout the TLB code is that all kernel code that runs at or higher +* than splvm blocks out update interrupts, and that such code does not +* touch pageable pages. +* +* A shootdown interrupt serves another function besides signaling a +* processor to invalidate. The interrupt routine (pmap_update_interrupt) +* waits for the both the pmap lock (and the kernel pmap lock) to clear, +* preventing user code from making implicit pmap updates while the +* sending processor is performing its update. (This could happen via a +* user data write reference that turns on the modify bit in the page +* table). It must wait for any kernel updates that may have started +* concurrently with a user pmap update because the IPC code +* changes mappings. +* Spinning on the VALUES of the locks is sufficient (rather than +* having to acquire the locks) because any updates that occur subsequent +* to finding the lock unlocked will be signaled via another interrupt. +* (This assumes the interrupt is cleared before the low level interrupt code +* calls pmap_update_interrupt()). +* +* The signaling processor must wait for any implicit updates in progress +* to terminate before continuing with its update. Thus it must wait for an +* acknowledgement of the interrupt from each processor for which such +* references could be made. For maintaining this information, a set +* cpus_active is used. A cpu is in this set if and only if it can +* use a pmap. When pmap_update_interrupt() is entered, a cpu is removed from +* this set; when all such cpus are removed, it is safe to update. +* +* Before attempting to acquire the update lock on a pmap, a cpu (A) must +* be at least at the priority of the interprocessor interrupt +* (splip<=splvm). Otherwise, A could grab a lock and be interrupted by a +* kernel update; it would spin forever in pmap_update_interrupt() trying +* to acquire the user pmap lock it had already acquired. Furthermore A +* must remove itself from cpus_active. Otherwise, another cpu holding +* the lock (B) could be in the process of sending an update signal to A, +* and thus be waiting for A to remove itself from cpus_active. If A is +* spinning on the lock at priority this will never happen and a deadlock +* will result. +*/ + +/* + * Signal another CPU that it must flush its TLB + */ +void signal_cpus( + cpu_set use_list, + pmap_t pmap, + vm_offset_t start, + vm_offset_t end) +{ + int which_cpu, j; + pmap_update_list_t update_list_p; + + while ((which_cpu = __builtin_ffs(use_list)) != 0) { + which_cpu -= 1; /* convert to 0 origin */ + + update_list_p = &cpu_update_list[which_cpu]; + simple_lock(&update_list_p->lock); + + j = update_list_p->count; + if (j >= UPDATE_LIST_SIZE) { + /* + * list overflowed. Change last item to + * indicate overflow. + */ + update_list_p->item[UPDATE_LIST_SIZE-1].pmap = kernel_pmap; + update_list_p->item[UPDATE_LIST_SIZE-1].start = VM_MIN_USER_ADDRESS; + update_list_p->item[UPDATE_LIST_SIZE-1].end = VM_MAX_KERNEL_ADDRESS; + } + else { + update_list_p->item[j].pmap = pmap; + update_list_p->item[j].start = start; + update_list_p->item[j].end = end; + update_list_p->count = j+1; + } + cpu_update_needed[which_cpu] = TRUE; + simple_unlock(&update_list_p->lock); + + __sync_synchronize(); + if (((cpus_idle & (1 << which_cpu)) == 0)) + interrupt_processor(which_cpu); + use_list &= ~(1 << which_cpu); + } +} + +/* + * This is called at splvm + */ +void process_pmap_updates(pmap_t my_pmap) +{ + int my_cpu = cpu_number(); + pmap_update_list_t update_list_p; + int j; + pmap_t pmap; + + update_list_p = &cpu_update_list[my_cpu]; + simple_lock_nocheck(&update_list_p->lock); + + for (j = 0; j < update_list_p->count; j++) { + pmap = update_list_p->item[j].pmap; + if (pmap == my_pmap || + pmap == kernel_pmap) { + + INVALIDATE_TLB(pmap, + update_list_p->item[j].start, + update_list_p->item[j].end); + } + } + update_list_p->count = 0; + cpu_update_needed[my_cpu] = FALSE; + simple_unlock_nocheck(&update_list_p->lock); +} + +/* + * Interrupt routine for TBIA requested from other processor. + */ +void pmap_update_interrupt(void) +{ + int my_cpu; + pmap_t my_pmap; + int s; + + my_cpu = cpu_number(); + + /* + * Exit now if we're idle. We'll pick up the update request + * when we go active, and we must not put ourselves back in + * the active set because we'll never process the interrupt + * while we're idle (thus hanging the system). + */ + if (cpus_idle & (1 << my_cpu)) + return; + + if (current_thread() == THREAD_NULL) + my_pmap = kernel_pmap; + else { + my_pmap = current_pmap(); + if (!pmap_in_use(my_pmap, my_cpu)) + my_pmap = kernel_pmap; + } + + /* + * Raise spl to splvm (above splip) to block out pmap_extract + * from IO code (which would put this cpu back in the active + * set). + */ + s = splvm(); + + do { + + /* + * Indicate that we're not using either user or kernel + * pmap. + */ + i_bit_clear(my_cpu, &cpus_active); + + /* + * Wait for any pmap updates in progress, on either user + * or kernel pmap. + */ + while (my_pmap->lock.lock_data || + kernel_pmap->lock.lock_data) + cpu_pause(); + + process_pmap_updates(my_pmap); + + i_bit_set(my_cpu, &cpus_active); + + } while (cpu_update_needed[my_cpu]); + + splx(s); +} +#else /* NCPUS > 1 */ +/* + * Dummy routine to satisfy external reference. + */ +void pmap_update_interrupt(void) +{ + /* should never be called. */ +} +#endif /* NCPUS > 1 */ + +#if defined(__i386__) || defined (__x86_64__) +/* Unmap page 0 to trap NULL references. */ +void +pmap_unmap_page_zero (void) +{ + int *pte; + + printf("Unmapping the zero page. Some BIOS functions may not be working any more.\n"); + pte = (int *) pmap_pte (kernel_pmap, 0); + if (!pte) + return; + assert (pte); +#ifdef MACH_PV_PAGETABLES + if (!hyp_mmu_update_pte(kv_to_ma(pte), 0)) + printf("couldn't unmap page 0\n"); +#else /* MACH_PV_PAGETABLES */ + *pte = 0; + INVALIDATE_TLB(kernel_pmap, 0, PAGE_SIZE); +#endif /* MACH_PV_PAGETABLES */ +} +#endif /* __i386__ */ + +void +pmap_make_temporary_mapping(void) +{ + int i; + /* + * We'll have to temporarily install a direct mapping + * between physical memory and low linear memory, + * until we start using our new kernel segment descriptors. + */ +#if INIT_VM_MIN_KERNEL_ADDRESS != LINEAR_MIN_KERNEL_ADDRESS + vm_offset_t delta = INIT_VM_MIN_KERNEL_ADDRESS - LINEAR_MIN_KERNEL_ADDRESS; + if ((vm_offset_t)(-delta) < delta) + delta = (vm_offset_t)(-delta); + int nb_direct = delta >> PDESHIFT; + for (i = 0; i < nb_direct; i++) + kernel_page_dir[lin2pdenum_cont(INIT_VM_MIN_KERNEL_ADDRESS) + i] = + kernel_page_dir[lin2pdenum_cont(LINEAR_MIN_KERNEL_ADDRESS) + i]; +#endif + +#ifdef LINUX_DEV + /* We need BIOS memory mapped at 0xc0000 & co for BIOS accesses */ +#if VM_MIN_KERNEL_ADDRESS != 0 + kernel_page_dir[lin2pdenum_cont(LINEAR_MIN_KERNEL_ADDRESS - VM_MIN_KERNEL_ADDRESS)] = + kernel_page_dir[lin2pdenum_cont(LINEAR_MIN_KERNEL_ADDRESS)]; +#endif +#endif /* LINUX_DEV */ + +#ifdef MACH_PV_PAGETABLES +#ifndef __x86_64__ + const int PDPNUM_KERNEL = PDPNUM; +#endif + for (i = 0; i < PDPNUM_KERNEL; i++) + pmap_set_page_readonly_init((void*) kernel_page_dir + i * INTEL_PGBYTES); +#if PAE +#ifndef __x86_64__ + pmap_set_page_readonly_init(kernel_pmap->pdpbase); +#endif +#endif /* PAE */ +#endif /* MACH_PV_PAGETABLES */ + + pmap_set_page_dir(); +} + +void +pmap_set_page_dir(void) +{ +#if PAE +#ifdef __x86_64__ + set_cr3((unsigned long)_kvtophys(kernel_pmap->l4base)); +#else + set_cr3((unsigned long)_kvtophys(kernel_pmap->pdpbase)); +#endif +#ifndef MACH_HYP + if (!CPU_HAS_FEATURE(CPU_FEATURE_PAE)) + panic("CPU doesn't have support for PAE."); + set_cr4(get_cr4() | CR4_PAE); +#endif /* MACH_HYP */ +#else + set_cr3((unsigned long)_kvtophys(kernel_page_dir)); +#endif /* PAE */ +} + +void +pmap_remove_temporary_mapping(void) +{ +#if INIT_VM_MIN_KERNEL_ADDRESS != LINEAR_MIN_KERNEL_ADDRESS + int i; + vm_offset_t delta = INIT_VM_MIN_KERNEL_ADDRESS - LINEAR_MIN_KERNEL_ADDRESS; + if ((vm_offset_t)(-delta) < delta) + delta = (vm_offset_t)(-delta); + int nb_direct = delta >> PDESHIFT; + /* Get rid of the temporary direct mapping and flush it out of the TLB. */ + for (i = 0 ; i < nb_direct; i++) { +#ifdef MACH_XEN +#ifdef MACH_PSEUDO_PHYS + if (!hyp_mmu_update_pte(kv_to_ma(&kernel_page_dir[lin2pdenum_cont(VM_MIN_KERNEL_ADDRESS) + i]), 0)) +#else /* MACH_PSEUDO_PHYS */ + if (hyp_do_update_va_mapping(VM_MIN_KERNEL_ADDRESS + i * INTEL_PGBYTES, 0, UVMF_INVLPG | UVMF_ALL)) +#endif /* MACH_PSEUDO_PHYS */ + printf("couldn't unmap frame %d\n", i); +#else /* MACH_XEN */ + kernel_page_dir[lin2pdenum_cont(INIT_VM_MIN_KERNEL_ADDRESS) + i] = 0; +#endif /* MACH_XEN */ + } +#endif + +#ifdef LINUX_DEV + /* Keep BIOS memory mapped */ +#if VM_MIN_KERNEL_ADDRESS != 0 + kernel_page_dir[lin2pdenum_cont(LINEAR_MIN_KERNEL_ADDRESS - VM_MIN_KERNEL_ADDRESS)] = + kernel_page_dir[lin2pdenum_cont(LINEAR_MIN_KERNEL_ADDRESS)]; +#endif +#endif /* LINUX_DEV */ + + /* Not used after boot, better give it back. */ +#ifdef MACH_XEN + hyp_free_page(0, (void*) VM_MIN_KERNEL_ADDRESS); +#endif /* MACH_XEN */ + + flush_tlb(); +} diff --git a/i386/intel/pmap.h b/i386/intel/pmap.h new file mode 100644 index 0000000..8b0eba0 --- /dev/null +++ b/i386/intel/pmap.h @@ -0,0 +1,574 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988 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. + */ +/* + * File: pmap.h + * + * Authors: Avadis Tevanian, Jr., Michael Wayne Young + * Date: 1985 + * + * Machine-dependent structures for the physical map module. + */ + +#ifndef _PMAP_MACHINE_ +#define _PMAP_MACHINE_ 1 + +#ifndef __ASSEMBLER__ + +#include +#include +#include +#include +#include +#include + +/* + * Define the generic in terms of the specific + */ + +#if defined(__i386__) || defined(__x86_64__) +#define INTEL_PGBYTES I386_PGBYTES +#define INTEL_PGSHIFT I386_PGSHIFT +#define intel_btop(x) i386_btop(x) +#define intel_ptob(x) i386_ptob(x) +#define intel_round_page(x) i386_round_page(x) +#define intel_trunc_page(x) i386_trunc_page(x) +#define trunc_intel_to_vm(x) trunc_i386_to_vm(x) +#define round_intel_to_vm(x) round_i386_to_vm(x) +#define vm_to_intel(x) vm_to_i386(x) +#endif /* __i386__ */ + +/* + * i386/i486 Page Table Entry + */ + +typedef phys_addr_t pt_entry_t; +#define PT_ENTRY_NULL ((pt_entry_t *) 0) + +#endif /* __ASSEMBLER__ */ + +#define INTEL_OFFMASK 0xfff /* offset within page */ +#if PAE +#ifdef __x86_64__ +#define L4SHIFT 39 /* L4 shift */ +#define L4MASK 0x1ff /* mask for L4 index */ +#define PDPNUM_KERNEL (((VM_MAX_KERNEL_ADDRESS - VM_MIN_KERNEL_ADDRESS) >> PDPSHIFT) + 1) +#define PDPMASK 0x1ff /* mask for page directory pointer index */ +#else /* __x86_64__ */ +#define PDPNUM 4 /* number of page directory pointers */ +#define PDPMASK 3 /* mask for page directory pointer index */ +#endif /* __x86_64__ */ +#define PDPSHIFT 30 /* page directory pointer */ +#define PDESHIFT 21 /* page descriptor shift */ +#define PDEMASK 0x1ff /* mask for page descriptor index */ +#define PTESHIFT 12 /* page table shift */ +#define PTEMASK 0x1ff /* mask for page table index */ +#else /* PAE */ +#define PDPNUM 1 /* number of page directory pointers */ +#define PDESHIFT 22 /* page descriptor shift */ +#define PDEMASK 0x3ff /* mask for page descriptor index */ +#define PTESHIFT 12 /* page table shift */ +#define PTEMASK 0x3ff /* mask for page table index */ +#endif /* PAE */ + +/* + * Convert linear offset to L4 pointer index + */ +#ifdef __x86_64__ +#define lin2l4num(a) (((a) >> L4SHIFT) & L4MASK) +#endif + +/* + * Convert linear offset to page descriptor index + */ +#define lin2pdenum(a) (((a) >> PDESHIFT) & PDEMASK) + +#if PAE +/* Special version assuming contiguous page directories. Making it + include the page directory pointer table index too. */ +#ifdef __x86_64__ +#define lin2pdenum_cont(a) (((a) >> PDESHIFT) & 0x3ff) +#else +#define lin2pdenum_cont(a) (((a) >> PDESHIFT) & 0x7ff) +#endif +#else +#define lin2pdenum_cont(a) lin2pdenum(a) +#endif + +/* + * Convert linear offset to page directory pointer index + */ +#if PAE +#define lin2pdpnum(a) (((a) >> PDPSHIFT) & PDPMASK) +#endif + +/* + * Convert page descriptor index to linear address + */ +#define pdenum2lin(a) ((vm_offset_t)(a) << PDESHIFT) + +#if PAE +#ifdef __x86_64__ +#define pagenum2lin(l4num, l3num, l2num, l1num) \ + (((vm_offset_t)(l4num) << L4SHIFT) + \ + ((vm_offset_t)(l3num) << PDPSHIFT) + \ + ((vm_offset_t)(l2num) << PDESHIFT) + \ + ((vm_offset_t)(l1num) << PTESHIFT)) +#else /* __x86_64__ */ +#define pagenum2lin(l4num, l3num, l2num, l1num) \ + (((vm_offset_t)(l3num) << PDPSHIFT) + \ + ((vm_offset_t)(l2num) << PDESHIFT) + \ + ((vm_offset_t)(l1num) << PTESHIFT)) +#endif +#else /* PAE */ +#define pagenum2lin(l4num, l3num, l2num, l1num) \ + (((vm_offset_t)(l2num) << PDESHIFT) + \ + ((vm_offset_t)(l1num) << PTESHIFT)) +#endif + + +/* + * Convert linear offset to page table index + */ +#define ptenum(a) (((a) >> PTESHIFT) & PTEMASK) + +#define NPTES (intel_ptob(1)/sizeof(pt_entry_t)) +#define NPDES (PDPNUM * (intel_ptob(1)/sizeof(pt_entry_t))) + +/* + * Hardware pte bit definitions (to be used directly on the ptes + * without using the bit fields). + */ + +#define INTEL_PTE_VALID 0x00000001 +#define INTEL_PTE_WRITE 0x00000002 +#define INTEL_PTE_USER 0x00000004 +#define INTEL_PTE_WTHRU 0x00000008 +#define INTEL_PTE_NCACHE 0x00000010 +#define INTEL_PTE_REF 0x00000020 +#define INTEL_PTE_MOD 0x00000040 +#define INTEL_PTE_PS 0x00000080 +#ifdef MACH_PV_PAGETABLES +/* Not supported */ +#define INTEL_PTE_GLOBAL 0x00000000 +#else /* MACH_PV_PAGETABLES */ +#define INTEL_PTE_GLOBAL 0x00000100 +#endif /* MACH_PV_PAGETABLES */ +#define INTEL_PTE_WIRED 0x00000200 +#ifdef PAE +#ifdef __x86_64__ +#define INTEL_PTE_PFN 0xfffffffffffff000ULL +#else /* __x86_64__ */ +#define INTEL_PTE_PFN 0x00007ffffffff000ULL +#endif/* __x86_64__ */ +#else +#define INTEL_PTE_PFN 0xfffff000 +#endif + +#define pa_to_pte(a) ((a) & INTEL_PTE_PFN) +#ifdef MACH_PSEUDO_PHYS +#define pte_to_pa(p) ma_to_pa((p) & INTEL_PTE_PFN) +#else /* MACH_PSEUDO_PHYS */ +#define pte_to_pa(p) ((p) & INTEL_PTE_PFN) +#endif /* MACH_PSEUDO_PHYS */ +#define pte_increment_pa(p) ((p) += INTEL_OFFMASK+1) + +/* + * Convert page table entry to kernel virtual address + */ +#define ptetokv(a) (phystokv(pte_to_pa(a))) + +#ifndef __ASSEMBLER__ +typedef volatile long cpu_set; /* set of CPUs - must be <= 32 */ + /* changed by other processors */ + +struct pmap { +#if ! PAE + pt_entry_t *dirbase; /* page directory table */ +#else /* PAE */ +#ifdef __x86_64__ + pt_entry_t *l4base; /* l4 table */ +#ifdef MACH_HYP + pt_entry_t *user_l4base; /* Userland l4 table */ + pt_entry_t *user_pdpbase; /* Userland l4 table */ +#endif /* MACH_HYP */ +#else /* x86_64 */ + pt_entry_t *pdpbase; /* page directory pointer table */ +#endif /* x86_64 */ +#endif /* PAE */ + int ref_count; /* reference count */ + decl_simple_lock_data(,lock) + /* lock on map */ + struct pmap_statistics stats; /* map statistics */ + cpu_set cpus_using; /* bitmap of cpus using pmap */ +}; + +typedef struct pmap *pmap_t; + +#define PMAP_NULL ((pmap_t) 0) + +#ifdef MACH_PV_PAGETABLES +extern void pmap_set_page_readwrite(void *addr); +extern void pmap_set_page_readonly(void *addr); +extern void pmap_set_page_readonly_init(void *addr); +extern void pmap_map_mfn(void *addr, unsigned long mfn); +extern void pmap_clear_bootstrap_pagetable(pt_entry_t *addr); +#endif /* MACH_PV_PAGETABLES */ + +#if PAE +#ifdef __x86_64__ +/* TODO: support PCID */ +#ifdef MACH_HYP +#define set_pmap(pmap) \ + MACRO_BEGIN \ + set_cr3(kvtophys((vm_offset_t)(pmap)->l4base)); \ + if (pmap->user_l4base) \ + if (!hyp_set_user_cr3(kvtophys((vm_offset_t)(pmap)->user_l4base))) \ + panic("set_user_cr3"); \ + MACRO_END +#else /* MACH_HYP */ +#define set_pmap(pmap) set_cr3(kvtophys((vm_offset_t)(pmap)->l4base)) +#endif /* MACH_HYP */ +#else /* x86_64 */ +#define set_pmap(pmap) set_cr3(kvtophys((vm_offset_t)(pmap)->pdpbase)) +#endif /* x86_64 */ +#else /* PAE */ +#define set_pmap(pmap) set_cr3(kvtophys((vm_offset_t)(pmap)->dirbase)) +#endif /* PAE */ + +typedef struct { + pt_entry_t *entry; + vm_offset_t vaddr; +} pmap_mapwindow_t; + +extern pmap_mapwindow_t *pmap_get_mapwindow(pt_entry_t entry); +extern void pmap_put_mapwindow(pmap_mapwindow_t *map); + +#define PMAP_NMAPWINDOWS 2 /* Per CPU */ + +#if NCPUS > 1 +/* + * List of cpus that are actively using mapped memory. Any + * pmap update operation must wait for all cpus in this list. + * Update operations must still be queued to cpus not in this + * list. + */ +extern cpu_set cpus_active; + +/* + * List of cpus that are idle, but still operating, and will want + * to see any kernel pmap updates when they become active. + */ +extern cpu_set cpus_idle; + +/* + * Quick test for pmap update requests. + */ +extern volatile +boolean_t cpu_update_needed[NCPUS]; + +/* + * External declarations for PMAP_ACTIVATE. + */ + +void process_pmap_updates(pmap_t); +extern pmap_t kernel_pmap; + +#endif /* NCPUS > 1 */ + +void pmap_update_interrupt(void); + +/* + * Machine dependent routines that are used only for i386/i486. + */ + +pt_entry_t *pmap_pte(const pmap_t pmap, vm_offset_t addr); + +/* + * Macros for speed. + */ + +#if NCPUS > 1 + +/* + * For multiple CPUS, PMAP_ACTIVATE and PMAP_DEACTIVATE must manage + * fields to control TLB invalidation on other CPUS. + */ + +#define PMAP_ACTIVATE_KERNEL(my_cpu) { \ + \ + /* \ + * Let pmap updates proceed while we wait for this pmap. \ + */ \ + i_bit_clear((my_cpu), &cpus_active); \ + \ + /* \ + * Lock the pmap to put this cpu in its active set. \ + * Wait for updates here. \ + */ \ + simple_lock(&kernel_pmap->lock); \ + \ + /* \ + * Process invalidate requests for the kernel pmap. \ + */ \ + if (cpu_update_needed[(my_cpu)]) \ + process_pmap_updates(kernel_pmap); \ + \ + /* \ + * Mark that this cpu is using the pmap. \ + */ \ + i_bit_set((my_cpu), &kernel_pmap->cpus_using); \ + \ + /* \ + * Mark this cpu active - IPL will be lowered by \ + * load_context(). \ + */ \ + i_bit_set((my_cpu), &cpus_active); \ + \ + simple_unlock(&kernel_pmap->lock); \ +} + +#define PMAP_DEACTIVATE_KERNEL(my_cpu) { \ + /* \ + * Mark pmap no longer in use by this cpu even if \ + * pmap is locked against updates. \ + */ \ + i_bit_clear((my_cpu), &kernel_pmap->cpus_using); \ +} + +#define PMAP_ACTIVATE_USER(pmap, th, my_cpu) { \ + pmap_t tpmap = (pmap); \ + \ + if (tpmap == kernel_pmap) { \ + /* \ + * If this is the kernel pmap, switch to its page tables. \ + */ \ + set_pmap(tpmap); \ + } \ + else { \ + /* \ + * Let pmap updates proceed while we wait for this pmap. \ + */ \ + i_bit_clear((my_cpu), &cpus_active); \ + \ + /* \ + * Lock the pmap to put this cpu in its active set. \ + * Wait for updates here. \ + */ \ + simple_lock(&tpmap->lock); \ + \ + /* \ + * No need to invalidate the TLB - the entire user pmap \ + * will be invalidated by reloading dirbase. \ + */ \ + set_pmap(tpmap); \ + \ + /* \ + * Mark that this cpu is using the pmap. \ + */ \ + i_bit_set((my_cpu), &tpmap->cpus_using); \ + \ + /* \ + * Mark this cpu active - IPL will be lowered by \ + * load_context(). \ + */ \ + i_bit_set((my_cpu), &cpus_active); \ + \ + simple_unlock(&tpmap->lock); \ + } \ +} + +#define PMAP_DEACTIVATE_USER(pmap, thread, my_cpu) { \ + pmap_t tpmap = (pmap); \ + \ + /* \ + * Do nothing if this is the kernel pmap. \ + */ \ + if (tpmap != kernel_pmap) { \ + /* \ + * Mark pmap no longer in use by this cpu even if \ + * pmap is locked against updates. \ + */ \ + i_bit_clear((my_cpu), &(pmap)->cpus_using); \ + } \ +} + +#define MARK_CPU_IDLE(my_cpu) { \ + /* \ + * Mark this cpu idle, and remove it from the active set, \ + * since it is not actively using any pmap. Signal_cpus \ + * will notice that it is idle, and avoid signaling it, \ + * but will queue the update request for when the cpu \ + * becomes active. \ + */ \ + int s = splvm(); \ + i_bit_set((my_cpu), &cpus_idle); \ + i_bit_clear((my_cpu), &cpus_active); \ + splx(s); \ +} + +#define MARK_CPU_ACTIVE(my_cpu) { \ + \ + int s = splvm(); \ + /* \ + * If a kernel_pmap update was requested while this cpu \ + * was idle, process it as if we got the interrupt. \ + * Before doing so, remove this cpu from the idle set. \ + * Since we do not grab any pmap locks while we flush \ + * our TLB, another cpu may start an update operation \ + * before we finish. Removing this cpu from the idle \ + * set assures that we will receive another update \ + * interrupt if this happens. \ + */ \ + i_bit_clear((my_cpu), &cpus_idle); \ + __sync_synchronize(); \ + \ + if (cpu_update_needed[(my_cpu)]) \ + pmap_update_interrupt(); \ + \ + /* \ + * Mark that this cpu is now active. \ + */ \ + i_bit_set((my_cpu), &cpus_active); \ + splx(s); \ +} + +#else /* NCPUS > 1 */ + +/* + * With only one CPU, we just have to indicate whether the pmap is + * in use. + */ + +#define PMAP_ACTIVATE_KERNEL(my_cpu) { \ + (void) (my_cpu); \ + kernel_pmap->cpus_using = TRUE; \ +} + +#define PMAP_DEACTIVATE_KERNEL(my_cpu) { \ + (void) (my_cpu); \ + kernel_pmap->cpus_using = FALSE; \ +} + +#define PMAP_ACTIVATE_USER(pmap, th, my_cpu) { \ + pmap_t tpmap = (pmap); \ + (void) (th); \ + (void) (my_cpu); \ + \ + set_pmap(tpmap); \ + if (tpmap != kernel_pmap) { \ + tpmap->cpus_using = TRUE; \ + } \ +} + +#define PMAP_DEACTIVATE_USER(pmap, thread, cpu) { \ + (void) (thread); \ + (void) (cpu); \ + if ((pmap) != kernel_pmap) \ + (pmap)->cpus_using = FALSE; \ +} + +#endif /* NCPUS > 1 */ + +#define PMAP_CONTEXT(pmap, thread) + +#define pmap_kernel() (kernel_pmap) +#define pmap_resident_count(pmap) ((pmap)->stats.resident_count) +#define pmap_phys_address(frame) ((intel_ptob((phys_addr_t) frame))) +#define pmap_phys_to_frame(phys) ((int) (intel_btop(phys))) +#define pmap_copy(dst_pmap,src_pmap,dst_addr,len,src_addr) +#define pmap_attribute(pmap,addr,size,attr,value) \ + (KERN_INVALID_ADDRESS) + +extern pt_entry_t *kernel_page_dir; + +extern vm_offset_t kernel_virtual_start; +extern vm_offset_t kernel_virtual_end; + +/* + * Bootstrap the system enough to run with virtual memory. + * Allocate the kernel page directory and page tables, + * and direct-map all physical memory. + * Called with mapping off. + */ +extern void pmap_bootstrap(void); + +extern void pmap_set_page_dir(void); +extern void pmap_make_temporary_mapping(void); +extern void pmap_remove_temporary_mapping(void); + +extern void pmap_unmap_page_zero (void); + +/* + * pmap_zero_page zeros the specified (machine independent) page. + */ +extern void pmap_zero_page (phys_addr_t); + +/* + * pmap_copy_page copies the specified (machine independent) pages. + */ +extern void pmap_copy_page (phys_addr_t, phys_addr_t); + +/* + * copy_to_phys(src_addr_v, dst_addr_p, count) + * + * Copy virtual memory to physical memory + */ +extern void +copy_to_phys( + vm_offset_t src_addr_v, + phys_addr_t dst_addr_p, + int count); + +/* + * copy_from_phys(src_addr_p, dst_addr_v, count) + * + * Copy physical memory to virtual memory. The virtual memory + * is assumed to be present (e.g. the buffer pool). + */ +extern void +copy_from_phys( + phys_addr_t src_addr_p, + vm_offset_t dst_addr_v, + int count); + +/* + * kvtophys(addr) + * + * Convert a kernel virtual address to a physical address + */ +extern phys_addr_t kvtophys (vm_offset_t); + +#if NCPUS > 1 +void signal_cpus( + cpu_set use_list, + pmap_t pmap, + vm_offset_t start, + vm_offset_t end); +#endif /* NCPUS > 1 */ + +#endif /* __ASSEMBLER__ */ + +#endif /* _PMAP_MACHINE_ */ diff --git a/i386/intel/read_fault.c b/i386/intel/read_fault.c new file mode 100644 index 0000000..0b79e3d --- /dev/null +++ b/i386/intel/read_fault.c @@ -0,0 +1,178 @@ +/* + * 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. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#if !(__i486__ || __i586__ || __i686__) +/* + * Expansion of vm_fault for read fault in kernel mode. + * Must enter the mapping as writable, since the i386 + * ignores write protection in kernel mode. + */ +kern_return_t +intel_read_fault( + vm_map_t map, + vm_offset_t vaddr) +{ + vm_map_version_t version; /* Map version for + verification */ + vm_object_t object; /* Top-level object */ + vm_offset_t offset; /* Top-level offset */ + vm_prot_t prot; /* Protection for mapping */ + vm_page_t result_page; /* Result of vm_fault_page */ + vm_page_t top_page; /* Placeholder page */ + boolean_t wired; /* Is map region wired? */ + kern_return_t result; + vm_page_t m; + + RetryFault: + + /* + * Find the backing store object and offset into it + * to begin search. + */ + result = vm_map_lookup(&map, vaddr, VM_PROT_READ, &version, + &object, &offset, &prot, &wired); + if (result != KERN_SUCCESS) + return (result); + + /* + * Make a reference to this object to prevent its + * disposal while we are playing with it. + */ + assert(object->ref_count > 0); + object->ref_count++; + vm_object_paging_begin(object); + + result = vm_fault_page(object, offset, VM_PROT_READ, FALSE, TRUE, + &prot, &result_page, &top_page, + FALSE, (void (*)()) 0); + + if (result != VM_FAULT_SUCCESS) { + vm_object_deallocate(object); + + switch (result) { + case VM_FAULT_RETRY: + goto RetryFault; + case VM_FAULT_INTERRUPTED: + return (KERN_SUCCESS); + case VM_FAULT_MEMORY_SHORTAGE: + VM_PAGE_WAIT((void (*)()) 0); + goto RetryFault; + case VM_FAULT_FICTITIOUS_SHORTAGE: + vm_page_more_fictitious(); + goto RetryFault; + case VM_FAULT_MEMORY_ERROR: + return (KERN_MEMORY_ERROR); + } + } + + m = result_page; + + /* + * How to clean up the result of vm_fault_page. This + * happens whether the mapping is entered or not. + */ + +#define UNLOCK_AND_DEALLOCATE \ + MACRO_BEGIN \ + vm_fault_cleanup(m->object, top_page); \ + vm_object_deallocate(object); \ + MACRO_END + + /* + * What to do with the resulting page from vm_fault_page + * if it doesn't get entered into the physical map: + */ + +#define RELEASE_PAGE(m) \ + MACRO_BEGIN \ + PAGE_WAKEUP_DONE(m); \ + vm_page_lock_queues(); \ + if (!m->active && !m->inactive) \ + vm_page_activate(m); \ + vm_page_unlock_queues(); \ + MACRO_END + + /* + * We must verify that the maps have not changed. + */ + vm_object_unlock(m->object); + while (!vm_map_verify(map, &version)) { + vm_object_t retry_object; + vm_offset_t retry_offset; + vm_prot_t retry_prot; + + result = vm_map_lookup(&map, vaddr, VM_PROT_READ, &version, + &retry_object, &retry_offset, &retry_prot, + &wired); + if (result != KERN_SUCCESS) { + vm_object_lock(m->object); + RELEASE_PAGE(m); + UNLOCK_AND_DEALLOCATE; + return (result); + } + + vm_object_unlock(retry_object); + + if (retry_object != object || retry_offset != offset) { + vm_object_lock(m->object); + RELEASE_PAGE(m); + UNLOCK_AND_DEALLOCATE; + goto RetryFault; + } + } + + /* + * Put the page in the physical map. + */ + PMAP_ENTER(map->pmap, vaddr, m, VM_PROT_READ|VM_PROT_WRITE, wired); + + vm_object_lock(m->object); + vm_page_lock_queues(); + if (!m->active && !m->inactive) + vm_page_activate(m); + m->reference = TRUE; + vm_page_unlock_queues(); + + vm_map_verify_done(map, &version); + PAGE_WAKEUP_DONE(m); + + UNLOCK_AND_DEALLOCATE; + +#undef UNLOCK_AND_DEALLOCATE +#undef RELEASE_PAGE + + return (KERN_SUCCESS); +} +#endif diff --git a/i386/intel/read_fault.h b/i386/intel/read_fault.h new file mode 100644 index 0000000..8aa3f03 --- /dev/null +++ b/i386/intel/read_fault.h @@ -0,0 +1,35 @@ +/* + * Kernel read_fault on i386 functions + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Barry deFreese. + */ +/* + * Kernel read_fault on i386 functions. + * + */ + +#ifndef _READ_FAULT_H_ +#define _READ_FAULT_H_ + +#include + +extern kern_return_t intel_read_fault( + vm_map_t map, + vm_offset_t vaddr); + +#endif /* _READ_FAULT_H_ */ diff --git a/i386/ldscript b/i386/ldscript new file mode 100644 index 0000000..ddbbf91 --- /dev/null +++ b/i386/ldscript @@ -0,0 +1,201 @@ +/* Default linker script, for normal executables */ +OUTPUT_FORMAT("elf32-i386", "elf32-i386", + "elf32-i386") +OUTPUT_ARCH(i386) +ENTRY(_start) +SECTIONS +{ + /* + * There are specific requirements about entry points, so we have it + * configurable via `_START': `.text' will begin there and `.text.start' will + * be first in there. See also `i386/i386at/boothdr.S' and + * `gnumach_LINKFLAGS' in `i386/Makefrag.am'. + */ + . = _START; + .text : + AT (_START_MAP) + { + *(.text.start) + *(.text .stub .text.* .gnu.linkonce.t.*) + *(.text.unlikely .text.*_unlikely) + KEEP (*(.text.*personality*)) + /* .gnu.warning sections are handled specially by elf32.em. */ + *(.gnu.warning) + } =0x90909090 + .init : + { + KEEP (*(.init)) + } =0x90909090 + .fini : + { + KEEP (*(.fini)) + } =0x90909090 + PROVIDE (__etext = .); + PROVIDE (_etext = .); + PROVIDE (etext = .); + + /* Read-only sections, merged into text segment: */ + PROVIDE (__executable_start = .); + .interp : { *(.interp) } + .note.gnu.build-id : { *(.note.gnu.build-id) } + .hash : { *(.hash) } + .gnu.hash : { *(.gnu.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .rel.init : { *(.rel.init) } + .rel.text : { *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*) } + .rel.fini : { *(.rel.fini) } + .rel.rodata : { *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*) } + .rel.data.rel.ro : { *(.rel.data.rel.ro* .rel.gnu.linkonce.d.rel.ro.*) } + .rel.data : { *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*) } + .rel.tdata : { *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) } + .rel.tbss : { *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) } + .rel.ctors : { *(.rel.ctors) } + .rel.dtors : { *(.rel.dtors) } + .rel.got : { *(.rel.got) } + .rel.bss : { *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*) } + .rel.ifunc : { *(.rel.ifunc) } + .rel.plt : + { + *(.rel.plt) + PROVIDE_HIDDEN (__rel_iplt_start = .); + *(.rel.iplt) + PROVIDE_HIDDEN (__rel_iplt_end = .); + } + .plt : { *(.plt) *(.iplt) } + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) } + .rodata1 : { *(.rodata1) } + .eh_frame_hdr : { *(.eh_frame_hdr) } + .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) } + .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table + .gcc_except_table.*) } + /* Adjust the address for the data segment. We want to adjust up to + the same address within the page on the next page up. */ + . = ALIGN (CONSTANT (MAXPAGESIZE)) - ((CONSTANT (MAXPAGESIZE) - .) & (CONSTANT (MAXPAGESIZE) - 1)); . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE)); + /* Exception handling */ + .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) } + .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } + /* Thread Local Storage sections */ + .tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) } + .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) } + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + } + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array)) + PROVIDE_HIDDEN (__fini_array_end = .); + } + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + .jcr : { KEEP (*(.jcr)) } + .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro* .gnu.linkonce.d.rel.ro.*) } + .dynamic : { *(.dynamic) } + .got : { *(.got) *(.igot) } + . = DATA_SEGMENT_RELRO_END (12, .); + .got.plt : { *(.got.plt) *(.igot.plt) } + .data : + { + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + .data1 : { *(.data1) } + _edata = .; PROVIDE (edata = .); + __bss_start = .; + .bss : + { + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + /* Align here to ensure that the .bss section occupies space up to + _end. Align after .bss to ensure correct alignment even if the + .bss section disappears because there are no input sections. + FIXME: Why do we need it? When there is no .bss section, we don't + pad the .data section. */ + . = ALIGN(. != 0 ? 32 / 8 : 1); + } + . = ALIGN(32 / 8); + . = ALIGN(32 / 8); + _end = .; PROVIDE (end = .); + . = DATA_SEGMENT_END (.); + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + /* DWARF 3 */ + .debug_pubtypes 0 : { *(.debug_pubtypes) } + .debug_ranges 0 : { *(.debug_ranges) } + .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) } + /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) } +} diff --git a/i386/linux/Makefrag.am b/i386/linux/Makefrag.am new file mode 100644 index 0000000..87b1ae2 --- /dev/null +++ b/i386/linux/Makefrag.am @@ -0,0 +1,25 @@ +# Makefile fragment for i386-specific Linux code. + +# Copyright (C) 2006, 2007 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +# +# Files for device driver support. +# + +liblinux_a_SOURCES += \ + i386/linux/dev/include/linux/autoconf.h \ + linux/src/arch/i386/lib/semaphore.S diff --git a/i386/linux/dev/include/linux/autoconf.h b/i386/linux/dev/include/linux/autoconf.h new file mode 100644 index 0000000..bd035d4 --- /dev/null +++ b/i386/linux/dev/include/linux/autoconf.h @@ -0,0 +1,284 @@ +/* + * Automatically generated C config: don't edit + */ +#define AUTOCONF_INCLUDED + +/* + * Code maturity level options + */ +#define CONFIG_EXPERIMENTAL 1 + +/* + * Loadable module support + */ +#undef CONFIG_MODULES + +/* + * General setup + */ +#undef CONFIG_MATH_EMULATION +#define CONFIG_NET 1 +#undef CONFIG_MAX_16M +#define CONFIG_PCI 1 +#define CONFIG_PCI_OPTIMIZE 1 +#define CONFIG_SYSVIPC 1 +#undef CONFIG_BINFMT_AOUT +#define CONFIG_BINFMT_ELF 1 +#undef CONFIG_BINFMT_JAVA +#define CONFIG_KERNEL_ELF 1 + +#if 0 +#undef CONFIG_M386 +#define CONFIG_M486 1 +#undef CONFIG_M586 +#undef CONFIG_M686 +#endif + +#if NCPUS > 1 +#define CONFIG_SMP 1 +#endif + +/* + * Floppy, IDE, and other block devices + */ +#if 0 +#define CONFIG_BLK_DEV_FD 1 +#define CONFIG_BLK_DEV_IDE 1 +#endif + +/* + * Please see Documentation/ide.txt for help/info on IDE drives + */ +#undef CONFIG_BLK_DEV_HD_IDE +#define CONFIG_BLK_DEV_IDECD 1 +#undef CONFIG_BLK_DEV_IDETAPE +#undef CONFIG_BLK_DEV_IDEFLOPPY +#undef CONFIG_BLK_DEV_IDESCSI +#undef CONFIG_BLK_DEV_IDE_PCMCIA +#undef CONFIG_BLK_DEV_CMD640 +#undef CONFIG_BLK_DEV_CMD640_ENHANCED +#define CONFIG_BLK_DEV_RZ1000 1 +#define CONFIG_BLK_DEV_TRITON 1 +#undef CONFIG_IDE_CHIPSETS + +/* + * Additional Block Devices + */ +#undef CONFIG_BLK_DEV_LOOP +#undef CONFIG_BLK_DEV_MD +#undef CONFIG_BLK_DEV_RAM +#undef CONFIG_BLK_DEV_XD +#undef CONFIG_BLK_DEV_HD + +/* + * Networking options + */ +#if 0 +#undef CONFIG_FIREWALL +#undef CONFIG_NET_ALIAS +#define CONFIG_INET 1 +#undef CONFIG_IP_FORWARD +#undef CONFIG_IP_MULTICAST +#undef CONFIG_SYN_COOKIES +#undef CONFIG_RST_COOKIES +#undef CONFIG_IP_ACCT +#undef CONFIG_IP_ROUTER +#undef CONFIG_NET_IPIP +#endif + +/* + * (it is safe to leave these untouched) + */ +#undef CONFIG_INET_PCTCP +#undef CONFIG_INET_RARP +#undef CONFIG_NO_PATH_MTU_DISCOVERY +#undef CONFIG_IP_NOSR +#undef CONFIG_SKB_LARGE + +/* + * + */ +#undef CONFIG_IPX +#undef CONFIG_ATALK +#undef CONFIG_AX25 +#undef CONFIG_BRIDGE +#undef CONFIG_NETLINK + +/* + * SCSI support + */ +#if 0 +#define CONFIG_SCSI 1 +#endif + +/* + * SCSI support type (disk, tape, CD-ROM) + */ +#define CONFIG_BLK_DEV_SD 1 +#undef CONFIG_CHR_DEV_ST +#define CONFIG_BLK_DEV_SR 1 +#undef CONFIG_CHR_DEV_SG + +/* + * Some SCSI devices (e.g. CD jukebox) support multiple LUNs + */ +#if 0 +#undef CONFIG_SCSI_MULTI_LUN +#undef CONFIG_SCSI_CONSTANTS + +/* + * SCSI low-level drivers + */ +#undef CONFIG_SCSI_7000FASST +#undef CONFIG_SCSI_AHA152X +#undef CONFIG_SCSI_AHA1542 +#undef CONFIG_SCSI_AHA1740 +#undef CONFIG_SCSI_AIC7XXX +#undef CONFIG_SCSI_ADVANSYS +#undef CONFIG_SCSI_IN2000 +#undef CONFIG_SCSI_AM53C974 +#undef CONFIG_SCSI_BUSLOGIC +#undef CONFIG_SCSI_DTC3280 +#undef CONFIG_SCSI_EATA_DMA +#undef CONFIG_SCSI_EATA_PIO +#undef CONFIG_SCSI_EATA +#undef CONFIG_SCSI_FUTURE_DOMAIN +#undef CONFIG_SCSI_GENERIC_NCR5380 +#undef CONFIG_SCSI_NCR53C406A +#undef CONFIG_SCSI_NCR53C7xx +#undef CONFIG_SCSI_NCR53C8XX +#undef CONFIG_SCSI_DC390W +#undef CONFIG_SCSI_PPA +#undef CONFIG_SCSI_PAS16 +#undef CONFIG_SCSI_QLOGIC_FAS +#undef CONFIG_SCSI_QLOGIC_ISP +#undef CONFIG_SCSI_SEAGATE +#undef CONFIG_SCSI_DC390T +#undef CONFIG_SCSI_T128 +#undef CONFIG_SCSI_U14_34F +#undef CONFIG_SCSI_ULTRASTOR +#undef CONFIG_SCSI_GDTH +#endif + +/* + * Network device support + */ +#define CONFIG_NETDEVICES 1 +#undef CONFIG_DUMMY +#undef CONFIG_EQUALIZER +#undef CONFIG_DLCI +#undef CONFIG_PLIP +#undef CONFIG_PPP +#undef CONFIG_SLIP +#undef CONFIG_NET_RADIO + +#if 0 +#define CONFIG_NET_ETHERNET 1 +#define CONFIG_NET_VENDOR_3COM 1 +#undef CONFIG_EL1 +#undef CONFIG_EL2 +#undef CONFIG_ELPLUS +#undef CONFIG_EL16 +#undef CONFIG_EL3 +#undef CONFIG_VORTEX +#undef CONFIG_LANCE +#undef CONFIG_NET_VENDOR_SMC +#define CONFIG_NET_ISA 1 +#undef CONFIG_AT1700 +#undef CONFIG_E2100 +#undef CONFIG_DEPCA +#undef CONFIG_EWRK3 +#undef CONFIG_EEXPRESS +#undef CONFIG_EEXPRESS_PRO +#undef CONFIG_FMV18X +#undef CONFIG_HPLAN_PLUS +#undef CONFIG_HPLAN +#undef CONFIG_HP100 +#undef CONFIG_ETH16I +#undef CONFIG_NE2000 +#undef CONFIG_NI52 +#undef CONFIG_NI65 +#undef CONFIG_SEEQ8005 +#undef CONFIG_SK_G16 +#undef CONFIG_NET_EISA +#undef CONFIG_NET_POCKET +#undef CONFIG_TR +#undef CONFIG_FDDI +#undef CONFIG_ARCNET +#endif + +/* + * ISDN subsystem + */ +#undef CONFIG_ISDN + +/* + * CD-ROM drivers (not for SCSI or IDE/ATAPI drives) + */ +#undef CONFIG_CD_NO_IDESCSI + +/* + * Filesystems + */ +#undef CONFIG_QUOTA +#define CONFIG_MINIX_FS 1 +#undef CONFIG_EXT_FS +#define CONFIG_EXT2_FS 1 +#undef CONFIG_XIA_FS +#define CONFIG_FAT_FS 1 +#define CONFIG_MSDOS_FS 1 +#define CONFIG_VFAT_FS 1 +#define CONFIG_UMSDOS_FS 1 +#define CONFIG_PROC_FS 1 +#define CONFIG_NFS_FS 1 +#undef CONFIG_ROOT_NFS +#undef CONFIG_SMB_FS +#define CONFIG_ISO9660_FS 1 +#define CONFIG_HPFS_FS 1 +#define CONFIG_SYSV_FS 1 +#undef CONFIG_AUTOFS_FS +#define CONFIG_AFFS_FS 1 +#undef CONFIG_AMIGA_PARTITION +#define CONFIG_UFS_FS 1 + +/* We want Linux's partitioning code to do only the DOS partition table, + since the Mach glue code does BSD disklabels for us. */ +#undef CONFIG_BSD_DISKLABEL +#undef CONFIG_SMD_DISKLABEL + +#define CONFIG_GPT_DISKLABEL 1 + +/* + * Character devices + */ +#if 0 +#define CONFIG_SERIAL 1 +#undef CONFIG_DIGI +#undef CONFIG_CYCLADES +#undef CONFIG_STALDRV +#undef CONFIG_RISCOM8 +#define CONFIG_PRINTER 1 +#undef CONFIG_SPECIALIX +#define CONFIG_MOUSE 1 +#undef CONFIG_ATIXL_BUSMOUSE +#undef CONFIG_BUSMOUSE +#undef CONFIG_MS_BUSMOUSE +#define CONFIG_PSMOUSE 1 +#undef CONFIG_82C710_MOUSE +#undef CONFIG_UMISC +#undef CONFIG_QIC02_TAPE +#undef CONFIG_FTAPE +#undef CONFIG_APM +#undef CONFIG_WATCHDOG +#undef CONFIG_RTC +#endif + +/* + * Sound + */ +#undef CONFIG_SOUND + +/* + * Kernel hacking + */ +#undef CONFIG_PROFILE diff --git a/i386/xen/Makefrag.am b/i386/xen/Makefrag.am new file mode 100644 index 0000000..ecb33ff --- /dev/null +++ b/i386/xen/Makefrag.am @@ -0,0 +1,34 @@ +# Makefile fragment for the ix86 specific part of the Xen platform. + +# Copyright (C) 2007 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +# +# Xen support. +# + +libkernel_a_SOURCES += \ + i386/xen/xen.c \ + i386/xen/xen_locore.S \ + i386/xen/xen_boothdr.S + + +if PLATFORM_xen +gnumach_LINKFLAGS += \ + --defsym _START=0xC0000000 \ + --defsym _START_MAP=0xC0000000 \ + -T '$(srcdir)'/i386/ldscript +endif diff --git a/i386/xen/xen.c b/i386/xen/xen.c new file mode 100644 index 0000000..5309675 --- /dev/null +++ b/i386/xen/xen.c @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2006-2009 Free Software Foundation + * + * This program is free software ; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation ; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY ; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the program ; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include + +unsigned long cr3; + +void hyp_failsafe_c_callback(struct failsafe_callback_regs *regs) { + printf("Fail-Safe callback!\n"); + printf("IP: %08X CS: %4X DS: %4X ES: %4X FS: %4X GS: %4X FLAGS %08X MASK %04X\n", regs->ip, regs->cs_and_mask & 0xffff, regs->ds, regs->es, regs->fs, regs->gs, regs->flags, regs->cs_and_mask >> 16); + panic("failsafe"); +} + +extern char return_to_iret[]; + +void hypclock_machine_intr(int old_ipl, void *ret_addr, struct i386_interrupt_state *regs, uint64_t delta) { + if (ret_addr == &return_to_iret) { + clock_interrupt(delta/1000, /* usec per tick */ + (regs->efl & EFL_VM) || /* user mode */ + ((regs->cs & 0x02) != 0), /* user mode */ + old_ipl == SPL0, /* base priority */ + regs->eip); /* interrupted eip */ + } else + clock_interrupt(delta/1000, FALSE, FALSE, 0); +} + +void hyp_p2m_init(void) { + unsigned long nb_pfns = vm_page_table_size(); +#ifdef MACH_PSEUDO_PHYS +#define P2M_PAGE_ENTRIES (PAGE_SIZE / sizeof(unsigned long)) + unsigned long *l3 = (unsigned long *)phystokv(pmap_grab_page()), *l2 = NULL; + unsigned long i; + + for (i = 0; i < (nb_pfns + P2M_PAGE_ENTRIES) / P2M_PAGE_ENTRIES; i++) { + if (!(i % P2M_PAGE_ENTRIES)) { + l2 = (unsigned long *) phystokv(pmap_grab_page()); + l3[i / P2M_PAGE_ENTRIES] = kv_to_mfn(l2); + } + l2[i % P2M_PAGE_ENTRIES] = kv_to_mfn(&mfn_list[i * P2M_PAGE_ENTRIES]); + } + + hyp_shared_info.arch.pfn_to_mfn_frame_list_list = kv_to_mfn(l3); +#endif + hyp_shared_info.arch.max_pfn = nb_pfns; +} diff --git a/i386/xen/xen_boothdr.S b/i386/xen/xen_boothdr.S new file mode 100644 index 0000000..4704c66 --- /dev/null +++ b/i386/xen/xen_boothdr.S @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2006-2011 Free Software Foundation + * + * This program is free software ; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation ; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY ; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the program ; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include + +.section __xen_guest + .ascii "GUEST_OS=GNU Mach" + .ascii ",GUEST_VERSION=1.3" + .ascii ",XEN_VER=xen-3.0" + .ascii ",VIRT_BASE=0xC0000000" + .ascii ",ELF_PADDR_OFFSET=0xC0000000" + .ascii ",HYPERCALL_PAGE=0x2" +#if PAE + .ascii ",PAE=yes[extended-cr3]" +#else + .ascii ",PAE=no" +#endif + .ascii ",LOADER=generic" +#ifdef MACH_PSEUDO_PHYS + .ascii ",FEATURES=pae_pgdir_above_4gb" +#else /* MACH_PSEUDO_PHYS */ + .ascii ",FEATURES=!auto_translated_physmap" +#endif +#ifndef MACH_PV_PAGETABLES + .ascii "|!writable_page_tables" +#endif /* MACH_PV_PAGETABLES */ +#ifndef MACH_PV_DESCRIPTORS + .ascii "|!writable_descriptor_tables" +#endif /* MACH_PV_DESCRIPTORS */ +#ifndef MACH_RING1 + .ascii "|!supervisor_mode_kernel" +#endif /* MACH_PV_DESCRIPTORS */ + .byte 0 + +/* Macro taken from linux/include/linux/elfnote.h */ +#define ELFNOTE(name, type, desctype, descdata) \ +.pushsection .note.name ; \ + .align 4 ; \ + .long 2f - 1f /* namesz */ ; \ + .long 4f - 3f /* descsz */ ; \ + .long type ; \ +1:.asciz "name" ; \ +2:.align 4 ; \ +3:desctype descdata ; \ +4:.align 4 ; \ +.popsection ; + + ELFNOTE(Xen, XEN_ELFNOTE_GUEST_OS, .asciz, "GNU Mach") + ELFNOTE(Xen, XEN_ELFNOTE_GUEST_VERSION, .asciz, "1.3") + ELFNOTE(Xen, XEN_ELFNOTE_XEN_VERSION, .asciz, "xen-3.0") + ELFNOTE(Xen, XEN_ELFNOTE_VIRT_BASE, .long, _START) + ELFNOTE(Xen, XEN_ELFNOTE_PADDR_OFFSET, .long, _START) + ELFNOTE(Xen, XEN_ELFNOTE_ENTRY, .long, start) + ELFNOTE(Xen, XEN_ELFNOTE_HYPERCALL_PAGE, .long, hypcalls) +#if PAE + ELFNOTE(Xen, XEN_ELFNOTE_PAE_MODE, .asciz, "yes[extended-cr3]") +#else + ELFNOTE(Xen, XEN_ELFNOTE_PAE_MODE, .asciz, "no") +#endif + ELFNOTE(Xen, XEN_ELFNOTE_LOADER, .asciz, "generic") + ELFNOTE(Xen, XEN_ELFNOTE_FEATURES, .asciz, "" +#ifdef MACH_PSEUDO_PHYS + "pae_pgdir_above_4gb" +#else /* MACH_PSEUDO_PHYS */ + "!auto_translated_physmap" +#endif +#ifndef MACH_PV_PAGETABLES + "|!writable_page_tables" +#endif /* MACH_PV_PAGETABLES */ +#ifndef MACH_PV_DESCRIPTORS + "|!writable_descriptor_tables" +#endif /* MACH_PV_DESCRIPTORS */ +#ifndef MACH_RING1 + "|!supervisor_mode_kernel" +#endif /* MACH_RING1 */ + ) + +#include + +#include + + .text + .globl gdt, ldt + .globl start, _start, gdt +start: +_start: + + /* Switch to our own interrupt stack. */ + movl $(_intstack+INTSTACK_SIZE),%eax + movl %eax,%esp + + /* Reset EFLAGS to a known state. */ + pushl $0 + popf + + /* Push the start_info pointer to be the second argument. */ + subl $KERNELBASE,%esi + pushl %esi + + /* Fix ifunc entries */ + movl $__rel_iplt_start,%esi + movl $__rel_iplt_end,%edi +iplt_cont: + cmpl %edi,%esi + jae iplt_done + movl (%esi),%ebx /* r_offset */ + movb 4(%esi),%al /* info */ + cmpb $42,%al /* IRELATIVE */ + jnz iplt_next + call *(%ebx) /* call ifunc */ + movl %eax,(%ebx) /* fixed address */ +iplt_next: + addl $8,%esi + jmp iplt_cont +iplt_done: + + /* Jump into C code. */ + call EXT(c_boot_entry) + +/* Those need to be aligned on page boundaries. */ +.global hyp_shared_info, hypcalls + + .org (start + 0x1000) +hyp_shared_info: + .org hyp_shared_info + 0x1000 + +/* Labels just for debuggers */ +#define hypcall(name, n) \ + .org hypcalls + n*32 ; \ +.globl __hyp_##name ; \ +__hyp_##name: + +hypcalls: + hypcall(set_trap_table, 0) + hypcall(mmu_update, 1) + hypcall(set_gdt, 2) + hypcall(stack_switch, 3) + hypcall(set_callbacks, 4) + hypcall(fpu_taskswitch, 5) + hypcall(sched_op_compat, 6) + hypcall(platform_op, 7) + hypcall(set_debugreg, 8) + hypcall(get_debugreg, 9) + hypcall(update_descriptor, 10) + hypcall(memory_op, 12) + hypcall(multicall, 13) + hypcall(update_va_mapping, 14) + hypcall(set_timer_op, 15) + hypcall(event_channel_op_compat, 16) + hypcall(xen_version, 17) + hypcall(console_io, 18) + hypcall(physdev_op_compat, 19) + hypcall(grant_table_op, 20) + hypcall(vm_assist, 21) + hypcall(update_va_mapping_otherdomain, 22) + hypcall(iret, 23) + hypcall(vcpu_op, 24) + hypcall(set_segment_base, 25) + hypcall(mmuext_op, 26) + hypcall(acm_op, 27) + hypcall(nmi_op, 28) + hypcall(sched_op, 29) + hypcall(callback_op, 30) + hypcall(xenoprof_op, 31) + hypcall(event_channel_op, 32) + hypcall(physdev_op, 33) + hypcall(hvm_op, 34) + hypcall(sysctl, 35) + hypcall(domctl, 36) + hypcall(kexec_op, 37) + + hypcall(arch_0, 48) + hypcall(arch_1, 49) + hypcall(arch_2, 50) + hypcall(arch_3, 51) + hypcall(arch_4, 52) + hypcall(arch_5, 53) + hypcall(arch_6, 54) + hypcall(arch_7, 55) + + .org hypcalls + 0x1000 + +gdt: + .org gdt + 0x1000 + +ldt: + .org ldt + 0x1000 + +stack: + .long _intstack+INTSTACK_SIZE,0xe021 + .comm _intstack,INTSTACK_SIZE + .comm _eintstack,0 + diff --git a/i386/xen/xen_locore.S b/i386/xen/xen_locore.S new file mode 100644 index 0000000..1468ef8 --- /dev/null +++ b/i386/xen/xen_locore.S @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2006-2009 Free Software Foundation + * + * This program is free software ; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation ; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY ; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the program ; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include + +#include +#include +#include + + .data 2 +int_active: + .long 0 + + + .text + .globl hyp_callback, hyp_failsafe_callback + P2ALIGN(TEXT_ALIGN) +hyp_callback: + pushl %eax + jmp EXT(all_intrs) + +ENTRY(interrupt) + incl int_active /* currently handling interrupts */ + call EXT(hyp_c_callback) /* call generic interrupt routine */ + decl int_active /* stopped handling interrupts */ + sti + ret + +/* FIXME: if we're _very_ unlucky, we may be re-interrupted, filling stack + * + * Far from trivial, see mini-os. That said, maybe we could just, before poping + * everything (which is _not_ destructive), save sp into a known place and use + * it+jmp back? + * + * Mmm, there seems to be an iret hypcall that does exactly what we want: + * perform iret, and if IF is set, clear the interrupt mask. + */ + +/* Pfff, we have to check pending interrupts ourselves. Some other DomUs just make an hypercall for retriggering the irq. Not sure it's really easier/faster */ +ENTRY(hyp_sti) + pushl %ebp + movl %esp, %ebp +_hyp_sti: + movb $0,hyp_shared_info+CPU_CLI /* Enable interrupts */ + cmpl $0,int_active /* Check whether we were already checking pending interrupts */ + jz 0f + popl %ebp + ret /* Already active, just return */ +0: + /* Not active, check pending interrupts by hand */ + /* no memory barrier needed on x86 */ + cmpb $0,hyp_shared_info+CPU_PENDING + jne 0f + popl %ebp + ret +0: + movb $0xff,hyp_shared_info+CPU_CLI +1: + pushl %eax + pushl %ecx + pushl %edx + incl int_active /* currently handling interrupts */ + + pushl $0 + pushl $0 + call EXT(hyp_c_callback) + popl %edx + popl %edx + + popl %edx + popl %ecx + popl %eax + decl int_active /* stopped handling interrupts */ + cmpb $0,hyp_shared_info+CPU_PENDING + jne 1b + jmp _hyp_sti + +/* Hypervisor failed to reload segments. Dump them. */ +hyp_failsafe_callback: +#if 1 + /* load sane segments */ + mov %ss, %ax + mov %ax, %ds + mov %ax, %es + mov %ax, %fs + mov %ax, %gs + push %esp + call EXT(hyp_failsafe_c_callback) +#else + popl %ds + popl %es + popl %fs + popl %gs + iret +#endif diff --git a/include/alloca.h b/include/alloca.h new file mode 100644 index 0000000..29081ca --- /dev/null +++ b/include/alloca.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 1994 The University of Utah and + * the Computer Systems Laboratory (CSL). 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. + * + * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS + * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF + * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * CSL requests users of this software to return to csl-dist@cs.utah.edu any + * improvements that they make and grant CSL redistribution rights. + * + * Author: Bryan Ford, University of Utah CSL + */ +#ifndef _MACH_ALLOCA_H_ +#define _MACH_ALLOCA_H_ + +#define alloca(size) __builtin_alloca(size) + +#endif /* _MACH_ALLOCA_H_ */ diff --git a/include/cache.h b/include/cache.h new file mode 100644 index 0000000..6260366 --- /dev/null +++ b/include/cache.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2013 Free Software Foundation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef _MACH_CACHE_H_ +#define _MACH_CACHE_H_ + +/* This macro can be used to align statically allocated objects so + that they start at a cache line. */ +#define __cacheline_aligned __attribute__((aligned(1 << CPU_L1_SHIFT))) + +#endif /* _MACH_CACHE_H_ */ diff --git a/include/device/audio_status.h b/include/device/audio_status.h new file mode 100644 index 0000000..7effe99 --- /dev/null +++ b/include/device/audio_status.h @@ -0,0 +1,164 @@ +/* + * Mach Operating System + * Copyright (c) 1993 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. + */ +/* + * Copyright (c) 1991, 1992 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. The name of the Laboratory may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 _DEVICE_AUDIO_STATUS_H_ +#define _DEVICE_AUDIO_STATUS_H_ + +/* + * Access to ADC devices, such as the AMD 79C30A/32A. + */ + +/* + * Programmable gains, see tables in device drivers + * for detailed mapping to device specifics. + */ +#define AUDIO_MIN_GAIN (0) +#define AUDIO_MAX_GAIN (255) + +/* + * Encoding of audio samples + */ +#define AUDIO_ENCODING_ULAW (1) +#define AUDIO_ENCODING_ALAW (2) + +/* + * Selection of input/output jack + */ +#define AUDIO_MIKE 1 + +#define AUDIO_SPEAKER 1 +#define AUDIO_HEADPHONE 2 + +/* + * Programming information from/to user application. + * Only portions of this might be available on any given chip. + */ +struct audio_prinfo { + unsigned int sample_rate; + unsigned int channels; + unsigned int precision; + unsigned int encoding; + unsigned int gain; + unsigned int port; /* input/output jack */ + unsigned int seek; /* BSD extension */ + unsigned int ispare[3]; + unsigned int samples; + unsigned int eof; + + unsigned char pause; + unsigned char error; + unsigned char waiting; + unsigned char cspare[3]; + unsigned char open; + unsigned char active; + +}; + +struct audio_info { + struct audio_prinfo play; + struct audio_prinfo record; + unsigned int monitor_gain; + /* BSD extensions */ + unsigned int blocksize; /* input blocking threshold */ + unsigned int hiwat; /* output high water mark */ + unsigned int lowat; /* output low water mark */ + unsigned int backlog; /* samples of output backlog to gen. */ +}; + +typedef struct audio_info audio_info_t; + +#define AUDIO_INITINFO(p)\ + (void)memset((void *)(p), 0xff, sizeof(struct audio_info)) + +#define AUDIO_GETINFO _IOR('A', 21, audio_info_t) +#define AUDIO_SETINFO _IOWR('A', 22, audio_info_t) +#define AUDIO_DRAIN _IO('A', 23) +#define AUDIO_FLUSH _IO('A', 24) +#define AUDIO_WSEEK _IOR('A', 25, unsigned int) +#define AUDIO_RERROR _IOR('A', 26, int) +#define AUDIO_WERROR _IOR('A', 27, int) + +/* + * Low level interface to the amd79c30. + * Internal registers of the MAP block, + * the Main Audio Processor. + */ +struct mapreg { + unsigned short mr_x[8]; + unsigned short mr_r[8]; + unsigned short mr_gx; + unsigned short mr_gr; + unsigned short mr_ger; + unsigned short mr_stgr; + unsigned short mr_ftgr; + unsigned short mr_atgr; + unsigned char mr_mmr1; + unsigned char mr_mmr2; +}; + +#define AUDIO_GETMAP _IOR('A', 27, struct mapreg) +#define AUDIO_SETMAP _IOW('A', 28, struct mapreg) + +/* + * Compatibility with Sun interface + */ +struct audio_ioctl { + short control; + unsigned char data[46]; +}; + +#define AUDIOGETREG _IOWR('i',1,struct audio_ioctl) +#define AUDIOSETREG _IOW('i',2,struct audio_ioctl) + +#endif /* _DEVICE_AUDIO_STATUS_H_ */ diff --git a/include/device/bpf.h b/include/device/bpf.h new file mode 100644 index 0000000..abc2d77 --- /dev/null +++ b/include/device/bpf.h @@ -0,0 +1,244 @@ +/* + * Mach Operating System + * Copyright (c) 1993 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. + */ + +/* + * Berkeley Packet Filter Definitions from Berkeley + */ + +/*- + * Copyright (c) 1990-1991 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. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 7.1 (Berkeley) 5/7/91 + * + */ + +#ifndef _DEVICE_BPF_H_ +#define _DEVICE_BPF_H_ + +/* + * Alignment macros. BPF_WORDALIGN rounds up to the next + * even multiple of BPF_ALIGNMENT. + */ +#define BPF_ALIGNMENT sizeof(int) +#define BPF_WORDALIGN(x) (((x)+(BPF_ALIGNMENT-1))&~(BPF_ALIGNMENT-1)) + +/* + * 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 { + unsigned short bv_major; + unsigned short bv_minor; +}; +/* Current version number. */ +#define BPF_MAJOR_VERSION 1 +#define BPF_MINOR_VERSION 1 + +/* + * 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. + */ + +/* Magic number and flags for the first instruction */ +#define BPF_BEGIN NETF_BPF +#define BPF_IN NETF_IN +#define BPF_OUT NETF_OUT + +/* 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_CKMATCH_IMM 0x50 +#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) & 0x38) +#define BPF_A 0x10 +#define BPF_MATCH_IMM 0x18 +#define BPF_MATCH_DATA 0x20 + +/* misc */ +#define BPF_MISCOP(code) ((code) & 0xf8) +#define BPF_TAX 0x00 +#define BPF_TXA 0x80 +#define BPF_KEY 0x10 +#define BPF_REG_DATA 0x18 +#define BPF_POSTPONE 0x20 + +/* + * The instruction data structure. + */ +struct bpf_insn { + unsigned short code; + unsigned char jt; + unsigned char jf; + int k; +}; +typedef struct bpf_insn *bpf_insn_t; + +/* + * largest bpf program size + */ +#define NET_MAX_BPF ((NET_MAX_FILTER*sizeof(filter_t))/sizeof(struct bpf_insn)) + +/* + * Macros for insn array initializers. + */ +#define BPF_STMT(code, k) { (unsigned short)(code), 0, 0, k } +#define BPF_JUMP(code, k, jt, jf) { (unsigned short)(code), jt, jf, k } +#define BPF_RETMATCH(code, k, nkey) { (unsigned short)(code), nkey, 0, k } + +#define BPF_INSN_STMT(pc, c, n) {\ + (pc)->code = (c); \ + (pc)->jt = (pc)->jf = 0; \ + (pc)->k = (n); \ + (pc)++; \ +} + +#define BPF_INSN_JUMP(pc, c, n, jtrue, jfalse) {\ + (pc)->code = (c); \ + (pc)->jt = (jtrue); \ + (pc)->jf = (jfalse); \ + (pc)->k = (n); \ + (pc)++; \ +} + +#define BPF_INSN_RETMATCH(pc, c, n, nkey) {\ + (pc)->code = (c); \ + (pc)->jt = (nkey); \ + (pc)->jf = 0; \ + (pc)->k = (n); \ + (pc)++; \ +} + +/* + * Number of scratch memory words (for BPF_LD|BPF_MEM and BPF_ST). + */ +#define BPF_MEMWORDS 16 + +/* + * Link level header can be accessed by adding BPF_DLBASE to an offset. + */ +#define BPF_DLBASE (1<<30) + +#define BPF_BYTES(n) ((n) * sizeof (struct bpf_insn)) +#define BPF_BYTES2LEN(n) ((n) / sizeof (struct bpf_insn)) +#define BPF_INSN_EQ(p,q) ((p)->code == (q)->code && \ + (p)->jt == (q)->jt && \ + (p)->jf == (q)->jf && \ + (p)->k == (q)->k) + +#endif /* _DEVICE_BPF_H_ */ diff --git a/include/device/device.defs b/include/device/device.defs new file mode 100644 index 0000000..7f31612 --- /dev/null +++ b/include/device/device.defs @@ -0,0 +1,183 @@ +/* + * 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. + */ +/* + * File: device/device.defs + * Author: Douglas Orr + * Feb 10, 1988 + * Abstract: + * Mach device support. Mach devices are accessed through + * block and character device interfaces to the kernel. + */ + +subsystem +#if KERNEL_SERVER + KernelServer +#endif + device 2800; + +#include +#include +#include + +serverprefix ds_; + +type reply_port_t = MACH_MSG_TYPE_MAKE_SEND_ONCE | polymorphic + ctype: mach_port_t +#ifndef KERNEL_SERVER +#ifdef MACH_PAYLOAD_TO_PORT + intranpayload: mach_port_t MACH_PAYLOAD_TO_PORT +#endif /* MACH_PAYLOAD_TO_PORT */ +#endif /* KERNEL_SERVER */ +; + +/* Deprecated in favor of device_open_new. */ +routine device_open( + master_port : mach_port_t; + sreplyport reply_port : reply_port_t; + mode : dev_mode_t; + name : dev_name_t; + out device : device_t = + MACH_MSG_TYPE_PORT_SEND + ctype: mach_port_t +#if KERNEL_SERVER + outtran: mach_port_t convert_device_to_port(device_t) +#else +#ifdef DEVICE_OUTTRAN + outtran: DEVICE_OUTTRAN +#endif +#endif /* KERNEL_SERVER */ + ); + +routine device_close( + device : device_t + ); + +routine device_write( + device : device_t; + sreplyport reply_port : reply_port_t; + in mode : dev_mode_t; + in recnum : recnum_t; + in data : io_buf_ptr_t; + out bytes_written : int + ); + +routine device_write_inband( + device : device_t; + sreplyport reply_port : reply_port_t; + in mode : dev_mode_t; + in recnum : recnum_t; + in data : io_buf_ptr_inband_t; + out bytes_written : int + ); + +routine device_read( + device : device_t; + sreplyport reply_port : reply_port_t; + in mode : dev_mode_t; + in recnum : recnum_t; + in bytes_wanted : int; + out data : io_buf_ptr_t, dealloc + ); + +routine device_read_inband( + device : device_t; + sreplyport reply_port : reply_port_t; + in mode : dev_mode_t; + in recnum : recnum_t; + in bytes_wanted : int; + out data : io_buf_ptr_inband_t + ); + +#if defined(KERNEL_SERVER) || defined(DEVICE_ENABLE_DEVICE_OPEN_NEW) +routine device_open_new( + master_port : mach_port_t; + sreplyport reply_port : reply_port_t; + mode : dev_mode_t; + name : new_dev_name_t; + out device : device_t = + MACH_MSG_TYPE_PORT_SEND + ctype: mach_port_t +#if KERNEL_SERVER + outtran: mach_port_t convert_device_to_port(device_t) +#else +#ifdef DEVICE_OUTTRAN + outtran: DEVICE_OUTTRAN +#endif +#endif /* KERNEL_SERVER */ + ); +#else +skip; /* old xxx_device_set_status */ +#endif + +skip; /* old xxx_device_get_status */ +skip; /* old xxx_device_set_filter*/ + +routine device_map( + device : device_t; + in prot : vm_prot_t; + in offset : vm_offset_t; + in size : vm_size_t; + out pager : memory_object_t; + in unmap : int + ); + +routine device_set_status( + device : device_t; + in flavor : dev_flavor_t; + in status : dev_status_t + ); + +routine device_get_status( + device : device_t; + in flavor : dev_flavor_t; + out status : dev_status_t, CountInOut + ); + +routine device_set_filter( + device : device_t; + in receive_port : mach_port_send_t; + in priority : int; + in filter : filter_array_t + ); + +routine device_intr_register( + device : device_t; + in id : int; + in flags : int; + in receive_port : mach_port_send_t + ); + +/* + * Acknowledge the specified interrupt notification. + */ +/* + * When an IRQ happens and an intr notification is thus sent, the IRQ line + * is kept disabled until the notification is acknowledged with this RPC + */ +routine device_intr_ack( + device : device_t; + in receive_port : mach_port_send_t); + diff --git a/include/device/device_reply.defs b/include/device/device_reply.defs new file mode 100644 index 0000000..5a32507 --- /dev/null +++ b/include/device/device_reply.defs @@ -0,0 +1,110 @@ +/* + * 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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 8/89 + * + * Reply-only side of device interface. + */ + +subsystem +#if KERNEL_USER + KernelUser +#endif + device_reply 2900; + /* to match reply numbers for device.defs */ + +/* + * Device_write_reply (only user of this data type) deallocates + * the data. + */ + + +#include +#include + +userprefix ds_; + +#if SEQNOS +serverprefix seqnos_; +serverdemux seqnos_device_reply_server; +#endif /* SEQNOS */ + +type reply_port_t = polymorphic|MACH_MSG_TYPE_PORT_SEND_ONCE + ctype: mach_port_t +#ifndef KERNEL_SERVER +#ifdef MACH_PAYLOAD_TO_PORT + intranpayload: mach_port_t MACH_PAYLOAD_TO_PORT +#endif /* MACH_PAYLOAD_TO_PORT */ +#endif /* KERNEL_SERVER */ +; + +simpleroutine device_open_reply( + reply_port : reply_port_t; +#if SEQNOS + msgseqno seqno : mach_port_seqno_t; +#endif /* SEQNOS */ + in return_code : kern_return_t; + in device_port : mach_port_make_send_t + ); + +skip; /* device_close */ + +simpleroutine device_write_reply( + reply_port : reply_port_t; +#if SEQNOS + msgseqno seqno : mach_port_seqno_t; +#endif /* SEQNOS */ + in return_code : kern_return_t; + in bytes_written : int + ); + +simpleroutine device_write_reply_inband( + reply_port : reply_port_t; +#if SEQNOS + msgseqno seqno : mach_port_seqno_t; +#endif /* SEQNOS */ + in return_code : kern_return_t; + in bytes_written : int + ); + +simpleroutine device_read_reply( + reply_port : reply_port_t; +#if SEQNOS + msgseqno seqno : mach_port_seqno_t; +#endif /* SEQNOS */ + in return_code : kern_return_t; + in data : io_buf_ptr_t, dealloc + ); + +simpleroutine device_read_reply_inband( + reply_port : reply_port_t; +#if SEQNOS + msgseqno seqno : mach_port_seqno_t; +#endif /* SEQNOS */ + in return_code : kern_return_t; + in data : io_buf_ptr_inband_t + ); diff --git a/include/device/device_request.defs b/include/device/device_request.defs new file mode 100644 index 0000000..a8af3a8 --- /dev/null +++ b/include/device/device_request.defs @@ -0,0 +1,95 @@ +/* + * 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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 8/89 + * + * Request-only side of device interface. + */ + +subsystem device_request 2800; /* to match device.defs */ + +#include + +serverprefix ds_; + +type reply_port_t = MACH_MSG_TYPE_MAKE_SEND_ONCE + ctype: mach_port_t +#ifndef KERNEL_SERVER +#ifdef MACH_PAYLOAD_TO_PORT + intranpayload: mach_port_t MACH_PAYLOAD_TO_PORT +#endif /* MACH_PAYLOAD_TO_PORT */ +#endif /* KERNEL_SERVER */ +; + +/* Deprecated in favor of device_open_new_request. */ +simpleroutine device_open_request( + device_server_port : mach_port_t; + ureplyport reply_port : reply_port_t; + in mode : dev_mode_t; + in name : dev_name_t + ); + +skip; /* device_close */ + +simpleroutine device_write_request( + device : device_t; + ureplyport reply_port : reply_port_t; + in mode : dev_mode_t; + in recnum : recnum_t; + in data : io_buf_ptr_t + ); + +simpleroutine device_write_request_inband( + device : device_t; + ureplyport reply_port : reply_port_t; + in mode : dev_mode_t; + in recnum : recnum_t; + in data : io_buf_ptr_inband_t + ); + +simpleroutine device_read_request( + device : device_t; + ureplyport reply_port : reply_port_t; + in mode : dev_mode_t; + in recnum : recnum_t; + in bytes_wanted : int + ); + +simpleroutine device_read_request_inband( + device : device_t; + ureplyport reply_port : reply_port_t; + in mode : dev_mode_t; + in recnum : recnum_t; + in bytes_wanted : int + ); + +simpleroutine device_open_new_request( + device_server_port : mach_port_t; + ureplyport reply_port : reply_port_t; + in mode : dev_mode_t; + in name : new_dev_name_t + ); diff --git a/include/device/device_types.defs b/include/device/device_types.defs new file mode 100644 index 0000000..c74bff5 --- /dev/null +++ b/include/device/device_types.defs @@ -0,0 +1,92 @@ +/* + * 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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 3/89 + * + * Common definitions for device interface types. + */ + +#ifndef _DEVICE_DEVICE_TYPES_DEFS_ +#define _DEVICE_DEVICE_TYPES_DEFS_ + +/* + * Basic types + */ + +#include + +#ifdef DEVICE_IMPORTS +DEVICE_IMPORTS +#endif + +type rpc_recnum_t = rpc_long_natural_t; +type recnum_t = rpc_recnum_t +#if defined(KERNEL_SERVER) + intran: recnum_t convert_long_natural_from_user(rpc_recnum_t) + outtran: rpc_recnum_t convert_long_natural_to_user(recnum_t) +#elif defined(KERNEL_USER) + ctype: rpc_recnum_t +#endif + ; + +type dev_mode_t = uint32_t; +type dev_flavor_t = uint32_t; +type dev_name_t = (MACH_MSG_TYPE_STRING_C, 8*128); +type new_dev_name_t = c_string[128] + ctype: dev_name_t; +type dev_status_t = array[*:1024] of int; +type io_buf_ptr_t = ^array[] of MACH_MSG_TYPE_INTEGER_8; +type io_buf_ptr_inband_t= array[*:128] of char; +type filter_t = short; +type filter_array_t = array[*:128] of filter_t; + +type device_t = mach_port_t + ctype: mach_port_t +#if KERNEL_SERVER + intran: device_t dev_port_lookup(mach_port_t) + outtran: mach_port_t convert_device_to_port(device_t) + destructor: device_deallocate(device_t) +#else /* KERNEL_SERVER */ +#ifdef DEVICE_INTRAN + intran: DEVICE_INTRAN +#endif +#ifdef DEVICE_INTRAN_PAYLOAD + intranpayload: DEVICE_INTRAN_PAYLOAD +#endif +#ifdef DEVICE_OUTTRAN + outtran: DEVICE_OUTTRAN +#endif +#ifdef DEVICE_DESTRUCTOR + destructor: DEVICE_DESTRUCTOR +#endif +#endif /* KERNEL_SERVER */ + ; + +import ; +import ; + +#endif /* _DEVICE_DEVICE_TYPES_DEFS_ */ diff --git a/include/device/device_types.h b/include/device/device_types.h new file mode 100644 index 0000000..583d9e0 --- /dev/null +++ b/include/device/device_types.h @@ -0,0 +1,148 @@ +/* + * 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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 3/89 + */ + +#ifndef DEVICE_TYPES_H +#define DEVICE_TYPES_H + +/* + * Types for device interface. + */ +#include + +#ifdef MACH_KERNEL +/* + * Get kernel-only type definitions. + */ +#include + +#else /* MACH_KERNEL */ +/* + * Device handle. + */ +typedef mach_port_t device_t; + +#endif /* MACH_KERNEL */ + +/* + * Device name string + */ +typedef char dev_name_t[128]; /* must match device_types.defs */ +typedef const char *const_dev_name_t; + +/* + * Mode for open/read/write + */ +typedef unsigned int dev_mode_t; +#define D_READ 0x1 /* read */ +#define D_WRITE 0x2 /* write */ +#define D_NODELAY 0x4 /* no delay on open */ +#define D_NOWAIT 0x8 /* do not wait if data not available */ + +/* + * IO buffer - out-of-line array of characters. + */ +typedef char * io_buf_ptr_t; +typedef const char * const_io_buf_ptr_t; + +/* + * IO buffer - in-line array of characters. + */ +#define IO_INBAND_MAX (128) /* must match device_types.defs */ +typedef char io_buf_ptr_inband_t[IO_INBAND_MAX]; +typedef const char *const_io_buf_ptr_inband_t; + +/* + * IO buffer vector - for scatter/gather IO. + */ +typedef struct { + vm_offset_t data; + vm_size_t count; +} io_buf_vec_t; +typedef struct { + rpc_vm_offset_t data; + rpc_vm_size_t count; +} rpc_io_buf_vec_t; + +/* + * Record number for random-access devices + */ +typedef long_natural_t recnum_t; +typedef rpc_long_natural_t rpc_recnum_t; + +/* + * Flavors of set/get statuses + */ +typedef unsigned int dev_flavor_t; + +/* + * Generic array for get/set status + */ +typedef int *dev_status_t; /* Variable-length array of integers */ +#define DEV_STATUS_MAX (1024) /* Maximum array size */ + +typedef int dev_status_data_t[DEV_STATUS_MAX]; + +/* + * Mandatory get/set status operations + */ + +/* size a device: op code and indexes for returned values */ +#define DEV_GET_SIZE 0 +# define DEV_GET_SIZE_DEVICE_SIZE 0 /* 0 if unknown */ +# define DEV_GET_SIZE_RECORD_SIZE 1 /* 1 if sequential */ +#define DEV_GET_SIZE_COUNT 2 +/* size a device in record numbers, not bytes */ +#define DEV_GET_RECORDS 1 +# define DEV_GET_RECORDS_DEVICE_RECORDS 0 /* 0 if unknown */ +# define DEV_GET_RECORDS_RECORD_SIZE 1 /* 1 if sequential */ +#define DEV_GET_RECORDS_COUNT 2 + +/* + * Device error codes + */ +typedef int io_return_t; + +#define D_IO_QUEUED (-1) /* IO queued - do not return result */ +#define D_SUCCESS 0 + +#define D_IO_ERROR 2500 /* hardware IO error */ +#define D_WOULD_BLOCK 2501 /* would block, but D_NOWAIT set */ +#define D_NO_SUCH_DEVICE 2502 /* no such device */ +#define D_ALREADY_OPEN 2503 /* exclusive-use device already open */ +#define D_DEVICE_DOWN 2504 /* device has been shut down */ +#define D_INVALID_OPERATION 2505 /* bad operation for device */ +#define D_INVALID_RECNUM 2506 /* invalid record (block) number */ +#define D_INVALID_SIZE 2507 /* invalid IO size */ +#define D_NO_MEMORY 2508 /* memory allocation failure */ +#define D_READ_ONLY 2509 /* device cannot be written to */ + +void device_deallocate(device_t); + +#endif /* DEVICE_TYPES_H */ diff --git a/include/device/disk_status.h b/include/device/disk_status.h new file mode 100644 index 0000000..a6ed106 --- /dev/null +++ b/include/device/disk_status.h @@ -0,0 +1,318 @@ +/* + * 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. + */ +/* + * Copyright (c) 1987, 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. + * 4. The name of the Laboratory may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 7.10 (Berkeley) 6/27/88 + */ + +#ifndef _DISK_STATUS_H_ +#define _DISK_STATUS_H_ + +/* + * 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. + */ + +#define LABELSECTOR 0 /* sector containing label */ +#define LABELOFFSET 64 /* offset of label in sector */ +#define DISKMAGIC ((unsigned int) 0x82564557U) /* The disk magic number */ +#ifndef MAXPARTITIONS +#define MAXPARTITIONS 8 +#endif + + +#ifndef LOCORE +struct disklabel { + unsigned int 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(MACH_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 /* ! MACH_KERNEL or STANDALONE */ + /* disk geometry: */ + unsigned int d_secsize; /* # of bytes per sector */ + unsigned int d_nsectors; /* # of data sectors per track */ + unsigned int d_ntracks; /* # of tracks per cylinder */ + unsigned int d_ncylinders; /* # of data cylinders per unit */ + unsigned int d_secpercyl; /* # of data sectors per cylinder */ + unsigned int 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. + */ + unsigned short d_sparespertrack; /* # of spare sectors per track */ + unsigned short d_sparespercyl; /* # of spare sectors per cylinder */ + /* + * Alternate cylinders include maintenance, replacement, + * configuration description areas, etc. + */ + unsigned int 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. + */ + unsigned short d_rpm; /* rotational speed */ + unsigned short d_interleave; /* hardware sector interleave */ + unsigned short d_trackskew; /* sector 0 skew, per track */ + unsigned short d_cylskew; /* sector 0 skew, per cylinder */ + unsigned int d_headswitch; /* head switch time, usec */ + unsigned int d_trkseek; /* track-to-track seek, usec */ + unsigned int d_flags; /* generic flags */ +#define NDDATA 5 + unsigned int d_drivedata[NDDATA]; /* drive-type specific information */ +#define NSPARE 5 + unsigned int d_spare[NSPARE]; /* reserved for future use */ + unsigned int d_magic2; /* the magic number (again) */ + unsigned short d_checksum; /* xor of data incl. partitions */ + + /* filesystem and partition information: */ + unsigned short d_npartitions; /* number of partitions in following */ + unsigned int d_bbsize; /* size of boot area at sn0, bytes */ + unsigned int d_sbsize; /* max size of fs superblock, bytes */ + struct partition { /* the partition table */ + unsigned int p_size; /* number of sectors in partition */ + unsigned int p_offset; /* starting sector */ + unsigned int p_fsize; /* filesystem basic fragment size */ + unsigned char p_fstype; /* filesystem type, see below */ + unsigned char p_frag; /* filesystem fragments per block */ + unsigned short p_cpg; /* filesystem cylinders per group */ + } d_partitions[MAXPARTITIONS+1]; /* actually may be more */ + +#if defined(alpha) && defined(MACH_KERNEL) + /* + * Disgusting hack. If this structure contains a pointer, + * as it does for non-kernel, then the compiler rounds + * the size to make it pointer-sized properly (arrays of..). + * But if I define the pointer for the kernel then instances + * of this structure better be aligned otherwise picking + * up a short might be done by too-smart compilers (GCC) with + * a load-long instruction expecting the short to be aligned. + * I bet the OSF folks stomped into this too, since they use + * the same disgusting hack below.. [whatelse can I do ??] + */ + int bugfix; +#endif +}; +#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_FLOPPY 10 /* floppy */ + +#ifdef DKTYPENAMES +static char *dktypenames[] = { + "unknown", + "SMD", + "MSCP", + "old DEC", + "SCSI", + "ESDI", + "type 6", + "type 7", + "type 8", + "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_LINUXFS 8 /* Linux file system */ + +#ifdef DKTYPENAMES +static char *fstypenames[] = { + "unused", + "swap", + "Version 6", + "Version 7", + "System V", + "4.1BSD", + "Eighth Edition", + "4.2BSD", + "Linux", + 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 */ + +/* + * IBM controller info (d_precompcyl used, too) + */ +#define d_step d_drivedata[2] + +#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 */ + recnum_t df_startblk; + int df_reg[8]; /* result */ +}; + +/* + * 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 */ + +/* 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 */ + +#endif /* _DISK_STATUS_H_ */ diff --git a/include/device/input.h b/include/device/input.h new file mode 100644 index 0000000..9de73a3 --- /dev/null +++ b/include/device/input.h @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2023 Free Software Foundation, Inc. + * + * This file is part of GNU Mach. + * + * GNU Mach is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _DEVICE_INPUT_H +#define _DEVICE_INPUT_H + +#include +#include + +/* + * 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 IOC_VOID 0x20000000 /* no parameters */ +#define IOC_OUT 0x40000000 /* copy out parameters */ +#define IOC_IN 0x80000000U /* copy in parameters */ +#define IOC_INOUT (IOC_IN|IOC_OUT) + +#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)) +#define _IOWR(g,n,t) _IOC(IOC_INOUT, (g), (n), sizeof(t)) + +typedef uint8_t Scancode; +typedef uint16_t kev_type; /* kd event type */ + +/* (used for event records) */ +struct mouse_motion { + short mm_deltaX; /* units? */ + short mm_deltaY; +}; + +typedef struct { + kev_type type; /* see below */ + /* + * This is not used anymore but is kept for backwards compatibility. + * Note the use of rpc_time_value to ensure compatibility for a 64 bit kernel and + * 32 bit user land. + */ + struct rpc_time_value unused_time; /* timestamp*/ + union { /* value associated with event */ + boolean_t up; /* MOUSE_LEFT .. MOUSE_RIGHT */ + Scancode sc; /* KEYBD_EVENT */ + struct mouse_motion mmotion; /* MOUSE_MOTION */ + } value; +} kd_event; +#define m_deltaX mmotion.mm_deltaX +#define m_deltaY mmotion.mm_deltaY + +/* + * kd_event ID's. + */ +#define MOUSE_LEFT 1 /* mouse left button up/down */ +#define MOUSE_MIDDLE 2 +#define MOUSE_RIGHT 3 +#define MOUSE_MOTION 4 /* mouse motion */ +#define KEYBD_EVENT 5 /* key up/down */ + +/* Keyboard ioctls */ + +/* + * KDSKBDMODE - When the console is in "ascii" mode, keyboard events are + * converted to Ascii characters that are readable from /dev/console. + * When the console is in "event" mode, keyboard events are + * timestamped and queued up on /dev/kbd as kd_events. When the last + * close is done on /dev/kbd, the console automatically reverts to ascii + * mode. + * When /dev/mouse is opened, mouse events are timestamped and queued + * on /dev/mouse, again as kd_events. + * + * KDGKBDTYPE - Returns the type of keyboard installed. Currently + * there is only one type, KB_VANILLAKB, which is your standard PC-AT + * keyboard. + */ + +#define KDSKBDMODE _IOW('K', 1, int) /* set keyboard mode */ +#define KB_EVENT 1 +#define KB_ASCII 2 + +#define KDGKBDTYPE _IOR('K', 2, int) /* get keyboard type */ +#define KB_VANILLAKB 0 + +#define KDSETLEDS _IOW('K', 5, int) /* set the keyboard ledstate */ + +#endif /* _DEVICE_INPUT_H */ diff --git a/include/device/net_status.h b/include/device/net_status.h new file mode 100644 index 0000000..9ab95b9 --- /dev/null +++ b/include/device/net_status.h @@ -0,0 +1,201 @@ +/* + * 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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 3/89 + * + * Status information for network interfaces. + */ + +#ifndef _DEVICE_NET_STATUS_H_ +#define _DEVICE_NET_STATUS_H_ + +#include +#include + +/* + * General interface status + */ +struct net_status { + int min_packet_size; /* minimum size, including header */ + int max_packet_size; /* maximum size, including header */ + int header_format; /* format of network header */ + int header_size; /* size of network header */ + int address_size; /* size of network address */ + int flags; /* interface status */ + int mapped_size; /* if mappable, virtual mem needed */ +}; +#define NET_STATUS_COUNT (sizeof(struct net_status)/sizeof(int)) +#define NET_STATUS (('n'<<16) + 1) + +/* + * Header formats, as given by RFC 826/1010 for ARP: + */ +#define HDR_ETHERNET 1 /* Ethernet hardware address */ +#define HDR_EXP_ETHERNET 2 /* 3Mhz experimental Ethernet + hardware address */ +#define HDR_PRO_NET 4 /* Proteon ProNET Token Ring */ +#define HDR_CHAOS 5 /* Chaosnet */ +#define HDR_802 6 /* IEEE 802 networks */ + + +/* + * A network address is an array of bytes. In order to return + * this in an array of (long) integers, it is returned in net order. + * Use 'ntohl' on each element of the array to retrieve the original + * ordering. + */ +#define NET_ADDRESS (('n'<<16) + 2) + +#define NET_DSTADDR (('n'<<16) + 3) + +#define NET_FLAGS (('n'<<16) + 4) + +/* + * Input packet filter definition + */ +#define NET_MAX_FILTER 128 /* was 64, bpf programs are big */ +#define NET_FILTER_STACK_DEPTH 32 + +/* + * We allow specification of up to NET_MAX_FILTER (short) words of a filter + * command list to be applied to incoming packets to determine if + * those packets should be given to a particular network input filter. + * + * Each network filter specifies the filter command list via net_add_filter. + * Each filter command list specifies a sequences of actions which leave a + * boolean value on the top of an internal stack. Each word of the + * command list specifies an action from the set {PUSHLIT, PUSHZERO, + * PUSHWORD+N} which respectively push the next word of the filter, zero, + * or word N of the incoming packet on the stack, and a binary operator + * from the set {EQ, LT, LE, GT, GE, AND, OR, XOR} which operates on the + * top two elements of the stack and replaces them with its result. The + * special action NOPUSH and the special operator NOP can be used to only + * perform the binary operation or to only push a value on the stack. + * + * If the final value of the filter operation is true, then the packet is + * accepted for the filter. + * + * The first filter_t object is a header which allows to set flags for the + * filter code. Main flags concern the direction of packets. This header is + * split in the same way NETF words are : the 6 MSB bits indicate the type + * of filter while the 10 LSB bits are the flags. For native NETF filters, + * clear the 6 MSB bits (which is why there is no dedicated macro). + */ + +typedef unsigned short filter_t; +typedef filter_t *filter_array_t; + +#define CSPF_BYTES(n) ((n) * sizeof (filter_t)) + +/* these must sum to 16! */ +#define NETF_NBPA 10 /* # bits / argument */ +#define NETF_NBPO 6 /* # bits / operator */ + +#define NETF_ARG(word) ((word) & 0x3ff) +#define NETF_OP(word) (((word)>>NETF_NBPA)&0x3f) + +/* filter types */ +#define NETF_TYPE_MASK (((1 << NETF_NBPO) - 1) << NETF_NBPA) +#define NETF_BPF (1 << NETF_NBPA) + +/* flags */ +#define NETF_IN 0x1 +#define NETF_OUT 0x2 + +/* binary operators */ +#define NETF_NOP (0< + +/* + * Net receive message format. + * + * The header and data are packaged separately, since some hardware + * supports variable-length headers. We prefix the packet with + * a packet_hdr structure so that the real data portion begins + * on a long-word boundary, and so that packet filters can address + * the type field and packet size uniformly. + */ +#define NET_RCV_MAX 4095 +#define NET_HDW_HDR_MAX 64 + +#define NET_RCV_MSG_ID 2999 /* in device.defs reply range */ + +struct packet_header { + unsigned short length; + unsigned short type; /* network order */ +}; + +struct net_rcv_msg { + mach_msg_header_t msg_hdr; + mach_msg_type_t header_type; + char header[NET_HDW_HDR_MAX]; + mach_msg_type_t packet_type; + char packet[NET_RCV_MAX]; + boolean_t sent; +}; +typedef struct net_rcv_msg *net_rcv_msg_t; +#define net_rcv_msg_packet_count packet_type.msgt_number + + + +#endif /* _DEVICE_NET_STATUS_H_ */ diff --git a/include/device/notify.defs b/include/device/notify.defs new file mode 100644 index 0000000..7919b33 --- /dev/null +++ b/include/device/notify.defs @@ -0,0 +1,36 @@ +/* + * 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. + */ + +subsystem notify 100; + +#include + +serverprefix do_; +serverdemux device_intr_notify_server; + +simpleroutine device_intr_notify( + notify : notify_port_t; + id : int); diff --git a/include/device/notify.h b/include/device/notify.h new file mode 100644 index 0000000..addf911 --- /dev/null +++ b/include/device/notify.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2010 Free Software Foundation, Inc. + * + * 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. + * + * THE FREE SOFTWARE FOUNDATIONALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. THE FREE SOFTWARE FOUNDATION DISCLAIMS ANY LIABILITY OF ANY KIND + * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + */ + +/* + * Device notification definitions. + */ + +#ifndef _MACH_DEVICE_NOTIFY_H_ +#define _MACH_DEVICE_NOTIFY_H_ + +#include +#include + +typedef struct +{ + mach_msg_header_t intr_header; + mach_msg_type_t intr_type; + int id; +} device_intr_notification_t; + +#define DEVICE_INTR_NOTIFY 100 + +#endif /* _MACH_DEVICE_NOTIFY_H_ */ diff --git a/include/device/tape_status.h b/include/device/tape_status.h new file mode 100644 index 0000000..603d76c --- /dev/null +++ b/include/device/tape_status.h @@ -0,0 +1,140 @@ +/* + * 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. + */ +/* + * Copyright (c) 1982, 1986 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. The name of the Laboratory may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 7.4 (Berkeley) 8/31/88 + */ + +#ifndef _TAPE_STATUS_H_ +#define _TAPE_STATUS_H_ + +/* + * Tape status + */ + +struct tape_status { + unsigned int mt_type; + unsigned int speed; + unsigned int density; + unsigned int flags; +# define TAPE_FLG_REWIND 0x1 +# define TAPE_FLG_WP 0x2 +}; +#define TAPE_STATUS_COUNT (sizeof(struct tape_status)/sizeof(int)) +#define TAPE_STATUS (('m'<<16) + 1) + +/* + * Constants for mt_type. 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_ISSCSI 0x0a /* SCSI tape (all brands) */ + + +/* + * Set status parameters + */ + +struct tape_params { + unsigned int mt_operation; + unsigned int mt_repeat_count; +}; + +/* 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 */ + + +/* + * U*x compatibility + */ + +/* 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 */ +/* the following two are not yet implemented */ + unsigned long mt_fileno; /* file number of current position */ + unsigned long mt_blkno; /* block number of current position */ +/* end not yet implemented */ +}; + + +/* mag tape io control commands */ +#define MTIOCTOP _IOW('m', 1, struct tape_params)/* 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 */ + + +#endif /* _TAPE_STATUS_H_ */ diff --git a/include/device/tty_status.h b/include/device/tty_status.h new file mode 100644 index 0000000..2eed5d0 --- /dev/null +++ b/include/device/tty_status.h @@ -0,0 +1,134 @@ +/* + * 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. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: ll/90 + * + * Status information for tty. + */ + +#ifndef _DEVICE_TTY_STATUS_H_ +#define _DEVICE_TTY_STATUS_H_ + +struct tty_status { + int tt_ispeed; /* input speed */ + int tt_ospeed; /* output speed */ + int tt_breakc; /* character to deliver when break + detected on line */ + int tt_flags; /* mode flags */ +}; +#define TTY_STATUS_COUNT (sizeof(struct tty_status)/sizeof(int)) +#define TTY_STATUS (dev_flavor_t)(('t'<<16) + 1) + +/* + * Speeds + */ +#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 /* XX can we just get rid of EXTA and EXTB? */ +#define EXTB 15 +#define B19200 EXTA +#define B38400 EXTB +#define B57600 16 +#define B115200 17 + +#define NSPEEDS 18 + +/* + * Flags + */ +#define TF_TANDEM 0x00000001 /* send stop character when input + queue full */ +#define TF_ODDP 0x00000002 /* get/send odd parity */ +#define TF_EVENP 0x00000004 /* get/send even parity */ +#define TF_ANYP (TF_ODDP|TF_EVENP) + /* get any parity/send none */ +#define TF_LITOUT 0x00000008 /* output all 8 bits + otherwise, characters >= 0x80 + are time delays XXX */ +#define TF_MDMBUF 0x00000010 /* start/stop output on carrier + interrupt + otherwise, dropping carrier + hangs up line */ +#define TF_NOHANG 0x00000020 /* no hangup signal on carrier drop */ +#define TF_HUPCLS 0x00000040 /* hang up (outgoing) on last close */ + +/* + * Read-only flags - information about device + */ +#define TF_ECHO 0x00000080 /* device wants user to echo input */ +#define TF_CRMOD 0x00000100 /* device wants \r\n, not \n */ +#define TF_XTABS 0x00000200 /* device does not understand tabs */ + +/* + * Modem control + */ +#define TTY_MODEM_COUNT (1) /* one integer */ +#define TTY_MODEM (dev_flavor_t)(('t'<<16) + 2) + +#define TM_LE 0x0001 /* line enable */ +#define TM_DTR 0x0002 /* data terminal ready */ +#define TM_RTS 0x0004 /* request to send */ +#define TM_ST 0x0008 /* secondary transmit */ +#define TM_SR 0x0010 /* secondary receive */ +#define TM_CTS 0x0020 /* clear to send */ +#define TM_CAR 0x0040 /* carrier detect */ +#define TM_RNG 0x0080 /* ring */ +#define TM_DSR 0x0100 /* data set ready */ + +#define TM_BRK 0x0200 /* set line break (internal) */ +#define TM_HUP 0x0000 /* close line (internal) */ + +/* + * Other controls + */ +#define TTY_FLUSH_COUNT (1) /* one integer - D_READ|D_WRITE */ +#define TTY_FLUSH (dev_flavor_t)(('t'<<16) + 3) + /* flush input or output */ +#define TTY_STOP (dev_flavor_t)(('t'<<16) + 4) + /* stop output */ +#define TTY_START (dev_flavor_t)(('t'<<16) + 5) + /* start output */ +#define TTY_SET_BREAK (dev_flavor_t)(('t'<<16) + 6) + /* set break condition */ +#define TTY_CLEAR_BREAK (dev_flavor_t)(('t'<<16) + 7) + /* clear break condition */ +#define TTY_SET_TRANSLATION (dev_flavor_t)(('t'<<16) + 8) + /* set translation table */ + +#endif /* _DEVICE_TTY_STATUS_H_ */ diff --git a/include/inttypes.h b/include/inttypes.h new file mode 100644 index 0000000..353984a --- /dev/null +++ b/include/inttypes.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2020 Free Software Foundation, Inc. + * + * This file is part of GNU Mach. + * + * GNU Mach is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _INTTYPES_H_ +#define _INTTYPES_H_ + +#include + +#ifdef __x86_64__ +#define __64PREFIX "l" +#define __PTRPREFIX "l" +#else +#define __64PREFIX "ll" +#define __PTRPREFIX +#endif + +#define PRId8 "d" +#define PRId16 "d" +#define PRId32 "d" +#define PRId64 __64PREFIX"d" +#define PRIdPTR __PTRPREFIX"d" + +#define PRIi8 "i" +#define PRIi16 "i" +#define PRIi32 "i" +#define PRIi64 __64PREFIX"i" +#define PRIiPTR __PTRPREFIX"i" + +#define PRIu8 "u" +#define PRIu16 "u" +#define PRIu32 "u" +#define PRIu64 __64PREFIX"u" +#define PRIuPTR __PTRPREFIX"u" + +#define PRIx8 "x" +#define PRIx16 "x" +#define PRIx32 "x" +#define PRIx64 __64PREFIX"x" +#define PRIxPTR __PTRPREFIX"x" + +#define PRIo8 "o" +#define PRIo16 "o" +#define PRIo32 "o" +#define PRIo64 __64PREFIX"o" +#define PRIoPTR __PTRPREFIX"o" + +#endif /* _INTTYPES_H_ */ diff --git a/include/mach/alert.h b/include/mach/alert.h new file mode 100644 index 0000000..e8eb371 --- /dev/null +++ b/include/mach/alert.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 1993,1994 The University of Utah and + * the Computer Systems Laboratory (CSL). 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. + * + * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS + * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF + * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * CSL requests users of this software to return to csl-dist@cs.utah.edu any + * improvements that they make and grant CSL redistribution rights. + * + * Author: Bryan Ford, University of Utah CSL + */ +/* + * File: mach/alert.h + * + * Standard alert definitions + * + */ + +#ifndef _MACH_ALERT_H_ +#define _MACH_ALERT_H_ + +#define ALERT_BITS 32 /* Minimum; more may actually be available */ + +#define ALERT_ABORT_STRONG 0x00000001 /* Request to abort _all_ operations */ +#define ALERT_ABORT_SAFE 0x00000002 /* Request to abort restartable operations */ + +#define ALERT_USER 0xffff0000 /* User-defined alert bits */ + +#endif /* _MACH_ALERT_H_ */ diff --git a/include/mach/boolean.h b/include/mach/boolean.h new file mode 100644 index 0000000..f0f36a2 --- /dev/null +++ b/include/mach/boolean.h @@ -0,0 +1,63 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988,1987 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. + */ +/* + * File: mach/boolean.h + * + * Boolean data type. + * + */ + +#ifndef _MACH_BOOLEAN_H_ +#define _MACH_BOOLEAN_H_ + +/* + * Pick up "boolean_t" type definition + */ + +#ifndef __ASSEMBLER__ +#include +#endif /* __ASSEMBLER__ */ + +#endif /* _MACH_BOOLEAN_H_ */ + +/* + * Define TRUE and FALSE, only if they haven't been before, + * and not if they're explicitly refused. Note that we're + * outside the BOOLEAN_H_ conditional, to avoid ordering + * problems. + */ + +#if !defined(NOBOOL) + +#ifndef TRUE +#define TRUE ((boolean_t) 1) +#endif /* TRUE */ + +#ifndef FALSE +#define FALSE ((boolean_t) 0) +#endif /* FALSE */ + +#endif /* !defined(NOBOOL) */ diff --git a/include/mach/boot.h b/include/mach/boot.h new file mode 100644 index 0000000..7f14cc4 --- /dev/null +++ b/include/mach/boot.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 1994 The University of Utah and + * the Computer Systems Laboratory at the University of Utah (CSL). + * All rights reserved. + * + * Permission to use, copy, modify and distribute this software is hereby + * granted provided that (1) source code retains these copyright, permission, + * and disclaimer notices, and (2) redistributions including binaries + * reproduce the notices in supporting documentation, and (3) all advertising + * materials mentioning features or use of this software display the following + * acknowledgement: ``This product includes software developed by the + * Computer Systems Laboratory at the University of Utah.'' + * + * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS + * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF + * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * CSL requests users of this software to return to csl-dist@cs.utah.edu any + * improvements that they make and grant CSL redistribution rights. + * + * Author: Bryan Ford, University of Utah CSL + */ +#ifndef _MACH_BOOT_ +#define _MACH_BOOT_ + +#include + +#ifndef __ASSEMBLER__ + +#include + +struct boot_image_info +{ + /* First of the chain of boot modules in the boot image. */ + struct boot_module *first_bmod; + + /* List of rendezvous points: + starts out 0; and bmods can add nodes as needed. */ + struct boot_rendezvous *first_rzv; + + /* These register the total virtual address extent of the boot image. */ + vm_offset_t start, end; + + /* Machine-dependent boot information. */ + struct machine_boot_image_info mboot; +}; + +struct boot_module +{ + int magic; + int (*init)(struct boot_image_info *bii); + vm_offset_t text; + vm_offset_t etext; + vm_offset_t data; + vm_offset_t edata; + vm_offset_t bss; + vm_offset_t ebss; +}; +#define BMOD_VALID(bmod) ((bmod)->magic == BMOD_MAGIC) +#define BMOD_NEXT(bmod) ((struct boot_module*)((bmod)->edata)) + +struct boot_rendezvous +{ + struct boot_rendezvous *next; + int code; +}; + +#endif /* !__ASSEMBLER__ */ + + +/* This is the magic value that must appear in boot_module.magic. */ +#define BMOD_MAGIC 0x424d4f44 /* 'BMOD' */ + + +/* Following are the codes for boot_rendezvous.code. */ + +/* This rendezvous is used for choosing a microkernel to start. + XX not used yet */ +#define BRZV_KERNEL 'K' + +/* Once the microkernel is fully initialized, + it starts one or more bootstrap services... */ +#define BRZV_BOOTSTRAP 'B' + +/* The bootstrap services might need other OS-dependent data, + such as initial programs to run, filesystem snapshots, etc. + These generic chunks of data are packaged up by the microkernel + and provided to the bootstrap services upon request. + XX When can they be deallocated? */ +#define BRZV_DATA 'D' + + +#endif /* _MACH_BOOT_ */ diff --git a/include/mach/default_pager.defs b/include/mach/default_pager.defs new file mode 100644 index 0000000..e2154e2 --- /dev/null +++ b/include/mach/default_pager.defs @@ -0,0 +1,65 @@ +/* + * 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. + */ + +subsystem default_pager 2275; + +#include +#include +#include + +routine default_pager_object_create( + default_pager : mach_port_t; + out memory_object : memory_object_t = + MACH_MSG_TYPE_MAKE_SEND; + object_size : vm_size_t); + +routine default_pager_info( + default_pager : mach_port_t; + out info : default_pager_info_t); + +routine default_pager_objects( + default_pager : mach_port_t; + out objects : default_pager_object_array_t, + CountInOut, Dealloc; + out ports : mach_port_array_t = + array[] of mach_port_move_send_t, + CountInOut, Dealloc); + +routine default_pager_object_pages( + default_pager : mach_port_t; + memory_object : memory_object_name_t; + out pages : default_pager_page_array_t, + CountInOut, Dealloc); + +routine default_pager_paging_file( + default_pager : mach_port_t; + master_device_port : mach_port_t; + filename : default_pager_filename_t; + add : boolean_t); + +routine default_pager_register_fileserver( + default_pager : mach_port_t; + fileserver_port : mach_port_t); diff --git a/include/mach/default_pager_types.defs b/include/mach/default_pager_types.defs new file mode 100644 index 0000000..398c62c --- /dev/null +++ b/include/mach/default_pager_types.defs @@ -0,0 +1,53 @@ +/* + * Mach Operating System + * Copyright (c) 1992 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. + */ + +#ifndef _MACH_DEFAULT_PAGER_TYPES_DEFS_ +#define _MACH_DEFAULT_PAGER_TYPES_DEFS_ + +#include + +type default_pager_info_t = struct { + vm_size_t dpi_total_space; + vm_size_t dpi_free_space; + vm_size_t dpi_page_size; +}; + +type default_pager_object_t = struct { + vm_offset_t dpo_object; + vm_size_t dpo_size; +}; +type default_pager_object_array_t = array[] of default_pager_object_t; + +type default_pager_page_t = struct { + vm_offset_t dpp_offset; +}; +type default_pager_page_array_t = array[] of default_pager_page_t; + +type default_pager_filename_t = (MACH_MSG_TYPE_STRING_C, 8*256); + +import ; + +#endif /* _MACH_DEFAULT_PAGER_TYPES_DEFS_ */ diff --git a/include/mach/default_pager_types.h b/include/mach/default_pager_types.h new file mode 100644 index 0000000..2cf7da2 --- /dev/null +++ b/include/mach/default_pager_types.h @@ -0,0 +1,59 @@ +/* + * Mach Operating System + * Copyright (c) 1992 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. + */ + +#ifndef _MACH_DEFAULT_PAGER_TYPES_H_ +#define _MACH_DEFAULT_PAGER_TYPES_H_ + +/* + * Remember to update the mig type definitions + * in default_pager_types.defs when adding/removing fields. + */ + +typedef struct default_pager_info { + vm_size_t dpi_total_space; /* size of backing store */ + vm_size_t dpi_free_space; /* how much of it is unused */ + vm_size_t dpi_page_size; /* the pager's vm page size */ +} default_pager_info_t; + + +typedef struct default_pager_object { + vm_offset_t dpo_object; /* object managed by the pager */ + vm_size_t dpo_size; /* backing store used for the object */ +} default_pager_object_t; + +typedef default_pager_object_t *default_pager_object_array_t; + + +typedef struct default_pager_page { + vm_offset_t dpp_offset; /* offset of the page in its object */ +} default_pager_page_t; + +typedef default_pager_page_t *default_pager_page_array_t; + +typedef char default_pager_filename_t[256]; +typedef const char *const_default_pager_filename_t; + +#endif /* _MACH_DEFAULT_PAGER_TYPES_H_ */ diff --git a/include/mach/error.h b/include/mach/error.h new file mode 100644 index 0000000..035dcf8 --- /dev/null +++ b/include/mach/error.h @@ -0,0 +1,93 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988,1987 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. + */ +/* + * File: mach/error.h + * Purpose: + * error module definitions + * + */ + +#ifndef _MACH_ERROR_H_ +#define _MACH_ERROR_H_ +#include + +/* + * error number layout as follows: + * + * hi lo + * | system(6) | subsystem(12) | code(14) | + */ + + +#define err_none (mach_error_t)0 +#define ERR_SUCCESS (mach_error_t)0 + + +#define err_system(x) (((x)&0x3f)<<26) +#define err_sub(x) (((x)&0xfff)<<14) + +#define err_get_system(err) (((err)>>26)&0x3f) +#define err_get_sub(err) (((err)>>14)&0xfff) +#define err_get_code(err) ((err)&0x3fff) + +#define system_emask (err_system(0x3f)) +#define sub_emask (err_sub(0xfff)) +#define code_emask (0x3fff) + + +/* Mach error systems */ +#define err_kern err_system(0x0) /* kernel */ +#define err_us err_system(0x1) /* user space library */ +#define err_server err_system(0x2) /* user space servers */ +#define err_ipc err_system(0x3) /* old ipc errors */ +#define err_mach_ipc err_system(0x4) /* mach-ipc errors */ +#define err_bootstrap err_system(0x5) /* bootstrap errors */ +#define err_hurd err_system(0x10) /* GNU Hurd server errors */ +#define err_local err_system(0x3e) /* user defined errors */ +#define err_ipc_compat err_system(0x3f) /* (compatibility) mach-ipc errors */ + +#define err_max_system 0x3f + + +/* special old "subsystems" that don't really follow the above rules */ +#define err_mig -300 +#define err_exec 6000 + +/* unix errors get lumped into one subsystem */ +#define err_unix (err_kern|err_sub(3)) +#define unix_err(errno) (err_kern|err_sub(3)|errno) + +/* MS-DOS extended error codes */ +#define err_dos (err_kern|err_sub(0xd05)) + +/* Flux OS error systems */ +#define err_fluke err_system(0x20) /* Fluke API */ + +#ifndef __ASSEMBLER__ +typedef kern_return_t mach_error_t; +#endif /* __ASSEMBLER__ */ + +#endif /* _MACH_ERROR_H_ */ diff --git a/include/mach/exc.defs b/include/mach/exc.defs new file mode 100644 index 0000000..28638e2 --- /dev/null +++ b/include/mach/exc.defs @@ -0,0 +1,47 @@ +/* + * Mach Operating System + * Copyright (c) 1993,1991,1990,1989,1988,1987 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. + */ +/* + * Abstract: + * MiG definitions file for Mach exception interface. + */ + +subsystem +#if KERNEL_USER + KernelUser +#endif /* KERNEL_USER */ + exc 2400; + +#include + +ServerPrefix catch_; + +routine exception_raise( + exception_port : mach_port_t; + thread : mach_port_t; + task : mach_port_t; + exception : integer_t; + code : integer_t; + subcode : rpc_long_integer_t); diff --git a/include/mach/exception.h b/include/mach/exception.h new file mode 100644 index 0000000..c44fd53 --- /dev/null +++ b/include/mach/exception.h @@ -0,0 +1,58 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988,1987 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. + */ + +#ifndef _MACH_EXCEPTION_H_ +#define _MACH_EXCEPTION_H_ + +#include + +/* + * Machine-independent exception definitions. + */ + +#define EXC_BAD_ACCESS 1 /* Could not access memory */ + /* Code contains kern_return_t describing error. */ + /* Subcode contains bad memory address. */ + +#define EXC_BAD_INSTRUCTION 2 /* Instruction failed */ + /* Illegal or undefined instruction or operand */ + +#define EXC_ARITHMETIC 3 /* Arithmetic exception */ + /* Exact nature of exception is in code field */ + +#define EXC_EMULATION 4 /* Emulation instruction */ + /* Emulation support instruction encountered */ + /* Details in code and subcode fields */ + +#define EXC_SOFTWARE 5 /* Software generated exception */ + /* Exact exception is in code field. */ + /* Codes 0 - 0xFFFF reserved to hardware */ + /* Codes 0x10000 - 0x1FFFF reserved for OS emulation (Unix) */ + +#define EXC_BREAKPOINT 6 /* Trace, breakpoint, etc. */ + /* Details in code field. */ + +#endif /* _MACH_EXCEPTION_H_ */ diff --git a/include/mach/exec/a.out.h b/include/mach/exec/a.out.h new file mode 100644 index 0000000..c6dcaff --- /dev/null +++ b/include/mach/exec/a.out.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 1994 The University of Utah and + * the Computer Systems Laboratory at the University of Utah (CSL). + * All rights reserved. + * + * Permission to use, copy, modify and distribute this software is hereby + * granted provided that (1) source code retains these copyright, permission, + * and disclaimer notices, and (2) redistributions including binaries + * reproduce the notices in supporting documentation, and (3) all advertising + * materials mentioning features or use of this software display the following + * acknowledgement: ``This product includes software developed by the + * Computer Systems Laboratory at the University of Utah.'' + * + * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS + * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF + * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * CSL requests users of this software to return to csl-dist@cs.utah.edu any + * improvements that they make and grant CSL redistribution rights. + * + * Author: Bryan Ford, University of Utah CSL + */ +#ifndef _MACH_A_OUT_ +#define _MACH_A_OUT_ + +struct exec +{ + unsigned long a_magic; /* magic number */ + unsigned long a_text; /* size of text segment */ + unsigned long a_data; /* size of initialized data */ + unsigned long a_bss; /* size of uninitialized data */ + unsigned long a_syms; /* size of symbol table */ + unsigned long a_entry; /* entry point */ + unsigned long a_trsize; /* size of text relocation */ + unsigned long a_drsize; /* size of data relocation */ +}; + +struct nlist { + long n_strx; + unsigned char n_type; + char n_other; + short n_desc; + unsigned long n_value; +}; + +#define OMAGIC 0407 +#define NMAGIC 0410 +#define ZMAGIC 0413 +#define QMAGIC 0314 + +#define N_GETMAGIC(ex) \ + ( (ex).a_magic & 0xffff ) +#define N_GETMAGIC_NET(ex) \ + (ntohl((ex).a_magic) & 0xffff) + +/* 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) + +/* We don't provide any N_???OFF macros here + because they vary too much between the different a.out variants; + it's practically impossible to create one set of macros + that works for UX, FreeBSD, NetBSD, Linux, etc. */ + +#endif /* _MACH_A_OUT_ */ diff --git a/include/mach/exec/elf.h b/include/mach/exec/elf.h new file mode 100644 index 0000000..409947c --- /dev/null +++ b/include/mach/exec/elf.h @@ -0,0 +1,364 @@ +/* + * Copyright (c) 1995, 1994, 1993, 1992, 1991, 1990 + * Open Software Foundation, Inc. + * + * 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 + * FOR A PARTICULAR PURPOSE. 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 + * + */ +/* + * OSF Research Institute MK6.1 (unencumbered) 1/31/1995 + */ +#ifndef _MACH_EXEC_ELF_H_ +#define _MACH_EXEC_ELF_H_ + +#include + +/* ELF Header - figure 4-3, page 4-4 */ + +#define EI_NIDENT 16 + +typedef struct { + unsigned char e_ident[EI_NIDENT]; + Elf32_Half e_type; + Elf32_Half e_machine; + Elf32_Word e_version; + Elf32_Addr e_entry; + Elf32_Off e_phoff; + Elf32_Off e_shoff; + Elf32_Word e_flags; + Elf32_Half e_ehsize; + Elf32_Half e_phentsize; + Elf32_Half e_phnum; + Elf32_Half e_shentsize; + Elf32_Half e_shnum; + Elf32_Half e_shstrndx; +} Elf32_Ehdr; + +typedef struct { + unsigned char e_ident[EI_NIDENT]; /* Id bytes */ + Elf64_Half e_type; /* file type */ + Elf64_Half e_machine; /* machine type */ + Elf64_Word e_version; /* version number */ + Elf64_Addr e_entry; /* entry point */ + Elf64_Off e_phoff; /* Program hdr offset */ + Elf64_Off e_shoff; /* Section hdr offset */ + Elf64_Word e_flags; /* Processor flags */ + Elf64_Half e_ehsize; /* sizeof ehdr */ + Elf64_Half e_phentsize; /* Program header entry size */ + Elf64_Half e_phnum; /* Number of program headers */ + Elf64_Half e_shentsize; /* Section header entry size */ + Elf64_Half e_shnum; /* Number of section headers */ + Elf64_Half e_shstrndx; /* String table index */ +} Elf64_Ehdr; + +/* e_ident[] identification indexes - figure 4-4, page 4-7 */ + +#define EI_MAG0 0 +#define EI_MAG1 1 +#define EI_MAG2 2 +#define EI_MAG3 3 +#define EI_CLASS 4 +#define EI_DATA 5 +#define EI_VERSION 6 +#define EI_PAD 7 + +/* magic number - pg 4-8 */ + +#define ELFMAG0 0x7f +#define ELFMAG1 'E' +#define ELFMAG2 'L' +#define ELFMAG3 'F' + +/* file class or capacity - page 4-8 */ + +#define ELFCLASSNONE 0 +#define ELFCLASS32 1 +#define ELFCLASS64 2 + +/* date encoding - page 4-9 */ + +#define ELFDATANONE 0 +#define ELFDATA2LSB 1 +#define ELFDATA2MSB 2 + +/* object file types - page 4-5 */ + +#define ET_NONE 0 +#define ET_REL 1 +#define ET_EXEC 2 +#define ET_DYN 3 +#define ET_CORE 4 + +#define ET_LOPROC 0xff00 +#define ET_HIPROC 0xffff + +/* architecture - page 4-5 */ + +#define EM_NONE 0 +#define EM_M32 1 +#define EM_SPARC 2 +#define EM_386 3 +#define EM_68K 4 +#define EM_88K 5 +#define EM_860 7 +#define EM_MIPS 8 +#define EM_MIPS_RS4_BE 10 +#define EM_SPARC64 11 +#define EM_PARISC 15 +#define EM_PPC 20 +#define EM_X86_64 62 + +/* version - page 4-6 */ + +#define EV_NONE 0 +#define EV_CURRENT 1 + +/* special section indexes - page 4-11, figure 4-7 */ + +#define SHN_UNDEF 0 +#define SHN_LORESERVE 0xff00 +#define SHN_LOPROC 0xff00 +#define SHN_HIPROC 0xff1f +#define SHN_ABS 0xfff1 +#define SHN_COMMON 0xfff2 +#define SHN_HIRESERVE 0xffff + +/* section header - page 4-13, figure 4-8 */ + +typedef struct { + Elf32_Word sh_name; + Elf32_Word sh_type; + Elf32_Word sh_flags; + Elf32_Addr sh_addr; + Elf32_Off sh_offset; + Elf32_Word sh_size; + Elf32_Word sh_link; + Elf32_Word sh_info; + Elf32_Word sh_addralign; + Elf32_Word sh_entsize; +} Elf32_Shdr; + +typedef struct elf64_shdr { + Elf64_Word sh_name; + Elf64_Word sh_type; + Elf64_Xword sh_flags; + Elf64_Addr sh_addr; + Elf64_Off sh_offset; + Elf64_Xword sh_size; + Elf64_Word sh_link; + Elf64_Word sh_info; + Elf64_Xword sh_addralign; + Elf64_Xword sh_entsize; +} Elf64_Shdr; + +/* section types - page 4-15, figure 4-9 */ + +#define SHT_NULL 0 +#define SHT_PROGBITS 1 +#define SHT_SYMTAB 2 +#define SHT_STRTAB 3 +#define SHT_RELA 4 +#define SHT_HASH 5 +#define SHT_DYNAMIC 6 +#define SHT_NOTE 7 +#define SHT_NOBITS 8 +#define SHT_REL 9 +#define SHT_SHLIB 10 +#define SHT_DYNSYM 11 + +#define SHT_LOPROC 0x70000000 +#define SHT_HIPROC 0x7fffffff +#define SHT_LOUSER 0x80000000 +#define SHT_HIUSER 0xffffffff + +/* section attribute flags - page 4-18, figure 4-11 */ + +#define SHF_WRITE 0x1 +#define SHF_ALLOC 0x2 +#define SHF_EXECINSTR 0x4 +#define SHF_MASKPROC 0xf0000000 + +/* symbol table - page 4-25, figure 4-15 */ +typedef struct +{ + Elf32_Word st_name; + Elf32_Addr st_value; + Elf32_Word st_size; + unsigned char st_info; + unsigned char st_other; + Elf32_Half st_shndx; +} Elf32_Sym; + +typedef struct elf64_sym { + Elf64_Word st_name; + unsigned char st_info; + unsigned char st_other; + Elf64_Half st_shndx; + Elf64_Addr st_value; + Elf64_Xword st_size; +} Elf64_Sym; + +#ifdef __x86_64__ +#define Elf_Sym Elf64_Sym +#define Elf_Shdr Elf64_Shdr +#else +#define Elf_Sym Elf32_Sym +#define Elf_Shdr Elf32_Shdr +#endif + +/* symbol type and binding attributes - page 4-26 */ + +#define ELF_ST_BIND(i) ((i) >> 4) +#define ELF_ST_TYPE(i) ((i) & 0xf) +#define ELF_ST_INFO(b,t) (((b)<<4)+((t)&0xf)) + +/* symbol binding - page 4-26, figure 4-16 */ + +#define STB_LOCAL 0 +#define STB_GLOBAL 1 +#define STB_WEAK 2 +#define STB_LOPROC 13 +#define STB_HIPROC 15 + +/* symbol types - page 4-28, figure 4-17 */ + +#define STT_NOTYPE 0 +#define STT_OBJECT 1 +#define STT_FUNC 2 +#define STT_SECTION 3 +#define STT_FILE 4 +#define STT_LOPROC 13 +#define STT_HIPROC 15 + + +/* relocation entries - page 4-31, figure 4-19 */ + +typedef struct +{ + Elf32_Addr r_offset; + Elf32_Word r_info; +} Elf32_Rel; + +typedef struct +{ + Elf32_Addr r_offset; + Elf32_Word r_info; + Elf32_Sword r_addend; +} Elf32_Rela; + +/* Macros to split/combine relocation type and symbol page 4-32 */ + +#define ELF32_R_SYM(__i) ((__i)>>8) +#define ELF32_R_TYPE(__i) ((unsigned char) (__i)) +#define ELF32_R_INFO(__s, __t) (((__s)<<8) + (unsigned char) (__t)) + + +/* program header - page 5-2, figure 5-1 */ + +typedef struct { + Elf32_Word p_type; + Elf32_Off p_offset; + Elf32_Addr p_vaddr; + Elf32_Addr p_paddr; + Elf32_Word p_filesz; + Elf32_Word p_memsz; + Elf32_Word p_flags; + Elf32_Word p_align; +} Elf32_Phdr; + +typedef struct { + Elf64_Word p_type; /* entry type */ + Elf64_Word p_flags; /* flags */ + Elf64_Off p_offset; /* offset */ + Elf64_Addr p_vaddr; /* virtual address */ + Elf64_Addr p_paddr; /* physical address */ + Elf64_Xword p_filesz; /* file size */ + Elf64_Xword p_memsz; /* memory size */ + Elf64_Xword p_align; /* memory & file alignment */ +} Elf64_Phdr; + +/* segment types - page 5-3, figure 5-2 */ + +#define PT_NULL 0 +#define PT_LOAD 1 +#define PT_DYNAMIC 2 +#define PT_INTERP 3 +#define PT_NOTE 4 +#define PT_SHLIB 5 +#define PT_PHDR 6 + +#define PT_LOPROC 0x70000000 +#define PT_HIPROC 0x7fffffff + +/* segment permissions - page 5-6 */ + +#define PF_X 0x1 +#define PF_W 0x2 +#define PF_R 0x4 +#define PF_MASKPROC 0xf0000000 + + +/* dynamic structure - page 5-15, figure 5-9 */ + +typedef struct { + Elf32_Sword d_tag; + union { + Elf32_Word d_val; + Elf32_Addr d_ptr; + } d_un; +} Elf32_Dyn; + +/* Dynamic array tags - page 5-16, figure 5-10. */ + +#define DT_NULL 0 +#define DT_NEEDED 1 +#define DT_PLTRELSZ 2 +#define DT_PLTGOT 3 +#define DT_HASH 4 +#define DT_STRTAB 5 +#define DT_SYMTAB 6 +#define DT_RELA 7 +#define DT_RELASZ 8 +#define DT_RELAENT 9 +#define DT_STRSZ 10 +#define DT_SYMENT 11 +#define DT_INIT 12 +#define DT_FINI 13 +#define DT_SONAME 14 +#define DT_RPATH 15 +#define DT_SYMBOLIC 16 +#define DT_REL 17 +#define DT_RELSZ 18 +#define DT_RELENT 19 +#define DT_PLTREL 20 +#define DT_DEBUG 21 +#define DT_TEXTREL 22 +#define DT_JMPREL 23 + +#if defined(__x86_64__) && ! defined(USER32) +typedef Elf64_Ehdr Elf_Ehdr; +typedef Elf64_Phdr Elf_Phdr; +#else +typedef Elf32_Ehdr Elf_Ehdr; +typedef Elf32_Phdr Elf_Phdr; +#endif + +/* + * Bootstrap doesn't need machine dependent extensions. + */ + +#endif /* _MACH_EXEC_ELF_H_ */ diff --git a/include/mach/exec/exec.h b/include/mach/exec/exec.h new file mode 100644 index 0000000..94b234b --- /dev/null +++ b/include/mach/exec/exec.h @@ -0,0 +1,130 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988 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. + */ + +#ifndef _MACH_EXEC_H_ +#define _MACH_EXEC_H_ + +#include +#include + +/* XXX */ +typedef enum +{ + EXEC_ELF = 1, + EXEC_AOUT = 2, +} exec_format_t; + +typedef struct exec_info +{ + /* Format of executable loaded - see above. */ + exec_format_t format; + + /* Program entrypoint. */ + vm_offset_t entry; + + /* Initial data pointer - only some architectures use this. */ + vm_offset_t init_dp; + + /* (ELF) Address of interpreter string for loading shared libraries, null if none. */ + vm_offset_t interp; + +} exec_info_t; + +typedef int exec_sectype_t; +#define EXEC_SECTYPE_READ VM_PROT_READ +#define EXEC_SECTYPE_WRITE VM_PROT_WRITE +#define EXEC_SECTYPE_EXECUTE VM_PROT_EXECUTE +#define EXEC_SECTYPE_PROT_MASK VM_PROT_ALL +#define EXEC_SECTYPE_ALLOC ((exec_sectype_t)0x000100) +#define EXEC_SECTYPE_LOAD ((exec_sectype_t)0x000200) +#define EXEC_SECTYPE_DEBUG ((exec_sectype_t)0x010000) +#define EXEC_SECTYPE_AOUT_SYMTAB ((exec_sectype_t)0x020000) +#define EXEC_SECTYPE_AOUT_STRTAB ((exec_sectype_t)0x040000) + +typedef int exec_read_func_t(void *handle, vm_offset_t file_ofs, + void *buf, vm_size_t size, + vm_size_t *out_actual); + +typedef int exec_read_exec_func_t(void *handle, + vm_offset_t file_ofs, vm_size_t file_size, + vm_offset_t mem_addr, vm_size_t mem_size, + exec_sectype_t section_type); + +/* + * Routines exported from libmach_exec.a + */ + +/* Generic function to interpret an executable "file" + and "load" it into "memory". + Doesn't really know about files, loading, or memory; + all file I/O and destination memory accesses + go through provided functions. + Thus, this is a very generic loading mechanism. + + The read() function is used to read metadata from the file + into the local address space. + + The read_exec() function is used to load the actual sections. + It is used for all kinds of sections - code, data, bss, debugging data. + The 'section_type' parameter specifies what type of section is being loaded. + + For code, data, and bss, the EXEC_SECTYPE_ALLOC flag will be set. + For code and data (i.e. stuff that's actually loaded from the file), + EXEC_SECTYPE_LOAD will also be set. + The EXEC_SECTYPE_PROT_MASK contains the intended access permissions + for the section. + 'file_size' may be less than 'mem_size'; + the remaining data must be zero-filled. + 'mem_size' is always greater than zero, but 'file_size' may be zero + (e.g. in the case of a bss section). + No two read_exec() calls for one executable + will load data into the same virtual memory page, + although they may load from arbitrary (possibly overlapping) file positions. + + For sections that aren't normally loaded into the process image + (e.g. debug sections), EXEC_SECTYPE_ALLOC isn't set, + but some other appropriate flag is set to indicate the type of section. + + The 'handle' is an opaque pointer which is simply passed on + to the read() and read_exec() functions. + + On return, the specified info structure is filled in + with information about the loaded executable. +*/ +int exec_load(exec_read_func_t *read, exec_read_exec_func_t *read_exec, + void *handle, exec_info_t *out_info); + +/* + * Error codes + */ + +#define EX_NOT_EXECUTABLE 6000 /* not a recognized executable format */ +#define EX_WRONG_ARCH 6001 /* valid executable, but wrong arch. */ +#define EX_CORRUPT 6002 /* recognized executable, but mangled */ +#define EX_BAD_LAYOUT 6003 /* something wrong with the memory or file image layout */ + + +#endif /* _MACH_EXEC_H_ */ diff --git a/include/mach/experimental.defs b/include/mach/experimental.defs new file mode 100644 index 0000000..ddcbea5 --- /dev/null +++ b/include/mach/experimental.defs @@ -0,0 +1,15 @@ +subsystem +#if KERNEL_USER + KernelUser +#endif /* KERNEL_USER */ +#if KERNEL_SERVER + KernelServer +#endif /* KERNEL_SERVER */ + experimental 424242; + +#include +#include + +serverprefix experimental_; + +/* This is free for experimenting RPCs, with no backward compatibility guarantees. */ diff --git a/include/mach/gnumach.defs b/include/mach/gnumach.defs new file mode 100644 index 0000000..7ecf74d --- /dev/null +++ b/include/mach/gnumach.defs @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2012 Free Software Foundation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +subsystem +#if KERNEL_SERVER + KernelServer +#endif /* KERNEL_SERVER */ +#if KERNEL_USER + KernelUser +#endif /* KERNEL_USER */ + gnumach 4200; + +#include +#include +#include + +#ifdef GNUMACH_IMPORTS +GNUMACH_IMPORTS +#endif + +type vm_cache_statistics_data_t = struct[11] of integer_t; + +type vm_wire_t = int; + +/* + * Return page cache statistics for the host on which the target task + * resides. + */ +routine vm_cache_statistics( + target_task : vm_task_t; + out vm_cache_stats : vm_cache_statistics_data_t); + +/* + * Terminate a thread and release rights and memory. + * + * Intended to be used by threading libraries to provide a clean way for + * threads to terminate themselves. The resources a thread wouldn't be able + * to release without this call when terminating itself are its + * last reference to its kernel port, its reply port, and its stack. + * + * This call is semantically equivalent to : + * - mach_port_deallocate(task, thread_name); + * - if (reply_port != MACH_PORT_NULL) + * mach_port_destroy(task, reply_port); + * - if ((address != 0) || (size != 0)) + * vm_deallocate(task, address, size) + * - thread_terminate(thread) + * + * Implemented as a simple routine so a reply port isn't required. + */ +simpleroutine thread_terminate_release( + thread : thread_t; + task : task_t; + thread_name : mach_port_name_t; + reply_port : mach_port_name_t; + address : vm_address_t; + size : vm_size_t); + +/* + * Set the name of task TASK to NAME. This is a debugging aid. + * NAME will be used in error messages printed by the kernel. + */ +simpleroutine task_set_name( + task : task_t; + name : kernel_debug_name_t); + +/* + * Register a port to which a notification about newly created tasks + * are sent. + */ +routine register_new_task_notification( + host_priv : host_priv_t; + notification : mach_port_send_t); + +/* Test that the contents of ADDR are equal to the 32-bit integer VAL1. + * If they are not, return immediately, otherwise, block until a + * matching 'gsync_wake' is done on the same address. FLAGS is used + * to control how the thread waits, and may be composed of: + * - GSYNC_SHARED: The address may be shared among tasks. If this + bit is not set, the address is assumed to be task-local. + * - GSYNC_QUAD: Additionally check that the adjacent 32-bit word + following ADDR matches the value VAL2. + * - GSYNC_TIMED: The call only blocks for MSEC milliseconds. */ +routine gsync_wait( + task : task_t; + addr : vm_address_t; + val1 : unsigned; + val2 : unsigned; + msec : natural_t; + flags : int); + +/* Wake up threads waiting on the address ADDR. Much like with + * 'gsync_wait', the parameter FLAGS controls how it is done. In this + * case, it may be composed of the following: + * - GSYNC_SHARED: Same as with 'gsync_wait'. + * - GSYNC_BROADCAST: Wake up every thread waiting on the address. If + * this flag is not set, the call wakes (at most) 1 thread. + * - GSYNC_MUTATE: Before waking any potential waiting threads, set the + * contents of ADDR to VAL. + * + * This RPC is implemented as a simple routine for efficiency reasons, + * and because the return value rarely matters. */ +simpleroutine gsync_wake( + task : task_t; + addr : vm_address_t; + val : unsigned; + flags : int); + +/* Arrange for threads waiting on address SRC_ADDR to instead + * wait on address DST_ADDR. If WAKE_ONE is true, additionally + * wake one of the threads waiting on SRC_ADDR. For this function, + * the parameter flags may be a combination of: + * - GSYNC_SHARED: Just like with 'gsync_wait' and 'gsync_wake'. + * - GSYNC_BROADCAST: Move all the threads waiting on SRC_ADDR. If + this flag is not set, the call moves (at most) 1 thread. + * + * This RPC is also a simple routine, and for the same reasons as + * with 'gsync_wake'. */ +simpleroutine gsync_requeue( + task : task_t; + src_addr : vm_address_t; + dst_addr : vm_address_t; + wake_one : boolean_t; + flags : int); + +/* + * If the VM_WIRE_CURRENT flag is passed, specify that the entire + * virtual address space of the target task must not cause page faults. + * + * If the VM_WIRE_FUTURE flag is passed, automatically wire new + * mappings in the address space of the target task. + * + * If the flags are empty (VM_WIRE_NONE), unwire all mappings. + */ +routine vm_wire_all( + host : mach_port_t; + task : vm_task_t; + flags : vm_wire_t); + +routine vm_object_sync( + object : memory_object_name_t; + offset : vm_offset_t; + size : vm_size_t; + should_flush : boolean_t; + should_return : boolean_t; + should_iosync : boolean_t); + +routine vm_msync( + target_task : vm_task_t; + address : vm_address_t; + size : vm_size_t; + sync_flags : vm_sync_t); + +/* + * This routine is created for allocating DMA buffers. + * We are going to get a contiguous physical memory + * and its physical address in addition to the virtual address. + * We can specify physical memory range limits and alignment. + * NB: + * pmax is defined as the byte after the maximum address, + * eg 0x100000000 for 4GiB limit. + */ +/* XXX + * Future work: the RPC should return a special + * memory object (similar to device_map() ), which can then be mapped into + * the process address space with vm_map() like any other memory object. + */ +routine vm_allocate_contiguous( + host_priv : host_priv_t; + target_task : vm_task_t; + out vaddr : vm_address_t; + out paddr : rpc_phys_addr_t; + size : vm_size_t; + pmin : rpc_phys_addr_t; + pmax : rpc_phys_addr_t; + palign : rpc_phys_addr_t); + +/* + * Set whether TASK is an essential task, i.e. the whole system will crash + * if this task crashes. + */ +simpleroutine task_set_essential( + task : task_t; + essential : boolean_t); + +/* + * Returns physical addresses of a region of memory + */ +routine vm_pages_phys( + host_priv : host_priv_t; + target_task : vm_task_t; + vaddr : vm_address_t; + size : vm_size_t; + out pages : rpc_phys_addr_array_t); + +/* + * Set the name of thread THREAD to NAME. This is a debugging aid. + * NAME will be used in error messages printed by the kernel. + */ +simpleroutine thread_set_name( + thread : thread_t; + name : kernel_debug_name_t); diff --git a/include/mach/host_info.h b/include/mach/host_info.h new file mode 100644 index 0000000..b84376b --- /dev/null +++ b/include/mach/host_info.h @@ -0,0 +1,90 @@ +/* + * Mach Operating System + * Copyright (c) 1993,1992,1991,1990,1989,1988 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. + */ +/* + * File: mach/host_info.h + * + * Definitions for host_info call. + */ + +#ifndef _MACH_HOST_INFO_H_ +#define _MACH_HOST_INFO_H_ + +#include +#include + +/* + * Generic information structure to allow for expansion. + */ +typedef integer_t *host_info_t; /* varying array of integers */ + +#define HOST_INFO_MAX (1024) /* max array size */ +typedef integer_t host_info_data_t[HOST_INFO_MAX]; + +#define KERNEL_VERSION_MAX (512) +typedef char kernel_version_t[KERNEL_VERSION_MAX]; + +/* + * Currently defined information. + */ +#define HOST_BASIC_INFO 1 /* basic info */ +#define HOST_PROCESSOR_SLOTS 2 /* processor slot numbers */ +#define HOST_SCHED_INFO 3 /* scheduling info */ +#define HOST_LOAD_INFO 4 /* avenrun/mach_factor info */ + +struct host_basic_info { + integer_t max_cpus; /* max number of cpus possible */ + integer_t avail_cpus; /* number of cpus now available */ + rpc_vm_size_t memory_size; /* size of memory in bytes */ + cpu_type_t cpu_type; /* cpu type */ + cpu_subtype_t cpu_subtype; /* cpu subtype */ +}; + +typedef struct host_basic_info host_basic_info_data_t; +typedef struct host_basic_info *host_basic_info_t; +#define HOST_BASIC_INFO_COUNT \ + (sizeof(host_basic_info_data_t)/sizeof(integer_t)) + +struct host_sched_info { + integer_t min_timeout; /* minimum timeout in milliseconds */ + integer_t min_quantum; /* minimum quantum in milliseconds */ +}; + +typedef struct host_sched_info host_sched_info_data_t; +typedef struct host_sched_info *host_sched_info_t; +#define HOST_SCHED_INFO_COUNT \ + (sizeof(host_sched_info_data_t)/sizeof(integer_t)) + +struct host_load_info { + integer_t avenrun[3]; /* scaled by LOAD_SCALE */ + integer_t mach_factor[3]; /* scaled by LOAD_SCALE */ +}; + +typedef struct host_load_info host_load_info_data_t; +typedef struct host_load_info *host_load_info_t; +#define HOST_LOAD_INFO_COUNT \ + (sizeof(host_load_info_data_t)/sizeof(integer_t)) + +#endif /* _MACH_HOST_INFO_H_ */ diff --git a/include/mach/inline.h b/include/mach/inline.h new file mode 100644 index 0000000..35f5c5d --- /dev/null +++ b/include/mach/inline.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 1994 The University of Utah and + * the Center for Software Science (CSS). 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. + * + * THE UNIVERSITY OF UTAH AND CSS ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS + * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSS DISCLAIM ANY LIABILITY OF + * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * CSS requests users of this software to return to css-dist@cs.utah.edu any + * improvements that they make and grant CSS redistribution rights. + * + * Author: Bryan Ford, University of Utah CSS + */ +#ifndef _MACH_INLINE_H_ +#define _MACH_INLINE_H_ + +#ifndef MACH_INLINE +#define MACH_INLINE extern __inline +#endif + +#endif /* _MACH_INLINE_H_ */ diff --git a/include/mach/kern_return.h b/include/mach/kern_return.h new file mode 100644 index 0000000..15b836f --- /dev/null +++ b/include/mach/kern_return.h @@ -0,0 +1,166 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University. + * Copyright (c) 1993,1994 The University of Utah and + * the Computer Systems Laboratory (CSL). + * 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, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF + * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM 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. + */ +/* + * File: h/kern_return.h + * Author: Avadis Tevanian, Jr. + * Date: 1985 + * + * Kernel return codes. + * + */ + +#ifndef _MACH_KERN_RETURN_H_ +#define _MACH_KERN_RETURN_H_ + +#include + +#define KERN_SUCCESS 0 + +#define KERN_INVALID_ADDRESS 1 + /* Specified address is not currently valid. + */ + +#define KERN_PROTECTION_FAILURE 2 + /* Specified memory is valid, but does not permit the + * required forms of access. + */ + +#define KERN_NO_SPACE 3 + /* The address range specified is already in use, or + * no address range of the size specified could be + * found. + */ + +#define KERN_INVALID_ARGUMENT 4 + /* The function requested was not applicable to this + * type of argument, or an argument + */ + +#define KERN_FAILURE 5 + /* The function could not be performed. A catch-all. + */ + +#define KERN_RESOURCE_SHORTAGE 6 + /* A system resource could not be allocated to fulfill + * this request. This failure may not be permanent. + */ + +#define KERN_NOT_RECEIVER 7 + /* The task in question does not hold receive rights + * for the port argument. + */ + +#define KERN_NO_ACCESS 8 + /* Bogus access restriction. + */ + +#define KERN_MEMORY_FAILURE 9 + /* During a page fault, the target address refers to a + * memory object that has been destroyed. This + * failure is permanent. + */ + +#define KERN_MEMORY_ERROR 10 + /* During a page fault, the memory object indicated + * that the data could not be returned. This failure + * may be temporary; future attempts to access this + * same data may succeed, as defined by the memory + * object. + */ + +/* KERN_ALREADY_IN_SET 11 obsolete */ + +#define KERN_NOT_IN_SET 12 + /* The receive right is not a member of a port set. + */ + +#define KERN_NAME_EXISTS 13 + /* The name already denotes a right in the task. + */ + +#define KERN_ABORTED 14 + /* The operation was aborted. Ipc code will + * catch this and reflect it as a message error. + */ + +#define KERN_INVALID_NAME 15 + /* The name doesn't denote a right in the task. + */ + +#define KERN_INVALID_TASK 16 + /* Target task isn't an active task. + */ + +#define KERN_INVALID_RIGHT 17 + /* The name denotes a right, but not an appropriate right. + */ + +#define KERN_INVALID_VALUE 18 + /* A blatant range error. + */ + +#define KERN_UREFS_OVERFLOW 19 + /* Operation would overflow limit on user-references. + */ + +#define KERN_INVALID_CAPABILITY 20 + /* The supplied (port) capability is improper. + */ + +#define KERN_RIGHT_EXISTS 21 + /* The task already has send or receive rights + * for the port under another name. + */ + +#define KERN_INVALID_HOST 22 + /* Target host isn't actually a host. + */ + +#define KERN_MEMORY_PRESENT 23 + /* An attempt was made to supply "precious" data + * for memory that is already present in a + * memory object. + */ + +#define KERN_WRITE_PROTECTION_FAILURE 24 + /* + * A page was marked as VM_PROT_NOTIFY and an attempt was + * made to write it + */ +#define KERN_TERMINATED 26 + /* Object has been terminated and is no longer available. + */ + +#define KERN_TIMEDOUT 27 + /* Kernel operation timed out. */ + +#define KERN_INTERRUPTED 28 + /* Kernel operation was interrupted. */ + +#endif /* _MACH_KERN_RETURN_H_ */ diff --git a/include/mach/mach.defs b/include/mach/mach.defs new file mode 100644 index 0000000..c6ad077 --- /dev/null +++ b/include/mach/mach.defs @@ -0,0 +1,724 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988 Carnegie Mellon University. + * Copyright (c) 1993,1994 The University of Utah and + * the Computer Systems Laboratory (CSL). + * 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, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF + * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM 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. + */ +/* + * Matchmaker definitions file for Mach kernel interface. + */ + +subsystem +#if KERNEL_USER + KernelUser +#endif /* KERNEL_USER */ +#if KERNEL_SERVER + KernelServer +#endif /* KERNEL_SERVER */ + mach 2000; + +#ifdef KERNEL_USER +userprefix r_; +#endif /* KERNEL_USER */ + +#include +#include + +#ifdef MACH_IMPORTS +MACH_IMPORTS +#endif + +skip; /* old port_allocate */ +skip; /* old port_deallocate */ +skip; /* old port_enable */ +skip; /* old port_disable */ +skip; /* old port_select */ +skip; /* old port_set_backlog */ +skip; /* old port_status */ + +/* We use only a handful of RPCs as client. Skip the rest. */ +#if ! KERNEL_USER + +/* + * Create a new task with an empty set of IPC rights, + * and having an address space constructed from the + * target task (or empty, if inherit_memory is FALSE). + */ +routine task_create( + target_task : task_t; + inherit_memory : boolean_t; + out child_task : task_t); + +/* + * Destroy the target task, causing all of its threads + * to be destroyed, all of its IPC rights to be deallocated, + * and all of its address space to be deallocated. + */ +routine task_terminate( + target_task : task_t); + +/* + * Get user-level handler entry points for all + * emulated system calls. + */ +routine task_get_emulation_vector( + task : task_t; + out vector_start : int; + out emulation_vector: emulation_vector_t); + +/* + * Establish user-level handlers for the specified + * system calls. Non-emulated system calls are specified + * with emulation_vector[i] == EML_ROUTINE_NULL. + */ +routine task_set_emulation_vector( + task : task_t; + vector_start : int; + emulation_vector: emulation_vector_t); + + +/* + * Returns the set of threads belonging to the target task. + */ +routine task_threads( + target_task : task_t; + out thread_list : thread_array_t); + +/* + * Returns information about the target task. + */ +routine task_info( + target_task : task_t; + flavor : int; + out task_info_out : task_info_t, CountInOut); + + +skip; /* old task_status */ +skip; /* old task_set_notify */ +skip; /* old thread_create */ + +/* + * Destroy the target thread. + */ +routine thread_terminate( + target_thread : thread_t); + +/* + * Return the selected state information for the target + * thread. If the thread is currently executing, the results + * may be stale. [Flavor THREAD_STATE_FLAVOR_LIST provides a + * list of valid flavors for the target thread.] + */ +routine thread_get_state( + target_thread : thread_t; + flavor : int; + out old_state : thread_state_t, CountInOut); + +/* + * Set the selected state information for the target thread. + * If the thread is currently executing, the state change + * may be ill-defined. + */ +routine thread_set_state( + target_thread : thread_t; + flavor : int; + new_state : thread_state_t); + +/* + * Returns information about the target thread. + */ +routine thread_info( + target_thread : thread_t; + flavor : int; + out thread_info_out : thread_info_t, CountInOut); + +skip; /* old thread_mutate */ + +/* + * Allocate zero-filled memory in the address space + * of the target task, either at the specified address, + * or wherever space can be found (if anywhere is TRUE), + * of the specified size. The address at which the + * allocation actually took place is returned. + */ +#ifdef EMULATOR +skip; /* the emulator redefines vm_allocate using vm_map */ +#else /* EMULATOR */ +routine vm_allocate( + target_task : vm_task_t; + inout address : vm_address_t; + size : vm_size_t; + anywhere : boolean_t); +#endif /* EMULATOR */ + +skip; /* old vm_allocate_with_pager */ + +/* + * Deallocate the specified range from the virtual + * address space of the target task. + */ +routine vm_deallocate( + target_task : vm_task_t; + address : vm_address_t; + size : vm_size_t); + +/* + * Set the current or maximum protection attribute + * for the specified range of the virtual address + * space of the target task. The current protection + * limits the memory access rights of threads within + * the task; the maximum protection limits the accesses + * that may be given in the current protection. + * Protections are specified as a set of {read, write, execute} + * *permissions*. + */ +routine vm_protect( + target_task : vm_task_t; + address : vm_address_t; + size : vm_size_t; + set_maximum : boolean_t; + new_protection : vm_prot_t); + +/* + * Set the inheritance attribute for the specified range + * of the virtual address space of the target task. + * The inheritance value is one of {none, copy, share}, and + * specifies how the child address space should acquire + * this memory at the time of a task_create call. + */ +routine vm_inherit( + target_task : vm_task_t; + address : vm_address_t; + size : vm_size_t; + new_inheritance : vm_inherit_t); + +/* + * Returns the contents of the specified range of the + * virtual address space of the target task. [The + * range must be aligned on a virtual page boundary, + * and must be a multiple of pages in extent. The + * protection on the specified range must permit reading.] + */ +routine vm_read( + target_task : vm_task_t; + address : vm_address_t; + size : vm_size_t; + out data : pointer_t); + +/* + * Writes the contents of the specified range of the + * virtual address space of the target task. [The + * range must be aligned on a virtual page boundary, + * and must be a multiple of pages in extent. The + * protection on the specified range must permit writing.] + */ +routine vm_write( + target_task : vm_task_t; + address : vm_address_t; + data : pointer_t); + +/* + * Copy the contents of the source range of the virtual + * address space of the target task to the destination + * range in that same address space. [Both of the + * ranges must be aligned on a virtual page boundary, + * and must be multiples of pages in extent. The + * protection on the source range must permit reading, + * and the protection on the destination range must + * permit writing.] + */ +routine vm_copy( + target_task : vm_task_t; + source_address : vm_address_t; + size : vm_size_t; + dest_address : vm_address_t); + +/* + * Returns information about the contents of the virtual + * address space of the target task at the specified + * address. The returned protection, inheritance, sharing + * and memory object values apply to the entire range described + * by the address range returned; the memory object offset + * corresponds to the beginning of the address range. + * [If the specified address is not allocated, the next + * highest address range is described. If no addresses beyond + * the one specified are allocated, the call returns KERN_NO_SPACE.] + */ +routine vm_region( + target_task : vm_task_t; + inout address : vm_address_t; + out size : vm_size_t; + out protection : vm_prot_t; + out max_protection : vm_prot_t; + out inheritance : vm_inherit_t; + out is_shared : boolean_t; + /* avoid out-translation of the argument */ + out object_name : memory_object_name_t = + MACH_MSG_TYPE_MOVE_SEND + ctype: mach_port_t; + out offset : vm_offset_t); + +/* + * Return virtual memory statistics for the host + * on which the target task resides. [Note that the + * statistics are not specific to the target task.] + */ +routine vm_statistics( + target_task : vm_task_t; + out vm_stats : vm_statistics_data_t); + +skip; /* old task_by_u*x_pid */ +skip; /* old vm_pageable */ + +/* + * Stash a handful of ports for the target task; child + * tasks inherit this stash at task_create time. + */ +routine mach_ports_register( + target_task : task_t; + init_port_set : mach_port_array_t = + ^array[] of mach_port_t); + +/* + * Retrieve the stashed ports for the target task. + */ +routine mach_ports_lookup( + target_task : task_t; + out init_port_set : mach_port_array_t = + ^array[] of mach_port_t); + +skip; /* old u*x_pid */ +skip; /* old netipc_listen */ +skip; /* old netipc_ignore */ + +#else /* ! KERNEL_USER */ + +skip; skip; skip; skip; skip; +skip; skip; skip; skip; skip; +skip; skip; skip; skip; skip; +skip; skip; skip; skip; skip; +skip; skip; skip; skip; skip; +skip; skip; skip; skip; skip; +skip; + +#endif /* ! KERNEL_USER */ + +skip; /* was: memory_object_data_provided */ + +/* + * Indicate that a range of the given temporary memory object does + * not exist, and that the backing memory object should be used + * instead (or zero-fill memory be used, if no backing object exists). + * [This call is intended for use only by the default memory manager. + * It should not be used to indicate a real error -- + * memory_object_data_error should be used for that purpose.] + */ +simpleroutine memory_object_data_unavailable( + memory_control : memory_object_control_t; + offset : vm_offset_t; + size : vm_size_t); + +/* + * Retrieves the attributes currently associated with + * a memory object. + */ +routine memory_object_get_attributes( + memory_control : memory_object_control_t; + out object_ready : boolean_t; + out may_cache : boolean_t; + out copy_strategy : memory_object_copy_strategy_t); + +#if ! KERNEL_USER + +/* + * Sets the default memory manager, the port to which + * newly-created temporary memory objects are delivered. + * [See (memory_object_default)memory_object_create.] + * The old memory manager port is returned. + */ +routine vm_set_default_memory_manager( + host_priv : host_priv_t; + inout default_manager : mach_port_make_send_t); + +#else /* ! KERNEL_USER */ + +skip; + +#endif /* ! KERNEL_USER */ + +skip; /* old pager_flush_request */ + +/* + * Control use of the data associated with the given + * memory object. For each page in the given range, + * perform the following operations, in order: + * 1) restrict access to the page (disallow + * forms specified by "prot"); + * 2) write back modifications (if "should_return" + * is RETURN_DIRTY and the page is dirty, or + * "should_return" is RETURN_ALL and the page + * is either dirty or precious); and, + * 3) flush the cached copy (if "should_flush" + * is asserted). + * The set of pages is defined by a starting offset + * ("offset") and size ("size"). Only pages with the + * same page alignment as the starting offset are + * considered. + * + * A single acknowledgement is sent (to the "reply_to" + * port) when these actions are complete. + * + * There are two versions of this routine because IPC distinguishes + * between booleans and integers (a 2-valued integer is NOT a + * boolean). The new routine is backwards compatible at the C + * language interface. + */ + +skip; /* old xxx_memory_object_lock_request */ + +simpleroutine memory_object_lock_request( + memory_control : memory_object_control_t; + offset : vm_offset_t; + size : vm_size_t; + should_return : memory_object_return_t; + should_flush : boolean_t; + lock_value : vm_prot_t; + reply_to : mach_port_t = + MACH_MSG_TYPE_MAKE_SEND_ONCE|polymorphic); + +skip; /* old xxx_task_get_emulation_vector */ +skip; /* old xxx_task_set_emulation_vector */ +skip; /* old xxx_host_info */ +skip; /* old xxx_slot_info */ +skip; /* old xxx_cpu_control */ +skip; /* old thread_statistics */ +skip; /* old task_statistics */ +skip; /* old netport_init */ +skip; /* old netport_enter */ +skip; /* old netport_remove */ +skip; /* old thread_set_priority */ + +#if ! KERNEL_USER + +/* + * Increment the suspend count for the target task. + * No threads within a task may run when the suspend + * count for that task is non-zero. + */ +routine task_suspend( + target_task : task_t); + +/* + * Decrement the suspend count for the target task, + * if the count is currently non-zero. If the resulting + * suspend count is zero, then threads within the task + * that also have non-zero suspend counts may execute. + */ +routine task_resume( + target_task : task_t); + +/* + * Returns the current value of the selected special port + * associated with the target task. + */ +routine task_get_special_port( + task : task_t; + which_port : int; + out special_port : mach_port_t); + +/* + * Set one of the special ports associated with the + * target task. + */ +routine task_set_special_port( + task : task_t; + which_port : int; + special_port : mach_port_t); + +skip; /* old xxx_task_info */ + + +/* + * Create a new thread within the target task, returning + * the port representing that new thread. The + * initial execution state of the thread is undefined. + */ +routine thread_create( + parent_task : task_t; + out child_thread : thread_t); + +/* + * Increment the suspend count for the target thread. + * Once this call has completed, the thread will not + * execute any further user or meta- instructions. + * Once suspended, a thread may not execute again until + * its suspend count is zero, and the suspend count + * for its task is also zero. + */ +routine thread_suspend( + target_thread : thread_t); + +/* + * Decrement the suspend count for the target thread, + * if that count is not already zero. + */ +routine thread_resume( + target_thread : thread_t); + +/* + * Cause any user or meta- instructions currently being + * executed by the target thread to be aborted. [Meta- + * instructions consist of the basic traps for IPC + * (e.g., msg_send, msg_receive) and self-identification + * (e.g., task_self, thread_self, thread_reply). Calls + * described by MiG interfaces are not meta-instructions + * themselves.] + */ +routine thread_abort( + target_thread : thread_t); + +skip; /* old xxx_thread_get_state */ +skip; /* old xxx_thread_set_state */ + +/* + * Returns the current value of the selected special port + * associated with the target thread. + */ +routine thread_get_special_port( + thread : thread_t; + which_port : int; + out special_port : mach_port_t); + +/* + * Set one of the special ports associated with the + * target thread. + */ +routine thread_set_special_port( + thread : thread_t; + which_port : int; + special_port : mach_port_t); + +skip; /* old xxx_thread_info */ + +/* + * Establish a user-level handler for the specified + * system call. + */ +routine task_set_emulation( + target_port : task_t; + routine_entry_pt: vm_address_t; + routine_number : int); + +/* + * Establish restart pc for interrupted atomic sequences. + * This reuses the message number for the old task_get_io_port. + * See task_info.h for description of flavors. + * + */ +routine task_ras_control( + target_task : task_t; + basepc : vm_address_t; + boundspc : vm_address_t; + flavor : int); + + + +skip; /* old host_ipc_statistics */ +skip; /* old port_names */ +skip; /* old port_type */ +skip; /* old port_rename */ +skip; /* old port_allocate */ +skip; /* old port_deallocate */ +skip; /* old port_set_backlog */ +skip; /* old port_status */ +skip; /* old port_set_allocate */ +skip; /* old port_set_deallocate */ +skip; /* old port_set_add */ +skip; /* old port_set_remove */ +skip; /* old port_set_status */ +skip; /* old port_insert_send */ +skip; /* old port_extract_send */ +skip; /* old port_insert_receive */ +skip; /* old port_extract_receive */ + +/* + * Map a user-defined memory object into the virtual address + * space of the target task. If desired (anywhere is TRUE), + * the kernel will find a suitable address range of the + * specified size; else, the specific address will be allocated. + * + * The beginning address of the range will be aligned on a virtual + * page boundary, be at or beyond the address specified, and + * meet the mask requirements (bits turned on in the mask must not + * be turned on in the result); the size of the range, in bytes, + * will be rounded up to an integral number of virtual pages. + * + * The memory in the resulting range will be associated with the + * specified memory object, with the beginning of the memory range + * referring to the specified offset into the memory object. + * + * The mapping will take the current and maximum protections and + * the inheritance attributes specified; see the vm_protect and + * vm_inherit calls for a description of these attributes. + * + * If desired (copy is TRUE), the memory range will be filled + * with a copy of the data from the memory object; this copy will + * be private to this mapping in this target task. Otherwise, + * the memory in this mapping will be shared with other mappings + * of the same memory object at the same offset (in this task or + * in other tasks). [The Mach kernel only enforces shared memory + * consistency among mappings on one host with similar page alignments. + * The user-defined memory manager for this object is responsible + * for further consistency.] + */ +#ifdef EMULATOR +routine htg_vm_map( + target_task : vm_task_t; + ureplyport reply_port : mach_port_make_send_once_t; + inout address : vm_address_t; + size : vm_size_t; + mask : vm_address_t; + anywhere : boolean_t; + memory_object : memory_object_t; + offset : vm_offset_t; + copy : boolean_t; + cur_protection : vm_prot_t; + max_protection : vm_prot_t; + inheritance : vm_inherit_t); +#else /* EMULATOR */ +routine vm_map( + target_task : vm_task_t; + inout address : vm_address_t; + size : vm_size_t; + mask : vm_address_t; + anywhere : boolean_t; + memory_object : memory_object_t; + offset : vm_offset_t; + copy : boolean_t; + cur_protection : vm_prot_t; + max_protection : vm_prot_t; + inheritance : vm_inherit_t); +#endif /* EMULATOR */ + +#else /* ! KERNEL_USER */ + +skip; skip; skip; skip; skip; +skip; skip; skip; skip; skip; +skip; skip; skip; skip; skip; +skip; skip; skip; skip; skip; +skip; skip; skip; skip; skip; +skip; skip; skip; skip; skip; +skip; skip; skip; skip; + +#endif /* ! KERNEL_USER */ + +/* + * Indicate that a range of the specified memory object cannot + * be provided at this time. [Threads waiting for memory pages + * specified by this call will experience a memory exception. + * Only threads waiting at the time of the call are affected.] + */ +simpleroutine memory_object_data_error( + memory_control : memory_object_control_t; + offset : vm_offset_t; + size : vm_size_t; + error_value : kern_return_t); + +skip; /* was: memory_object_set_attributes */ + +/* + */ +simpleroutine memory_object_destroy( + memory_control : memory_object_control_t; + reason : kern_return_t); + +/* + * Provide the data contents of a range of the given memory + * object, with the access restriction specified, optional + * precious attribute, and reply message. [Only + * whole virtual pages of data can be accepted; partial pages + * will be discarded. Data should be provided on request, but + * may be provided in advance as desired. When data already + * held by this kernel is provided again, the new data is ignored. + * The access restriction is the subset of {read, write, execute} + * which are prohibited. The kernel may not provide any data (or + * protection) consistency among pages with different virtual page + * alignments within the same object. The precious value controls + * how the kernel treats the data. If it is FALSE, the kernel treats + * its copy as a temporary and may throw it away if it hasn't been + * changed. If the precious value is TRUE, the kernel treats its + * copy as a data repository and promises to return it to the manager; + * the manager may tell the kernel to throw it away instead by flushing + * and not cleaning the data -- see memory_object_lock_request. The + * reply_to port is for a compeletion message; it will be + * memory_object_supply_completed.] + */ + +simpleroutine memory_object_data_supply( + memory_control : memory_object_control_t; + offset : vm_offset_t; + data : pointer_t, Dealloc[]; + lock_value : vm_prot_t; + precious : boolean_t; + reply_to : mach_port_t = + MACH_MSG_TYPE_MAKE_SEND_ONCE|polymorphic); + +simpleroutine memory_object_ready( + memory_control : memory_object_control_t; + may_cache : boolean_t; + copy_strategy : memory_object_copy_strategy_t); + +simpleroutine memory_object_change_attributes( + memory_control : memory_object_control_t; + may_cache : boolean_t; + copy_strategy : memory_object_copy_strategy_t; + reply_to : mach_port_t = + MACH_MSG_TYPE_MAKE_SEND_ONCE|polymorphic); + +#if ! KERNEL_USER + +skip; /* old host_callout_statistics_reset */ +skip; /* old port_set_select */ +skip; /* old port_set_backup */ + +/* + * Set/Get special properties of memory associated + * to some virtual address range, such as cachability, + * migrability, replicability. Machine-dependent. + */ +routine vm_machine_attribute( + target_task : vm_task_t; + address : vm_address_t; + size : vm_size_t; + attribute : vm_machine_attribute_t; + inout value : vm_machine_attribute_val_t); + +skip; /* old host_fpa_counters_reset */ + +#endif /* ! KERNEL_USER */ + +/* + * There is no more room in this interface for additional calls. + */ diff --git a/include/mach/mach4.defs b/include/mach/mach4.defs new file mode 100644 index 0000000..d63d6f7 --- /dev/null +++ b/include/mach/mach4.defs @@ -0,0 +1,131 @@ +/* + * Mach Operating System + * Copyright (c) 1994,1993,1992 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. + */ +/* + * Matchmaker definitions file for Mach4 kernel interface. + */ + +subsystem +#if KERNEL_SERVER + KernelServer +#endif /* KERNEL_SERVER */ +#if KERNEL_USER + KernelUser +#endif /* KERNEL_USER */ + mach4 4000; + +#include +#include + + +#ifdef MACH_PCSAMPLE +type sampled_pc_flavor_t = unsigned; + +type sampled_pc_t = struct { + rpc_vm_offset_t id; + rpc_vm_offset_t pc; + sampled_pc_flavor_t sampletype; +}; + +type sampled_pc_array_t = array[*:512] of sampled_pc_t; +type sampled_pc_seqno_t = unsigned; + +routine task_enable_pc_sampling( + host : task_t; + out tick : int; /* sample frequency in usecs */ + flavor : sampled_pc_flavor_t ); + +routine task_disable_pc_sampling( + host : task_t; + out samplecnt : int); + +routine task_get_sampled_pcs( + host : task_t; + inout seqno : sampled_pc_seqno_t; + out sampled_pcs : sampled_pc_array_t); + +routine thread_enable_pc_sampling( + host : thread_t; + out tick : int; /* sample frequency in usecs*/ + flavor : sampled_pc_flavor_t ); + +routine thread_disable_pc_sampling( + host : thread_t; + out samplecnt : int); + +routine thread_get_sampled_pcs( + host : thread_t; + inout seqno : sampled_pc_seqno_t; + out sampled_pcs : sampled_pc_array_t); + + +skip /* pc_sampling reserved 1*/; +skip /* pc_sampling reserved 2*/; +skip /* pc_sampling reserved 3*/; +skip /* pc_sampling reserved 4*/; + +#else + +skip; /* task_enable_pc_sampling */ +skip; /* task_disable_pc_sampling */ +skip; /* task_get_sampled_pcs */ +skip; /* thread_enable_pc_sampling */ +skip; /* thread_disable_pc_sampling */ +skip; /* thread_get_sampled_pcs */ + +skip /* pc_sampling reserved 1*/; +skip /* pc_sampling reserved 2*/; +skip /* pc_sampling reserved 3*/; +skip /* pc_sampling reserved 4*/; + +#endif + + +/* Create a new proxy memory object from [START;START+LEN) in the + given memory object OBJECT at OFFSET in the new object with the maximum + protection MAX_PROTECTION and return it in *PORT. */ +type vm_offset_array_t = array[*:1024] of vm_offset_t; +type vm_size_array_t = array[*:1024] of vm_size_t; +type rpc_vm_size_array_t = array[*:1024] of rpc_vm_size_t; +type rpc_vm_offset_array_t = array[*:1024] of rpc_vm_offset_t; +routine memory_object_create_proxy( + task : ipc_space_t; + max_protection : vm_prot_t; + object : memory_object_array_t = + array[*:1024] of mach_port_send_t; + offset : rpc_vm_offset_array_t; + start : rpc_vm_offset_array_t; + len : rpc_vm_size_array_t; + out proxy : mach_port_t); + +/* Gets a proxy to the region that ADDRESS belongs to, starting at the region + start, with MAX_PROTECTION and LEN limited by the region ones, and returns + it in *PORT. */ +routine vm_region_create_proxy( + task : task_t; + address : vm_address_t; + max_protection : vm_prot_t; + len : vm_size_t; + out proxy : mach_port_t); diff --git a/include/mach/mach_host.defs b/include/mach/mach_host.defs new file mode 100644 index 0000000..a8c40af --- /dev/null +++ b/include/mach/mach_host.defs @@ -0,0 +1,388 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University. + * Copyright (c) 1993,1994 The University of Utah and + * the Computer Systems Laboratory (CSL). + * 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, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF + * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM 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. + */ +/* + * File: mach/mach_host.defs + * + * Abstract: + * Mach host operations support. Includes processor allocation and + * control. + */ + +subsystem +#if KERNEL_SERVER + KernelServer +#endif + mach_host 2600; + +/* + * Basic types + */ + +#include +#include + +#ifdef MACH_HOST_IMPORTS +MACH_HOST_IMPORTS +#endif + +/* + * Get list of processors on this host. + */ + +routine host_processors( + host_priv : host_priv_t; + out processor_list : processor_array_t); + +skip; /* old yyy_host_info */ +skip; /* old yyy_processor_info */ + +/* + * Start processor. + */ + +routine processor_start( + processor : processor_t); + +/* + * Exit processor -- may not be restartable. + */ + +routine processor_exit( + processor : processor_t); + +skip; /* old yyy_processor_control */ + +/* + * Get default processor set for host. + */ +routine processor_set_default( + host : host_t; + out default_set : processor_set_name_t); + +skip; /* old xxx_processor_set_default_priv */ + +/* + * Create new processor set. Returns real port for manipulations, + * and name port for obtaining information. + */ +routine processor_set_create( + host : host_t; + out new_set : processor_set_t; + out new_name : processor_set_name_t); + +/* + * Destroy processor set. + */ +routine processor_set_destroy( + set : processor_set_t); + +skip; /* old yyy_processor_set_info */ + +/* + * Assign processor to processor set. + */ +routine processor_assign( + processor : processor_t; + new_set : processor_set_t; + wait : boolean_t); + +/* + * Get current assignment for processor. + */ + +routine processor_get_assignment( + processor : processor_t; + out assigned_set : processor_set_name_t); + +/* + * Assign thread to processor set. + */ +routine thread_assign( + thread : thread_t; + new_set : processor_set_t); + +/* + * Assign thread to default set. + */ +routine thread_assign_default( + thread : thread_t); + +/* + * Get current assignment for thread. + */ +routine thread_get_assignment( + thread : thread_t; + out assigned_set : processor_set_name_t); + +/* + * Assign task to processor set. + */ +routine task_assign( + task : task_t; + new_set : processor_set_t; + assign_threads : boolean_t); +/* + * Assign task to default set. + */ +routine task_assign_default( + task : task_t; + assign_threads : boolean_t); + +/* + * Get current assignment for task. + */ +routine task_get_assignment( + task : task_t; + out assigned_set : processor_set_name_t); + +#if defined(__x86_64__) && !defined(USER32) +skip; +#else +/* + * Get string describing current kernel version. + * Deprecated, use host_get_kernel_version. + */ +routine host_kernel_version( + host : host_t; + out kernel_version : kernel_version_t); +#endif + +/* + * Set priority for thread. + */ +routine thread_priority( + thread : thread_t; + priority : int; + set_max : boolean_t); + +/* + * Set max priority for thread. + */ +routine thread_max_priority( + thread : thread_t; + processor_set : processor_set_t; + max_priority : int); + +/* + * Set task priority. + */ +routine task_priority( + task : task_t; + priority : int; + change_threads : boolean_t); + +/* + * Set max priority for processor_set. + */ +routine processor_set_max_priority( + processor_set : processor_set_t; + max_priority : int; + change_threads : boolean_t); + +/* + * Set policy for thread + */ +routine thread_policy( + thread : thread_t; + policy : int; + data : int); + +/* + * Enable policy for processor set + */ +routine processor_set_policy_enable( + processor_set : processor_set_t; + policy : int); + +/* + * Disable policy for processor set + */ +routine processor_set_policy_disable( + processor_set : processor_set_t; + policy : int; + change_threads : boolean_t); +/* + * List all tasks in processor set. + */ +routine processor_set_tasks( + processor_set : processor_set_t; + out task_list : task_array_t); + +/* + * List all threads in processor set. + */ +routine processor_set_threads( + processor_set : processor_set_t; + out thread_list : thread_array_t); + +/* + * List all processor sets on host. + */ +routine host_processor_sets( + host : host_t; + out processor_sets : processor_set_name_array_t); + +/* + * Get control port for a processor set. + */ +routine host_processor_set_priv( + host_priv : host_priv_t; + set_name : processor_set_name_t; + out set : processor_set_t); + +routine thread_depress_abort( + thread : thread_t); + +/* + * Set the time on this host. + * Only available to privileged users. + */ +routine host_set_time( + host_priv : host_priv_t; + new_time : time_value_t); + +/* + * Arrange for the time on this host to be gradually changed + * by an adjustment value, and return the old value. + * Only available to privileged users. + */ +routine host_adjust_time( + host_priv : host_priv_t; + in new_adjustment : time_value_t; + out old_adjustment : time_value_t); + +/* + * Get the time on this host. + * Available to all. + */ +routine host_get_time( + host : host_t; + out current_time : time_value_t); + +/* + * Reboot this host. + * Only available to privileged users. + */ +routine host_reboot( + host_priv : host_priv_t; + options : int); + +/* + * Specify that the range of the virtual address space + * of the target task must not cause page faults for + * the indicated accesses. + * + * [ To unwire the pages, specify VM_PROT_NONE. ] + */ +routine vm_wire( + host : mach_port_t; + task : vm_task_t; + address : vm_address_t; + size : vm_size_t; + access : vm_prot_t); + +/* + * Specify that the target thread must always be able + * to run and to allocate memory. + */ +routine thread_wire( + host_priv : host_priv_t; + thread : thread_t; + wired : boolean_t); + +/* + * Return information about this host. + */ + +routine host_info( + host : host_t; + flavor : int; + out host_info_out : host_info_t, CountInOut); + + +/* + * Return information about this processor. + */ +routine processor_info( + processor : processor_t; + flavor : int; + out host : host_t; + out processor_info_out: processor_info_t, CountInOut); + +/* + * Get information about processor set. + */ +routine processor_set_info( + set_name : processor_set_name_t; + flavor : int; + out host : host_t; + out info_out : processor_set_info_t, CountInOut); + +/* + * Do something machine-dependent to processor. + */ +routine processor_control( + processor : processor_t; + processor_cmd : processor_info_t); + +/* host_get_boot_info */ +skip; + +/* + * Get the time on this host. + * Available to all. + */ +routine host_get_time64( + host : host_t; + out current_time : time_value64_t); + +/* + * Set the time on this host. + * Only available to privileged users. + */ +routine host_set_time64( + host : host_t; + new_time : time_value64_t); + +/* + * Arrange for the time on this host to be gradually changed + * by an adjustment value, and return the old value. + * Only available to privileged users. + */ +routine host_adjust_time64( + host_priv : host_priv_t; + in new_adjustment : time_value64_t; + out old_adjustment : time_value64_t); + +/* + * Get string describing current kernel version. + */ +routine host_get_kernel_version( + host : host_t; + out kernel_version : new_kernel_version_t); diff --git a/include/mach/mach_param.h b/include/mach/mach_param.h new file mode 100644 index 0000000..b2aca08 --- /dev/null +++ b/include/mach/mach_param.h @@ -0,0 +1,39 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988,1987 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. + */ +/* + * File: mach/mach_param.h + * Author: Avadis Tevanian, Jr., Michael Wayne Young + * Date: 1986 + * + * Mach system sizing parameters + */ + +#ifndef _MACH_MACH_PARAM_H_ +#define _MACH_MACH_PARAM_H_ + +#define TASK_PORT_REGISTER_MAX 4 /* Number of "registered" ports */ + +#endif /* _MACH_MACH_PARAM_H_ */ diff --git a/include/mach/mach_port.defs b/include/mach/mach_port.defs new file mode 100644 index 0000000..3823bb1 --- /dev/null +++ b/include/mach/mach_port.defs @@ -0,0 +1,360 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University. + * Copyright (c) 1993,1994 The University of Utah and + * the Computer Systems Laboratory (CSL). + * 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, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF + * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM 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. + */ +/* + * File: mach/mach_port.defs + * Author: Rich Draves + * + * Copyright (c) 1989 Richard P. Draves, Jr. + * + * Exported kernel calls. + */ + +subsystem +#if KERNEL_SERVER + KernelServer +#endif + mach_port 3200; + +#include +#include + +/* + * Returns the set of port and port set names + * to which the target task has access, along with + * the type (set or port) for each name. + */ + +routine mach_port_names( + task : ipc_space_t; + out names : mach_port_name_array_t = + ^array[] of mach_port_name_t; + out types : mach_port_type_array_t = + ^array[] of mach_port_type_t); + +/* + * Returns the type (set or port) for the port name + * within the target task. Also indicates whether + * there is a dead-name request for the name. + */ + +routine mach_port_type( + task : ipc_space_t; + name : mach_port_name_t; + out ptype : mach_port_type_t); + +/* + * Changes the name by which a port (or port set) is known to + * the target task. The new name can't be in use. The + * old name becomes available for recycling. + */ + +routine mach_port_rename( + task : ipc_space_t; + old_name : mach_port_name_t; + new_name : mach_port_name_t); + +/* + * Allocates the specified kind of object, with the given name. + * The right must be one of + * MACH_PORT_RIGHT_RECEIVE + * MACH_PORT_RIGHT_PORT_SET + * MACH_PORT_RIGHT_DEAD_NAME + * New port sets are empty. New ports don't have any + * send/send-once rights or queued messages. The make-send + * count is zero and their queue limit is MACH_PORT_QLIMIT_DEFAULT. + * New sets, ports, and dead names have one user reference. + */ + +routine mach_port_allocate_name( + task : ipc_space_t; + right : mach_port_right_t; + name : mach_port_name_t); + +/* + * Allocates the specified kind of object. + * The right must be one of + * MACH_PORT_RIGHT_RECEIVE + * MACH_PORT_RIGHT_PORT_SET + * MACH_PORT_RIGHT_DEAD_NAME + * Like port_allocate_name, but the kernel picks a name. + * It can use any name not associated with a right. + */ + +routine mach_port_allocate( + task : ipc_space_t; + right : mach_port_right_t; + out name : mach_port_name_t); + +/* + * Destroys all rights associated with the name and makes it + * available for recycling immediately. The name can be a + * port (possibly with multiple user refs), a port set, or + * a dead name (again, with multiple user refs). + */ + +routine mach_port_destroy( + task : ipc_space_t; + name : mach_port_name_t); + +/* + * Releases one send/send-once/dead-name user ref. + * Just like mach_port_mod_refs -1, but deduces the + * correct type of right. This allows a user task + * to release a ref for a port without worrying + * about whether the port has died or not. + */ + +routine mach_port_deallocate( + task : ipc_space_t; + name : mach_port_name_t); + +/* + * A port set always has one user ref. + * A send-once right always has one user ref. + * A dead name always has one or more user refs. + * A send right always has one or more user refs. + * A receive right always has one user ref. + * The right must be one of + * MACH_PORT_RIGHT_RECEIVE + * MACH_PORT_RIGHT_PORT_SET + * MACH_PORT_RIGHT_DEAD_NAME + * MACH_PORT_RIGHT_SEND + * MACH_PORT_RIGHT_SEND_ONCE + */ + +routine mach_port_get_refs( + task : ipc_space_t; + name : mach_port_name_t; + right : mach_port_right_t; + out refs : mach_port_urefs_t); + +/* + * The delta is a signed change to the task's + * user ref count for the right. Only dead names + * and send rights can have a positive delta. + * The resulting user ref count can't be negative. + * If it is zero, the right is deallocated. + * If the name isn't a composite right, it becomes + * available for recycling. The right must be one of + * MACH_PORT_RIGHT_RECEIVE + * MACH_PORT_RIGHT_PORT_SET + * MACH_PORT_RIGHT_DEAD_NAME + * MACH_PORT_RIGHT_SEND + * MACH_PORT_RIGHT_SEND_ONCE + */ + +routine mach_port_mod_refs( + task : ipc_space_t; + name : mach_port_name_t; + right : mach_port_right_t; + delta : mach_port_delta_t); + +skip; /* old old_mach_port_get_receive_status */ + +/* + * Only valid for receive rights. + * Sets the queue-limit for the port. + * The limit must be + * 1 <= qlimit <= MACH_PORT_QLIMIT_MAX + */ + +routine mach_port_set_qlimit( + task : ipc_space_t; + name : mach_port_name_t; + qlimit : mach_port_msgcount_t); + +/* + * Only valid for receive rights. + * Sets the make-send count for the port. + */ + +routine mach_port_set_mscount( + task : ipc_space_t; + name : mach_port_name_t; + mscount : mach_port_mscount_t); + +/* + * Only valid for port sets. Returns a list of + * the members. + */ + +routine mach_port_get_set_status( + task : ipc_space_t; + name : mach_port_name_t; + out members : mach_port_name_array_t = + ^array[] of mach_port_name_t); + +/* + * Puts the member port (the task must have receive rights) + * into the after port set. (Or removes it from any port set + * if after is MACH_PORT_NULL.) If the port is already in + * a set, does an atomic move. + */ + +routine mach_port_move_member( + task : ipc_space_t; + member : mach_port_name_t; + after : mach_port_name_t); + +/* + * Requests a notification from the kernel. The request + * must supply the send-once right which is used for + * the notification. If a send-once right was previously + * registered, it is returned. The msg_id must be one of + * MACH_NOTIFY_PORT_DESTROYED (receive rights) + * MACH_NOTIFY_DEAD_NAME (send/receive/send-once rights) + * MACH_NOTIFY_NO_SENDERS (receive rights) + * + * The sync value specifies whether a notification should + * get sent immediately, if appropriate. The exact meaning + * depends on the notification: + * MACH_NOTIFY_PORT_DESTROYED: must be zero. + * MACH_NOTIFY_DEAD_NAME: if non-zero, then name can be dead, + * and the notification gets sent immediately. + * If zero, then name can't be dead. + * MACH_NOTIFY_NO_SENDERS: the notification gets sent + * immediately if the current mscount is greater + * than or equal to the sync value and there are no + * extant send rights. + */ + +routine mach_port_request_notification( + task : ipc_space_t; + name : mach_port_name_t; + id : mach_msg_id_t; + sync : mach_port_mscount_t; + notify : mach_port_send_once_t; + out previous : mach_port_send_once_t); + +/* + * Inserts the specified rights into the target task, + * using the specified name. If inserting send/receive + * rights and the task already has send/receive rights + * for the port, then the names must agree. In any case, + * the task gains a user ref for the port. + */ + +routine mach_port_insert_right( + task : ipc_space_t; + name : mach_port_name_t; + poly : mach_port_poly_t); + +/* + * Returns the specified right for the named port + * in the target task, extracting that right from + * the target task. The target task loses a user + * ref and the name may be available for recycling. + * msgt_name must be one of + * MACH_MSG_TYPE_MOVE_RECEIVE + * MACH_MSG_TYPE_COPY_SEND + * MACH_MSG_TYPE_MAKE_SEND + * MACH_MSG_TYPE_MOVE_SEND + * MACH_MSG_TYPE_MAKE_SEND_ONCE + * MACH_MSG_TYPE_MOVE_SEND_ONCE + */ + +routine mach_port_extract_right( + task : ipc_space_t; + name : mach_port_name_t; + msgt_name : mach_msg_type_name_t; + out poly : mach_port_poly_t); + +/* + * The task must have receive rights for the named port. + * Returns a status structure (see mach/port.h). + */ + +routine mach_port_get_receive_status( + task : ipc_space_t; + name : mach_port_name_t; + out status : mach_port_status_t); + +/* + * Only valid for receive rights. + * Sets the sequence number for the port. + */ + +routine mach_port_set_seqno( + task : ipc_space_t; + name : mach_port_name_t; + seqno : mach_port_seqno_t); + +#ifdef MIGRATING_THREADS +/* + * Only valid for receive rights. + * Set the user-mode entry info for RPCs coming through this port. + * Do this BEFORE attaching an ActPool to this port, + * unless you can be sure no RPCs will be coming through it yet. + */ + +routine mach_port_set_rpcinfo( + task : ipc_space_t; + name : mach_port_name_t; + rpc_info : thread_info_t); /* XXX */ + +/* + * Only valid for receive rights. + * Create a new activation for migrating RPC, and attach it to the port's ActPool. + * Create an ActPool for the port if it doesn't already have one. + * Supply a stack and receive memory buffer. + */ + +routine mach_port_create_act( + task : task_t; + name : mach_port_name_t; + user_stack : vm_offset_t; + user_rbuf : vm_offset_t; + user_rbuf_size : vm_size_t; + out new_act : thread_t); + +#else /* MIGRATING_THREADS */ + +skip; /* mach_port_set_rpcinfo */ +skip; /* mach_port_create_act */ + +#endif /* MIGRATING_THREADS */ + +/* + * Only valid for receive rights. + * Set the protected payload for this right to the given value. + */ + +routine mach_port_set_protected_payload( + task : ipc_space_t; + name : mach_port_name_t; + payload : rpc_uintptr_t); + +/* + * Only valid for receive rights. + * Clear the protected payload for this right. + */ + +routine mach_port_clear_protected_payload( + task : ipc_space_t; + name : mach_port_name_t); diff --git a/include/mach/mach_traps.h b/include/mach/mach_traps.h new file mode 100644 index 0000000..2a87f62 --- /dev/null +++ b/include/mach/mach_traps.h @@ -0,0 +1,43 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988,1987 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. + */ +/* + * Definitions of general Mach system traps. + * + * IPC traps are defined in . + * Kernel RPC functions are defined in . + */ + +#ifndef _MACH_MACH_TRAPS_H_ +#define _MACH_MACH_TRAPS_H_ + +#include + +mach_port_name_t mach_reply_port (void); +mach_port_name_t mach_thread_self (void); +mach_port_name_t mach_task_self (void); +mach_port_name_t mach_host_self (void); + +#endif /* _MACH_MACH_TRAPS_H_ */ diff --git a/include/mach/mach_types.defs b/include/mach/mach_types.defs new file mode 100644 index 0000000..7419601 --- /dev/null +++ b/include/mach/mach_types.defs @@ -0,0 +1,299 @@ +/* + * Mach Operating System + * Copyright (c) 1994-1988 Carnegie Mellon University. + * Copyright (c) 1993,1994 The University of Utah and + * the Computer Systems Laboratory (CSL). + * 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, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF + * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM 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. + */ +/* + * Mach kernel interface type declarations + */ + +#ifndef _MACH_MACH_TYPES_DEFS_ +#define _MACH_MACH_TYPES_DEFS_ + +/* + * For KernelServer and KernelUser interfaces, Mig will + * automagically use ipc_port_t instead of mach_port_t + * on the kernel side of the interface. For example, + * convert_task_to_port really returns ipc_port_t. + * Doing this in Mig saves many explicit conditional + * cusertype/cservertype declarations. + * + * Mig doesn't translate the components of an array. + * For example, Mig won't use the thread_t translations + * to translate a thread_array_t argument. + */ + +#include +#if KERNEL_SERVER +#endif /* KERNEL_SERVER */ + +#ifdef USERPREFIX +userprefix USERPREFIX; +#endif + +#ifdef SERVERPREFIX +serverprefix SERVERPREFIX; +#endif + +type mach_port_status_t = struct { + mach_port_name_t mps_pset; /* containing port set */ + mach_port_seqno_t mps_seqno; /* sequence number */ + mach_port_mscount_t mps_mscount; /* make-send count */ + mach_port_msgcount_t mps_qlimit; /* queue limit */ + mach_port_msgcount_t mps_msgcount; /* number in the queue */ + mach_port_rights_t mps_sorights; /* how many send-once rights */ + boolean_t mps_srights; /* do send rights exist? */ + boolean_t mps_pdrequest; /* port-deleted requested? */ + boolean_t mps_nsrequest; /* no-senders requested? */ +}; + +type task_t = mach_port_t + ctype: mach_port_t +#if KERNEL_SERVER + intran: task_t convert_port_to_task(mach_port_t) + outtran: mach_port_t convert_task_to_port(task_t) + destructor: task_deallocate(task_t) +#endif /* KERNEL_SERVER */ + ; + +#ifdef MIGRATING_THREADS +#if KERNEL +/* What the conventional external Mach interfaces see as a thread_t + is really an act_t within the kernel. */ +#define thread_t act_t +#define convert_port_to_thread convert_port_to_act +#define convert_thread_to_port convert_act_to_port +#define thread_deallocate act_deallocate +#endif /* KERNEL */ +#endif /* MIGRATING_THREADS */ + +type thread_t = mach_port_t + ctype: mach_port_t +#if KERNEL_SERVER + intran: thread_t convert_port_to_thread(mach_port_t) + outtran: mach_port_t convert_thread_to_port(thread_t) + destructor: thread_deallocate(thread_t) +#endif /* KERNEL_SERVER */ + ; + +type thread_state_t = array[*:1024] of natural_t; + +type task_array_t = ^array[] of task_t; +type thread_array_t = ^array[] of thread_t; + +type vm_task_t = mach_port_t + ctype: mach_port_t +#if KERNEL_SERVER + intran: vm_map_t convert_port_to_map(mach_port_t) + destructor: vm_map_deallocate(vm_map_t) +#endif /* KERNEL_SERVER */ + ; + +type ipc_space_t = mach_port_t + ctype: mach_port_t +#if KERNEL_SERVER + intran: ipc_space_t convert_port_to_space(mach_port_t) + destructor: space_deallocate(ipc_space_t) +#endif /* KERNEL_SERVER */ + ; + +#if defined(KERNEL) && defined(USER32) +type rpc_uintptr_t = uint32_t; +type rpc_vm_size_t = uint32_t; +#else /* KERNEL and USER32 */ +type rpc_uintptr_t = uintptr_t; +type rpc_vm_size_t = uintptr_t; +#endif /* KERNEL_SERVER and USER32 */ + +type rpc_vm_offset_t = rpc_vm_size_t; + +type vm_address_t = rpc_vm_size_t +#if defined(KERNEL_SERVER) + intran: vm_address_t convert_vm_from_user(rpc_vm_address_t) + outtran: rpc_vm_address_t convert_vm_to_user(vm_address_t) +#elif defined(KERNEL_USER) + ctype: rpc_vm_address_t +#endif + ; +type vm_offset_t = rpc_vm_offset_t +#if defined(KERNEL_SERVER) + intran: vm_offset_t convert_vm_from_user(rpc_vm_offset_t) + outtran: rpc_vm_offset_t convert_vm_to_user(vm_offset_t) +#elif defined(KERNEL_USER) + ctype: rpc_vm_offset_t +#endif + ; +type vm_size_t = rpc_vm_size_t +#if defined(KERNEL_SERVER) + intran: vm_size_t convert_vm_from_user(rpc_vm_size_t) + outtran: rpc_vm_size_t convert_vm_to_user(vm_size_t) +#elif defined(KERNEL_USER) + ctype: rpc_vm_size_t +#endif +; +type vm_prot_t = int; +type vm_inherit_t = int; +type vm_statistics_data_t = struct[13] of integer_t; +type vm_machine_attribute_t = int; +type vm_machine_attribute_val_t = int; +type vm_sync_t = int; + +type thread_info_t = array[*:1024] of integer_t; + +type task_info_t = array[*:1024] of integer_t; + +type memory_object_t = mach_port_t + ctype: mach_port_t +#if KERNEL_SERVER + intran: ipc_port_t null_conversion(mach_port_t) +#else /* KERNEL_SERVER */ +#ifdef MEMORY_OBJECT_INTRAN + intran: MEMORY_OBJECT_INTRAN +#endif +#ifdef MEMORY_OBJECT_INTRAN_PAYLOAD + intranpayload: MEMORY_OBJECT_INTRAN_PAYLOAD +#endif +#ifdef MEMORY_OBJECT_OUTTRAN + outtran: MEMORY_OBJECT_OUTTRAN +#endif +#ifdef MEMORY_OBJECT_DESTRUCTOR + destructor: MEMORY_OBJECT_DESTRUCTOR +#endif +#endif /* KERNEL_SERVER */ + ; + +type memory_object_control_t = mach_port_t + ctype: mach_port_t +#if KERNEL_SERVER + intran: vm_object_t vm_object_lookup(mach_port_t) +#endif /* KERNEL_SERVER */ + ; + +type memory_object_name_t = mach_port_t + ctype: mach_port_t +#if KERNEL_SERVER + intran: vm_object_t vm_object_lookup_name(mach_port_t) + destructor: vm_object_deallocate(vm_object_t) +#endif /* KERNEL_SERVER */ + ; + +type memory_object_copy_strategy_t = int; +type memory_object_return_t = int; + +type host_t = mach_port_t + ctype: mach_port_t +#if KERNEL_SERVER + intran: host_t convert_port_to_host(mach_port_t) + outtran: mach_port_t convert_host_to_port(host_t) +#endif /* KERNEL_SERVER */ + ; + +type host_priv_t = mach_port_t + ctype: mach_port_t +#if KERNEL_SERVER + intran: host_t convert_port_to_host_priv(mach_port_t) +#endif /* KERNEL_SERVER */ + ; + +type host_info_t = array[*:1024] of integer_t; + +type processor_t = mach_port_t + ctype: mach_port_t +#if KERNEL_SERVER + intran: processor_t convert_port_to_processor(mach_port_t) + outtran: mach_port_t convert_processor_to_port(processor_t) +#endif /* KERNEL_SERVER */ + ; + +type processor_array_t = ^array[] of processor_t; +type processor_info_t = array[*:1024] of integer_t; + +type processor_set_t = mach_port_t + ctype: mach_port_t +#if KERNEL_SERVER + intran: processor_set_t convert_port_to_pset(mach_port_t) + outtran: mach_port_t convert_pset_to_port(processor_set_t) + destructor: pset_deallocate(processor_set_t) +#endif /* KERNEL_SERVER */ + ; + +type processor_set_array_t = ^array[] of processor_set_t; + +type processor_set_name_t = mach_port_t + ctype: mach_port_t +#if KERNEL_SERVER + intran: processor_set_t convert_port_to_pset_name(mach_port_t) + outtran: mach_port_t convert_pset_name_to_port(processor_set_t) + destructor: pset_deallocate(processor_set_t) +#endif /* KERNEL_SERVER */ + ; + +type processor_set_name_array_t = ^array[] of processor_set_name_t; + +type processor_set_info_t = array[*:1024] of integer_t; + +type kernel_version_t = (MACH_MSG_TYPE_STRING, 512*8); +type new_kernel_version_t = c_string[512] + ctype: kernel_version_t; + +type rpc_time_value_t = struct { + rpc_long_integer_t seconds; + integer_t microseconds; +}; +type time_value_t = rpc_time_value_t +#if defined(KERNEL_SERVER) + intran: time_value_t convert_time_value_from_user(rpc_time_value_t) + outtran: rpc_time_value_t convert_time_value_to_user(time_value_t) +#elif defined(KERNEL_USER) + ctype: rpc_time_value_t +#endif + ; + +type time_value64_t = struct { + int64_t seconds; + int64_t nanoseconds; +}; + +type emulation_vector_t = ^array[] of vm_offset_t; + +type rpc_signature_info_t = array[*:1024] of int; + +#if KERNEL_SERVER +simport ; /* for null conversion */ +simport ; /* for task/thread conversion */ +simport ; /* for host/processor/pset conversions */ +simport ; /* for task_t */ +simport ; /* for thread_t */ +simport ; /* for host_t */ +simport ; /* for processor_t, processor_set_t */ +simport ; /* for vm_object_t */ +simport ; /* for vm_map_t */ +simport ; /* for ipc_space_t */ +#endif /* KERNEL_SERVER */ + +import ; + +#endif /* _MACH_MACH_TYPES_DEFS_ */ diff --git a/include/mach/mach_types.h b/include/mach/mach_types.h new file mode 100644 index 0000000..5ecd686 --- /dev/null +++ b/include/mach/mach_types.h @@ -0,0 +1,90 @@ +/* + * Mach Operating System + * Copyright (c) 1992,1991,1990,1989,1988 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. + */ +/* + * File: mach/mach_types.h + * Author: Avadis Tevanian, Jr., Michael Wayne Young + * Date: 1986 + * + * Mach external interface definitions. + * + */ + +#ifndef _MACH_MACH_TYPES_H_ +#define _MACH_MACH_TYPES_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef MACH_KERNEL + +typedef struct task *task_t; +typedef struct thread *thread_t; +typedef struct processor *processor_t; +typedef struct processor_set *processor_set_t; + +#else /* MACH_KERNEL */ +typedef mach_port_t task_t; +typedef task_t *task_array_t; +typedef task_t vm_task_t; +typedef task_t ipc_space_t; +typedef mach_port_t thread_t; +typedef thread_t *thread_array_t; +typedef mach_port_t host_t; +typedef mach_port_t host_priv_t; +typedef mach_port_t processor_t; +typedef mach_port_t *processor_array_t; +typedef mach_port_t processor_set_t; +typedef mach_port_t processor_set_name_t; +typedef mach_port_t *processor_set_array_t; +typedef mach_port_t *processor_set_name_array_t; +typedef vm_offset_t *emulation_vector_t; +#endif /* MACH_KERNEL */ + +/* + * Backwards compatibility, for those programs written + * before mach/{std,mach}_types.{defs,h} were set up. + */ +#include + +#endif /* _MACH_MACH_TYPES_H_ */ diff --git a/include/mach/machine.h b/include/mach/machine.h new file mode 100644 index 0000000..9a176e8 --- /dev/null +++ b/include/mach/machine.h @@ -0,0 +1,268 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988,1987 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. + */ +/* File: machine.h + * Author: Avadis Tevanian, Jr. + * Date: 1986 + * + * Machine independent machine abstraction. + */ + +#ifndef _MACH_MACHINE_H_ +#define _MACH_MACHINE_H_ + +#include +#include + +/* + * For each host, there is a maximum possible number of + * cpus that may be available in the system. This is the + * compile-time constant NCPUS, which is defined in cpus.h. + * + * In addition, there is a machine_slot specifier for each + * possible cpu in the system. + */ + +struct machine_info { + integer_t major_version; /* kernel major version id */ + integer_t minor_version; /* kernel minor version id */ + integer_t max_cpus; /* max number of cpus compiled */ + integer_t avail_cpus; /* number actually available */ + vm_size_t memory_size; /* size of memory in bytes */ +}; + +typedef struct machine_info *machine_info_t; +typedef struct machine_info machine_info_data_t; /* bogus */ + +typedef integer_t cpu_type_t; +typedef integer_t cpu_subtype_t; + +#define CPU_STATE_MAX 3 + +#define CPU_STATE_USER 0 +#define CPU_STATE_SYSTEM 1 +#define CPU_STATE_IDLE 2 + +struct machine_slot { +/*boolean_t*/integer_t is_cpu; /* is there a cpu in this slot? */ + cpu_type_t cpu_type; /* type of cpu */ + cpu_subtype_t cpu_subtype; /* subtype of cpu */ +/*boolean_t*/integer_t running; /* is cpu running */ + integer_t cpu_ticks[CPU_STATE_MAX]; + integer_t clock_freq; /* clock interrupt frequency */ +}; + +typedef struct machine_slot *machine_slot_t; +typedef struct machine_slot machine_slot_data_t; /* bogus */ + +#ifdef MACH_KERNEL +extern struct machine_info machine_info; +extern struct machine_slot machine_slot[NCPUS]; +#endif /* MACH_KERNEL */ + +/* + * Machine types known by all. + * + * When adding new types & subtypes, please also update slot_name.c + * in the libmach sources. + */ + +#define CPU_TYPE_VAX ((cpu_type_t) 1) +#define CPU_TYPE_ROMP ((cpu_type_t) 2) +#define CPU_TYPE_MC68020 ((cpu_type_t) 3) +#define CPU_TYPE_NS32032 ((cpu_type_t) 4) +#define CPU_TYPE_NS32332 ((cpu_type_t) 5) +#define CPU_TYPE_NS32532 ((cpu_type_t) 6) +#define CPU_TYPE_I386 ((cpu_type_t) 7) +#define CPU_TYPE_MIPS ((cpu_type_t) 8) +#define CPU_TYPE_MC68030 ((cpu_type_t) 9) +#define CPU_TYPE_MC68040 ((cpu_type_t) 10) +#define CPU_TYPE_HPPA ((cpu_type_t) 11) +#define CPU_TYPE_ARM ((cpu_type_t) 12) +#define CPU_TYPE_MC88000 ((cpu_type_t) 13) +#define CPU_TYPE_SPARC ((cpu_type_t) 14) +#define CPU_TYPE_I860 ((cpu_type_t) 15) +#define CPU_TYPE_ALPHA ((cpu_type_t) 16) +#define CPU_TYPE_I486 ((cpu_type_t) 17) +#define CPU_TYPE_PENTIUM ((cpu_type_t) 18) +#define CPU_TYPE_PENTIUMPRO ((cpu_type_t) 19) +#define CPU_TYPE_POWERPC ((cpu_type_t) 20) +#define CPU_TYPE_X86_64 ((cpu_type_t) 21) + +/* + * Machine subtypes (these are defined here, instead of in a machine + * dependent directory, so that any program can get all definitions + * regardless of where is it compiled). + */ + +/* + * VAX subtypes (these do *not* necessarily conform to the actual cpu + * ID assigned by DEC available via the SID register). + */ + +#define CPU_SUBTYPE_VAX780 ((cpu_subtype_t) 1) +#define CPU_SUBTYPE_VAX785 ((cpu_subtype_t) 2) +#define CPU_SUBTYPE_VAX750 ((cpu_subtype_t) 3) +#define CPU_SUBTYPE_VAX730 ((cpu_subtype_t) 4) +#define CPU_SUBTYPE_UVAXI ((cpu_subtype_t) 5) +#define CPU_SUBTYPE_UVAXII ((cpu_subtype_t) 6) +#define CPU_SUBTYPE_VAX8200 ((cpu_subtype_t) 7) +#define CPU_SUBTYPE_VAX8500 ((cpu_subtype_t) 8) +#define CPU_SUBTYPE_VAX8600 ((cpu_subtype_t) 9) +#define CPU_SUBTYPE_VAX8650 ((cpu_subtype_t) 10) +#define CPU_SUBTYPE_VAX8800 ((cpu_subtype_t) 11) +#define CPU_SUBTYPE_UVAXIII ((cpu_subtype_t) 12) + +/* + * ROMP subtypes. + */ + +#define CPU_SUBTYPE_RT_PC ((cpu_subtype_t) 1) +#define CPU_SUBTYPE_RT_APC ((cpu_subtype_t) 2) +#define CPU_SUBTYPE_RT_135 ((cpu_subtype_t) 3) + +/* + * 68020 subtypes. + */ + +#define CPU_SUBTYPE_SUN3_50 ((cpu_subtype_t) 1) +#define CPU_SUBTYPE_SUN3_160 ((cpu_subtype_t) 2) +#define CPU_SUBTYPE_SUN3_260 ((cpu_subtype_t) 3) +#define CPU_SUBTYPE_SUN3_110 ((cpu_subtype_t) 4) +#define CPU_SUBTYPE_SUN3_60 ((cpu_subtype_t) 5) + +#define CPU_SUBTYPE_HP_320 ((cpu_subtype_t) 6) + /* 16.67 Mhz HP 300 series, custom MMU [HP 320] */ +#define CPU_SUBTYPE_HP_330 ((cpu_subtype_t) 7) + /* 16.67 Mhz HP 300 series, MC68851 MMU [HP 318,319,330,349] */ +#define CPU_SUBTYPE_HP_350 ((cpu_subtype_t) 8) + /* 25.00 Mhz HP 300 series, custom MMU [HP 350] */ + +/* + * 32032/32332/32532 subtypes. + */ + +#define CPU_SUBTYPE_MMAX_DPC ((cpu_subtype_t) 1) /* 032 CPU */ +#define CPU_SUBTYPE_SQT ((cpu_subtype_t) 2) +#define CPU_SUBTYPE_MMAX_APC_FPU ((cpu_subtype_t) 3) /* 32081 FPU */ +#define CPU_SUBTYPE_MMAX_APC_FPA ((cpu_subtype_t) 4) /* Weitek FPA */ +#define CPU_SUBTYPE_MMAX_XPC ((cpu_subtype_t) 5) /* 532 CPU */ +#define CPU_SUBTYPE_PC532 ((cpu_subtype_t) 6) /* pc532 board */ + +/* + * 80386/80486 subtypes. + */ + +#define CPU_SUBTYPE_AT386 ((cpu_subtype_t) 1) +#define CPU_SUBTYPE_EXL ((cpu_subtype_t) 2) +#define CPU_SUBTYPE_iPSC386 ((cpu_subtype_t) 3) +#define CPU_SUBTYPE_SYMMETRY ((cpu_subtype_t) 4) +#define CPU_SUBTYPE_PS2 ((cpu_subtype_t) 5) /* PS/2 w/ MCA */ + +/* + * Mips subtypes. + */ + +#define CPU_SUBTYPE_MIPS_R2300 ((cpu_subtype_t) 1) +#define CPU_SUBTYPE_MIPS_R2600 ((cpu_subtype_t) 2) +#define CPU_SUBTYPE_MIPS_R2800 ((cpu_subtype_t) 3) +#define CPU_SUBTYPE_MIPS_R2000a ((cpu_subtype_t) 4) /* pmax */ +#define CPU_SUBTYPE_MIPS_R2000 ((cpu_subtype_t) 5) +#define CPU_SUBTYPE_MIPS_R3000a ((cpu_subtype_t) 6) /* 3max */ +#define CPU_SUBTYPE_MIPS_R3000 ((cpu_subtype_t) 7) + +/* + * MC68030 subtypes. + */ + +#define CPU_SUBTYPE_NeXT ((cpu_subtype_t) 1) + /* NeXt thinks MC68030 is 6 rather than 9 */ +#define CPU_SUBTYPE_HP_340 ((cpu_subtype_t) 2) + /* 16.67 Mhz HP 300 series [HP 332,340] */ +#define CPU_SUBTYPE_HP_360 ((cpu_subtype_t) 3) + /* 25.00 Mhz HP 300 series [HP 360] */ +#define CPU_SUBTYPE_HP_370 ((cpu_subtype_t) 4) + /* 33.33 Mhz HP 300 series [HP 370] */ + +/* + * HPPA subtypes. + */ + +#define CPU_SUBTYPE_HPPA_825 ((cpu_subtype_t) 1) +#define CPU_SUBTYPE_HPPA_835 ((cpu_subtype_t) 2) +#define CPU_SUBTYPE_HPPA_840 ((cpu_subtype_t) 3) +#define CPU_SUBTYPE_HPPA_850 ((cpu_subtype_t) 4) +#define CPU_SUBTYPE_HPPA_855 ((cpu_subtype_t) 5) + +/* + * ARM subtypes. + */ + +#define CPU_SUBTYPE_ARM_A500_ARCH ((cpu_subtype_t) 1) +#define CPU_SUBTYPE_ARM_A500 ((cpu_subtype_t) 2) +#define CPU_SUBTYPE_ARM_A440 ((cpu_subtype_t) 3) +#define CPU_SUBTYPE_ARM_M4 ((cpu_subtype_t) 4) +#define CPU_SUBTYPE_ARM_A680 ((cpu_subtype_t) 5) + +/* + * MC88000 subtypes. + */ + +#define CPU_SUBTYPE_MMAX_JPC ((cpu_subtype_t) 1) +#define CPU_SUBTYPE_LUNA88K ((cpu_subtype_t) 2) + +/* + * Sparc subtypes. + */ + +#define CPU_SUBTYPE_SUN4_260 ((cpu_subtype_t) 1) +#define CPU_SUBTYPE_SUN4_110 ((cpu_subtype_t) 2) +#define CPU_SUBTYPE_SUN4_330 ((cpu_subtype_t) 3) +#define CPU_SUBTYPE_SUN4C_60 ((cpu_subtype_t) 4) +#define CPU_SUBTYPE_SUN4C_65 ((cpu_subtype_t) 5) +#define CPU_SUBTYPE_SUN4C_20 ((cpu_subtype_t) 6) +#define CPU_SUBTYPE_SUN4C_30 ((cpu_subtype_t) 7) +#define CPU_SUBTYPE_SUN4C_40 ((cpu_subtype_t) 8) +#define CPU_SUBTYPE_SUN4C_50 ((cpu_subtype_t) 9) +#define CPU_SUBTYPE_SUN4C_75 ((cpu_subtype_t) 10) + +/* + * i860 subtypes. + */ + +#define CPU_SUBTYPE_iPSC860 ((cpu_subtype_t) 1) +#define CPU_SUBTYPE_OKI860 ((cpu_subtype_t) 2) + +/* + * Alpha subtypes. + */ + +#define CPU_SUBTYPE_ALPHA_EV3 ((cpu_subtype_t) 1) +#define CPU_SUBTYPE_ALPHA_EV4 ((cpu_subtype_t) 2) +#define CPU_SUBTYPE_ALPHA_ISP ((cpu_subtype_t) 3) +#define CPU_SUBTYPE_ALPHA_21064 ((cpu_subtype_t) 4) + + +#endif /* _MACH_MACHINE_H_ */ diff --git a/include/mach/macro_help.h b/include/mach/macro_help.h new file mode 100644 index 0000000..f041e40 --- /dev/null +++ b/include/mach/macro_help.h @@ -0,0 +1,18 @@ +/* + * Mach Operating System + * Copyright (c) 1988 Carnegie-Mellon University + * All rights reserved. The CMU software License Agreement specifies + * the terms and conditions for use and redistribution. + */ + +#ifndef _MACRO_HELP_H_ +#define _MACRO_HELP_H_ 1 + +#define MACRO_BEGIN do { +#define MACRO_END } while (0) + +#define MACRO_RETURN if (1) return + +#endif /* _MACRO_HELP_H_ */ + + diff --git a/include/mach/memory_object.defs b/include/mach/memory_object.defs new file mode 100644 index 0000000..4afd67b --- /dev/null +++ b/include/mach/memory_object.defs @@ -0,0 +1,333 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988,1987 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. + */ +/* + * File: mach/memory_object.defs + * + * Abstract: + * Basic Mach external memory management interface declaration. + */ + +subsystem +#if KERNEL_USER + KernelUser +#endif /* KERNEL_USER */ +#if KERNEL_SERVER + KernelServer +#endif /* KERNEL_SERVER */ + memory_object 2200; + +#include +#include + +#ifdef MEMORY_OBJECT_IMPORTS +MEMORY_OBJECT_IMPORTS +#endif + +#if SEQNOS +serverprefix seqnos_; +serverdemux seqnos_memory_object_server; +#endif /* SEQNOS */ + +/* + * Initialize the specified memory object, providing + * a reqeust port on which control calls can be made, and + * a name port that identifies this object to callers of + * vm_regions. + * [To allow the mapping of this object to be used, the + * memory manager must call memory_object_ready or + * memory_object_change_attributes. To reject all mappings of + * this object, the memory manager may use + * memory_object_destroy.] + */ +simpleroutine memory_object_init( + memory_object : memory_object_t; +#if SEQNOS + msgseqno seqno : mach_port_seqno_t; +#endif /* SEQNOS */ + memory_control : memory_object_control_t = + MACH_MSG_TYPE_MAKE_SEND + ctype: mach_port_t; + memory_object_name : memory_object_name_t = + MACH_MSG_TYPE_MAKE_SEND + ctype: mach_port_t; + memory_object_page_size : vm_size_t); + +/* + * Indicates that the specified memory object is no longer mapped + * (or cached -- see memory_object_ready or + * memory_object_change_attributes), and that further mappings + * will cause another memory_object_init call to be made. No + * further calls will be made on the memory object by this + * kernel. + * + * [All rights to the control and name ports are included + * in this call. The memory manager should use port_deallocate + * to release them once they are no longer needed.] + */ +simpleroutine memory_object_terminate( + memory_object : memory_object_t = + MACH_MSG_TYPE_MOVE_SEND + ctype: mach_port_t +#ifdef MEMORY_OBJECT_INTRAN + intran: MEMORY_OBJECT_INTRAN +#endif +#ifdef MEMORY_OBJECT_INTRAN_PAYLOAD + intranpayload: + MEMORY_OBJECT_INTRAN_PAYLOAD +#endif +#ifdef MEMORY_OBJECT_DESTRUCTOR + destructor: MEMORY_OBJECT_DESTRUCTOR +#endif + ; +#if SEQNOS + msgseqno seqno : mach_port_seqno_t; +#endif /* SEQNOS */ + memory_control : memory_object_control_t = + MACH_MSG_TYPE_MOVE_RECEIVE + ctype: mach_port_t +#if KERNEL_USER + /* for compatibility with Mach 2.5 kernels */ + , dealloc +#endif /* KERNEL_USER */ + ; + memory_object_name : memory_object_name_t = + MACH_MSG_TYPE_MOVE_RECEIVE + ctype: mach_port_t +#if KERNEL_USER + /* for compatibility with Mach 2.5 kernels */ + , dealloc +#endif /* KERNEL_USER */ + ); + +/* + * Indicates that a copy has been made of the specified range of + * the given original memory object. The kernel will use the new + * memory object, control and name ports to refer to the new copy + * (once the memory manager has asserted its "ready" attribute). + * + * Cached pages from the original memory object at the time of + * the copy operation are handled as follows: + * Readable pages may be silently copied to the new + * memory object (with all access permissions). + * Pages not copied are locked to prevent write access. + * + * This call includes only the new memory object itself; a + * memory_object_init call will be made on the new memory + * object after the actions above are completed. + * + * The new memory object is *temporary*, meaning that the + * memory manager should not change its contents or allow + * the memory object to be mapped in another client. The + * memory manager may use the memory_object_data_unavailable + * call to indicate that the appropriate page of the original + * memory object may be used to fulfill a data request. + * + * [Reply should be memory_object_ready or + * memory_object_change_attributes on the new memory object control + * port to indicate readiness.] + */ +simpleroutine memory_object_copy( + old_memory_object : memory_object_t; +#if SEQNOS + msgseqno seqno : mach_port_seqno_t; +#endif /* SEQNOS */ + old_memory_control : memory_object_control_t = + MACH_MSG_TYPE_MAKE_SEND + ctype: mach_port_t; + offset : vm_offset_t; + length : vm_size_t; + new_memory_object : memory_object_t = + MACH_MSG_TYPE_MOVE_RECEIVE + ctype: mach_port_t +#if KERNEL_USER + /* for compatibility with Mach 2.5 kernels */ + , dealloc +#endif /* KERNEL_USER */ + ); + +/* + * Request data from this memory object. At least + * the specified data should be returned with at + * least the specified access permitted. + * + * [Reply should be memory_object_data_supply.] + */ +simpleroutine memory_object_data_request( + memory_object : memory_object_t; +#if SEQNOS + msgseqno seqno : mach_port_seqno_t; +#endif /* SEQNOS */ + memory_control : memory_object_control_t = + MACH_MSG_TYPE_MAKE_SEND + ctype: mach_port_t; + offset : vm_offset_t; + length : vm_size_t; + desired_access : vm_prot_t); + +/* + * Request that the specified portion of this + * memory object be unlocked to allow the specified + * forms of access; the kernel already has the data. + * + * [Reply should be memory_object_lock_request.] + */ +simpleroutine memory_object_data_unlock( + memory_object : memory_object_t; +#if SEQNOS + msgseqno seqno : mach_port_seqno_t; +#endif /* SEQNOS */ + memory_control : memory_object_control_t = + MACH_MSG_TYPE_MAKE_SEND + ctype: mach_port_t; + offset : vm_offset_t; + length : vm_size_t; + desired_access : vm_prot_t); + +skip; /* was: memory_object_data_write */ + +/* + * Indicate that a previous memory_object_lock_request has been + * completed. Note that this call is made on whatever + * port is specified in the memory_object_lock_request; that port + * need not be the memory object port itself. + * + * [No reply expected.] + */ +simpleroutine memory_object_lock_completed( + memory_object : memory_object_t = + polymorphic|MACH_MSG_TYPE_PORT_SEND_ONCE + ctype: mach_port_t +#ifdef MEMORY_OBJECT_INTRAN + intran: MEMORY_OBJECT_INTRAN +#endif +#ifdef MEMORY_OBJECT_INTRAN_PAYLOAD + intranpayload: MEMORY_OBJECT_INTRAN_PAYLOAD +#endif +#ifdef MEMORY_OBJECT_DESTRUCTOR + destructor: MEMORY_OBJECT_DESTRUCTOR +#endif + ; +#if SEQNOS + msgseqno seqno : mach_port_seqno_t; +#endif /* SEQNOS */ + memory_control : memory_object_control_t = + MACH_MSG_TYPE_MAKE_SEND + ctype: mach_port_t; + offset : vm_offset_t; + length : vm_size_t); + +/* + * Indicate that a previous memory_object_data_supply has been + * completed. Note that this call is made on whatever + * port is specified in the memory_object_data_supply; that port + * need not be the memory object port itself. + * + * The result parameter indicates what happened during the supply. + * If it is not KERN_SUCCESS, then error_offset identifies the + * first offset at which a problem occurred. The pagein operation + * stopped at this point. Note that the only failures reported + * by this mechanism are KERN_MEMORY_PRESENT. All other failures + * (invalid argument, error on pagein of supplied data in manager's + * address space) cause the entire operation to fail. + * + * XXX Check what actually happens in latter case! + * + * [No reply expected.] + */ +simpleroutine memory_object_supply_completed( + memory_object : memory_object_t = + polymorphic|MACH_MSG_TYPE_PORT_SEND_ONCE + ctype: mach_port_t +#ifdef MEMORY_OBJECT_INTRAN + intran: MEMORY_OBJECT_INTRAN +#endif +#ifdef MEMORY_OBJECT_INTRAN_PAYLOAD + intranpayload: MEMORY_OBJECT_INTRAN_PAYLOAD +#endif +#ifdef MEMORY_OBJECT_DESTRUCTOR + destructor: MEMORY_OBJECT_DESTRUCTOR +#endif + ; +#if SEQNOS + msgseqno seqno : mach_port_seqno_t; +#endif /* SEQNOS */ + memory_control : memory_object_control_t = + MACH_MSG_TYPE_MAKE_SEND + ctype: mach_port_t; + offset : vm_offset_t; + length : vm_size_t; + result : kern_return_t; + error_offset : vm_offset_t); + +/* + * Return data to manager. This call indicates whether the + * returned data is dirty and whether the kernel kept a copy. + * Precious data remains precious if the kernel keeps a copy. + * The indication that the kernel kept a copy is only a hint if + * the data is not precious; the cleaned copy may be discarded + * without further notifying the manager. + * + * [Reply should be vm_deallocate to release the data.] + */ +simpleroutine memory_object_data_return( + memory_object : memory_object_t; +#if SEQNOS + msgseqno seqno : mach_port_seqno_t; +#endif /* SEQNOS */ + memory_control : memory_object_control_t = + MACH_MSG_TYPE_MAKE_SEND + ctype: mach_port_t; + offset : vm_offset_t; + data : pointer_t; + dirty : boolean_t; + kernel_copy : boolean_t); + +/* + * XXX Warning: This routine does NOT contain a memory_object_control_t + * XXX because the memory_object_change_attributes call may cause + * XXX memory object termination (by uncaching the object). This would + * XXX yield an invalid port. + */ + +simpleroutine memory_object_change_completed( + memory_object : memory_object_t = + polymorphic|MACH_MSG_TYPE_PORT_SEND_ONCE + ctype: mach_port_t +#ifdef MEMORY_OBJECT_INTRAN + intran: MEMORY_OBJECT_INTRAN +#endif +#ifdef MEMORY_OBJECT_INTRAN_PAYLOAD + intranpayload: MEMORY_OBJECT_INTRAN_PAYLOAD +#endif +#ifdef MEMORY_OBJECT_DESTRUCTOR + destructor: MEMORY_OBJECT_DESTRUCTOR +#endif + ; +#if SEQNOS + msgseqno seqno : mach_port_seqno_t; +#endif /* SEQNOS */ + may_cache : boolean_t; + copy_strategy : memory_object_copy_strategy_t); diff --git a/include/mach/memory_object.h b/include/mach/memory_object.h new file mode 100644 index 0000000..7e0c374 --- /dev/null +++ b/include/mach/memory_object.h @@ -0,0 +1,90 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988 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. + */ +/* + * File: memory_object.h + * Author: Michael Wayne Young + * + * External memory management interface definition. + */ + +#ifndef _MACH_MEMORY_OBJECT_H_ +#define _MACH_MEMORY_OBJECT_H_ + +/* + * User-visible types used in the external memory + * management interface: + */ + +#include + +#ifdef MACH_KERNEL +#include +typedef ipc_port_t memory_object_t; +#else +typedef mach_port_t memory_object_t; +#endif + /* Represents a memory object ... */ + /* Used by user programs to specify */ + /* the object to map; used by the */ + /* kernel to retrieve or store data */ + +typedef memory_object_t *memory_object_array_t; + +typedef mach_port_t memory_object_control_t; + /* Provided to a memory manager; ... */ + /* used to control a memory object */ + +typedef mach_port_t memory_object_name_t; + /* Used to describe the memory ... */ + /* object in vm_regions() calls */ + +typedef int memory_object_copy_strategy_t; + /* How memory manager handles copy: */ +#define MEMORY_OBJECT_COPY_NONE 0 + /* ... No special support */ +#define MEMORY_OBJECT_COPY_CALL 1 + /* ... Make call on memory manager */ +#define MEMORY_OBJECT_COPY_DELAY 2 + /* ... Memory manager doesn't ... */ + /* change data externally. */ +#define MEMORY_OBJECT_COPY_TEMPORARY 3 + /* ... Memory manager doesn't ... */ + /* change data externally, and */ + /* doesn't need to see changes. */ + +typedef int memory_object_return_t; + /* Which pages to return to manager + this time (lock_request) */ +#define MEMORY_OBJECT_RETURN_NONE 0 + /* ... don't return any. */ +#define MEMORY_OBJECT_RETURN_DIRTY 1 + /* ... only dirty pages. */ +#define MEMORY_OBJECT_RETURN_ALL 2 + /* ... dirty and precious pages. */ + +#define MEMORY_OBJECT_NULL MACH_PORT_NULL + +#endif /* _MACH_MEMORY_OBJECT_H_ */ diff --git a/include/mach/memory_object_default.defs b/include/mach/memory_object_default.defs new file mode 100644 index 0000000..e62f14d --- /dev/null +++ b/include/mach/memory_object_default.defs @@ -0,0 +1,118 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988,1987 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. + */ +/* + * File: mach/memory_object_default.defs + * + * Abstract: + * Mach external memory management interface declaration; subset + * that is applicable to managers of kernel-created memory objects. + */ + +subsystem +#if KERNEL_USER + KernelUser +#endif /* KERNEL_USER */ + memory_object_default 2250; + +#include +#include + +#ifdef MEMORY_OBJECT_IMPORTS +MEMORY_OBJECT_IMPORTS +#endif + +#if SEQNOS +serverprefix seqnos_; +serverdemux seqnos_memory_object_default_server; +#endif /* SEQNOS */ + +/* + * Pass on responsibility for the new kernel-created memory + * object. The port on which this request is that port + * (possibly a memory object itself) registered as the "default + * pager". Other arguments are as described for memory_object_init. + * [No reply required.] + */ +simpleroutine memory_object_create( + old_memory_object : memory_object_t = + MACH_MSG_TYPE_MOVE_SEND + ctype: mach_port_t; +#if SEQNOS + msgseqno seqno : mach_port_seqno_t; +#endif /* SEQNOS */ + new_memory_object : memory_object_t = + MACH_MSG_TYPE_MOVE_RECEIVE + ctype: mach_port_t +#if KERNEL_USER + /* for compatibility with Mach 2.5 kernels */ + , dealloc +#endif /* KERNEL_USER */ + ; + new_object_size : vm_size_t; + new_control_port : memory_object_control_t = + MACH_MSG_TYPE_MAKE_SEND + ctype: mach_port_t; + new_name : memory_object_name_t = + MACH_MSG_TYPE_MAKE_SEND + ctype: mach_port_t; + new_page_size : vm_size_t); + +/* + * Provide initial data contents for this region of + * the memory object. If data has already been written + * to the object, this value must be discarded; otherwise, + * this call acts identically to memory_object_data_return. + */ +simpleroutine memory_object_data_initialize( + memory_object : memory_object_t; +#if SEQNOS + msgseqno seqno : mach_port_seqno_t; +#endif /* SEQNOS */ + memory_control_port : memory_object_control_t = + MACH_MSG_TYPE_MAKE_SEND + ctype: mach_port_t; + offset : vm_offset_t; + data : pointer_t); + +#if 0 +/* + * Indicate that the specified range of data in this memory object + * will not be requested again until it is reinitialized with + * memory_object_data_return or memory_object_data_initialize. + */ +simpleroutine memory_object_data_terminate( + memory_object : memory_object_t; +#if SEQNOS + msgseqno seqno : mach_port_seqno_t; +#endif /* SEQNOS */ + memory_control_port : memory_object_control_t = + MACH_MSG_TYPE_MAKE_SEND + ctype: mach_port_t; + offset : vm_offset_t; + size : vm_size_t); +#else /* 0 */ +skip; /* memory_object_data_terminate */ +#endif /* 0 */ diff --git a/include/mach/message.h b/include/mach/message.h new file mode 100644 index 0000000..9790ef9 --- /dev/null +++ b/include/mach/message.h @@ -0,0 +1,540 @@ +/* + * Mach Operating System + * Copyright (c) 1992-1987 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. + */ +/* + * File: mach/message.h + * + * Mach IPC message and primitive function definitions. + */ + +#ifndef _MACH_MESSAGE_H_ +#define _MACH_MESSAGE_H_ + +#include +#include + + +/* + * The timeout mechanism uses mach_msg_timeout_t values, + * passed by value. The timeout units are milliseconds. + * It is controlled with the MACH_SEND_TIMEOUT + * and MACH_RCV_TIMEOUT options. + */ + +typedef natural_t mach_msg_timeout_t; + +/* + * The value to be used when there is no timeout. + * (No MACH_SEND_TIMEOUT/MACH_RCV_TIMEOUT option.) + */ + +#define MACH_MSG_TIMEOUT_NONE ((mach_msg_timeout_t) 0) + +/* + * The kernel uses MACH_MSGH_BITS_COMPLEX as a hint. It it isn't on, it + * assumes the body of the message doesn't contain port rights or OOL + * data. The field is set in received messages. A user task must + * use caution in interpreting the body of a message if the bit isn't + * on, because the mach_msg_type's in the body might "lie" about the + * contents. If the bit isn't on, but the mach_msg_types + * in the body specify rights or OOL data, the behaviour is undefined. + * (Ie, an error may or may not be produced.) + * + * The value of MACH_MSGH_BITS_REMOTE determines the interpretation + * of the msgh_remote_port field. It is handled like a msgt_name. + * + * The value of MACH_MSGH_BITS_LOCAL determines the interpretation + * of the msgh_local_port field. It is handled like a msgt_name. + * + * MACH_MSGH_BITS() combines two MACH_MSG_TYPE_* values, for the remote + * and local fields, into a single value suitable for msgh_bits. + * + * MACH_MSGH_BITS_COMPLEX_PORTS, MACH_MSGH_BITS_COMPLEX_DATA, and + * MACH_MSGH_BITS_CIRCULAR should be zero; they are used internally. + * + * The unused bits should be zero. + */ + +#define MACH_MSGH_BITS_ZERO 0x00000000 +#define MACH_MSGH_BITS_REMOTE_MASK 0x000000ff +#define MACH_MSGH_BITS_LOCAL_MASK 0x0000ff00 +#define MACH_MSGH_BITS_COMPLEX 0x80000000U +#define MACH_MSGH_BITS_CIRCULAR 0x40000000 /* internal use only */ +#define MACH_MSGH_BITS_COMPLEX_PORTS 0x20000000 /* internal use only */ +#define MACH_MSGH_BITS_COMPLEX_DATA 0x10000000 /* internal use only */ +#define MACH_MSGH_BITS_MIGRATED 0x08000000 /* internal use only */ +#define MACH_MSGH_BITS_UNUSED 0x07ff0000 + +#define MACH_MSGH_BITS_PORTS_MASK \ + (MACH_MSGH_BITS_REMOTE_MASK|MACH_MSGH_BITS_LOCAL_MASK) + +#define MACH_MSGH_BITS(remote, local) \ + ((remote) | ((local) << 8)) +#define MACH_MSGH_BITS_REMOTE(bits) \ + ((bits) & MACH_MSGH_BITS_REMOTE_MASK) +#define MACH_MSGH_BITS_LOCAL(bits) \ + (((bits) & MACH_MSGH_BITS_LOCAL_MASK) >> 8) +#define MACH_MSGH_BITS_PORTS(bits) \ + ((bits) & MACH_MSGH_BITS_PORTS_MASK) +#define MACH_MSGH_BITS_OTHER(bits) \ + ((bits) &~ MACH_MSGH_BITS_PORTS_MASK) + +/* + * Every message starts with a message header. + * Following the message header are zero or more pairs of + * type descriptors (mach_msg_type_t/mach_msg_type_long_t) and + * data values. The size of the message must be specified in bytes, + * and includes the message header, type descriptors, inline + * data, and inline pointer for out-of-line data. + * + * The msgh_remote_port field specifies the destination of the message. + * It must specify a valid send or send-once right for a port. + * + * The msgh_local_port field specifies a "reply port". Normally, + * This field carries a send-once right that the receiver will use + * to reply to the message. It may carry the values MACH_PORT_NULL, + * MACH_PORT_DEAD, a send-once right, or a send right. + * + * The msgh_seqno field carries a sequence number associated with the + * received-from port. A port's sequence number is incremented every + * time a message is received from it. In sent messages, the field's + * value is ignored. + * + * The msgh_id field is uninterpreted by the message primitives. + * It normally carries information specifying the format + * or meaning of the message. + */ + +typedef unsigned int mach_msg_bits_t; +typedef unsigned int mach_msg_size_t; +typedef natural_t mach_msg_seqno_t; +typedef integer_t mach_msg_id_t; + +/* full header structure, may have different size in user/kernel spaces */ +typedef struct mach_msg_header { + mach_msg_bits_t msgh_bits; + mach_msg_size_t msgh_size; + union { + mach_port_t msgh_remote_port; + /* + * Ensure msgh_remote_port is wide enough to hold a kernel pointer + * to avoid message resizing for the 64 bits case. This field should + * not be used since it is here just for padding purposes. + */ + rpc_uintptr_t msgh_remote_port_do_not_use; + }; + union { + mach_port_t msgh_local_port; + rpc_uintptr_t msgh_protected_payload; + }; + mach_port_seqno_t msgh_seqno; + mach_msg_id_t msgh_id; +} mach_msg_header_t; + +#ifdef KERNEL +/* user-side header format, needed in the kernel */ +typedef struct { + mach_msg_bits_t msgh_bits; + mach_msg_size_t msgh_size; + union { + mach_port_name_t msgh_remote_port; + rpc_uintptr_t msgh_remote_port_do_not_use; + }; + union { + mach_port_name_t msgh_local_port; + rpc_uintptr_t msgh_protected_payload; + }; + mach_port_seqno_t msgh_seqno; + mach_msg_id_t msgh_id; +} mach_msg_user_header_t; +#else +typedef mach_msg_header_t mach_msg_user_header_t; +#endif + +/* + * There is no fixed upper bound to the size of Mach messages. + */ + +#define MACH_MSG_SIZE_MAX ((mach_msg_size_t) ~0) + +/* + * Compatibility definitions, for code written + * when there was a msgh_kind instead of msgh_seqno. + */ + +#define MACH_MSGH_KIND_NORMAL 0x00000000 +#if 0 +/* code using this is likely to break, so better not to have it defined */ +#define MACH_MSGH_KIND_NOTIFICATION 0x00000001 +#endif +#define msgh_kind msgh_seqno +#define mach_msg_kind_t mach_port_seqno_t + +/* + * The msgt_number field specifies the number of data elements. + * The msgt_size field specifies the size of each data element, in bits. + * The msgt_name field specifies the type of each data element. + * If msgt_inline is TRUE, the data follows the type descriptor + * in the body of the message. If msgt_inline is FALSE, then a pointer + * to the data should follow the type descriptor, and the data is + * sent out-of-line. In this case, if msgt_deallocate is TRUE, + * then the out-of-line data is moved (instead of copied) into the message. + * If msgt_longform is TRUE, then the type descriptor is actually + * a mach_msg_type_long_t. + * + * The actual amount of inline data following the descriptor must + * a multiple of the word size. For out-of-line data, this is a + * pointer. For inline data, the supplied data size (calculated + * from msgt_number/msgt_size) is rounded up. This guarantees + * that type descriptors always fall on word boundaries. + * + * For port rights, msgt_size must be 8*sizeof(mach_port_t). + * If the data is inline, msgt_deallocate should be FALSE. + * The msgt_unused bit should be zero. + * The msgt_name, msgt_size, msgt_number fields in + * a mach_msg_type_long_t should be zero. + */ + +typedef unsigned int mach_msg_type_name_t; +typedef unsigned int mach_msg_type_size_t; +typedef natural_t mach_msg_type_number_t; + +/** + * Structure used for inlined port rights in messages. + * + * We use this to avoid having to perform message resizing in the kernel + * since userspace port rights might be smaller than kernel ports in 64 bit + * architectures. + */ +typedef struct { + union { + mach_port_name_t name; +#ifdef KERNEL + mach_port_t kernel_port; +#else + uintptr_t kernel_port_do_not_use; +#endif /* KERNEL */ + }; +} mach_port_name_inlined_t; + +typedef struct { +#ifdef __x86_64__ + /* + * For 64 bits, this struct is 8 bytes long so we + * can pack the same amount of information as mach_msg_type_long_t. + * Note that for 64 bit userland, msgt_size only needs to be 8 bits long + * but for kernel compatibility with 32 bit userland we allow it to be + * 16 bits long. + * + * Effectively, we don't need mach_msg_type_long_t but we are keeping it + * for a while to make the code similar between 32 and 64 bits. + * + * We also keep the msgt_longform bit around simply because it makes it + * very easy to convert messages from a 32 bit userland into a 64 bit + * kernel. Otherwise, we would have to replicate some of the MiG logic + * internally in the kernel. + */ + unsigned int msgt_name : 8, + msgt_size : 16, + msgt_unused : 5, + msgt_inline : 1, + msgt_longform : 1, + msgt_deallocate : 1; + mach_msg_type_number_t msgt_number; +#else + unsigned int msgt_name : 8, + msgt_size : 8, + msgt_number : 12, + msgt_inline : 1, + msgt_longform : 1, + msgt_deallocate : 1, + msgt_unused : 1; +#endif +} __attribute__ ((aligned (__alignof__ (uintptr_t)))) mach_msg_type_t; + +typedef struct { +#ifdef __x86_64__ + union { + /* On x86_64 this is equivalent to mach_msg_type_t so use + * union to overlay with the old field names. */ + mach_msg_type_t msgtl_header; + struct { + unsigned int msgtl_name : 8, + msgtl_size : 16, + msgtl_unused : 5, + msgtl_inline : 1, + msgtl_longform : 1, + msgtl_deallocate : 1; + mach_msg_type_number_t msgtl_number; + }; + }; +#else + mach_msg_type_t msgtl_header; + unsigned short msgtl_name; + unsigned short msgtl_size; + natural_t msgtl_number; +#endif +} __attribute__ ((aligned (__alignof__ (uintptr_t)))) mach_msg_type_long_t; + +#ifdef __x86_64__ +#ifdef __cplusplus +#if __cplusplus >= 201103L +static_assert (sizeof (mach_msg_type_t) == sizeof (mach_msg_type_long_t), + "mach_msg_type_t and mach_msg_type_long_t need to have the same size."); +#endif +#else +_Static_assert (sizeof (mach_msg_type_t) == sizeof (mach_msg_type_long_t), + "mach_msg_type_t and mach_msg_type_long_t need to have the same size."); +#endif +#endif + +/* + * Known values for the msgt_name field. + * + * The only types known to the Mach kernel are + * the port types, and those types used in the + * kernel RPC interface. + */ + +#define MACH_MSG_TYPE_UNSTRUCTURED 0 +#define MACH_MSG_TYPE_BIT 0 +#define MACH_MSG_TYPE_BOOLEAN 0 +#define MACH_MSG_TYPE_INTEGER_16 1 +#define MACH_MSG_TYPE_INTEGER_32 2 +#define MACH_MSG_TYPE_CHAR 8 +#define MACH_MSG_TYPE_BYTE 9 +#define MACH_MSG_TYPE_INTEGER_8 9 +#define MACH_MSG_TYPE_REAL 10 +#define MACH_MSG_TYPE_INTEGER_64 11 +#define MACH_MSG_TYPE_STRING 12 +#define MACH_MSG_TYPE_STRING_C 12 + +/* + * Values used when sending a port right. + */ + +#define MACH_MSG_TYPE_MOVE_RECEIVE 16 /* Must hold receive rights */ +#define MACH_MSG_TYPE_MOVE_SEND 17 /* Must hold send rights */ +#define MACH_MSG_TYPE_MOVE_SEND_ONCE 18 /* Must hold sendonce rights */ +#define MACH_MSG_TYPE_COPY_SEND 19 /* Must hold send rights */ +#define MACH_MSG_TYPE_MAKE_SEND 20 /* Must hold receive rights */ +#define MACH_MSG_TYPE_MAKE_SEND_ONCE 21 /* Must hold receive rights */ + +/* + * Values received/carried in messages. Tells the receiver what + * sort of port right he now has. + * + * MACH_MSG_TYPE_PORT_NAME is used to transfer a port name + * which should remain uninterpreted by the kernel. (Port rights + * are not transferred, just the port name.) + */ + +#define MACH_MSG_TYPE_PORT_NAME 15 +#define MACH_MSG_TYPE_PORT_RECEIVE MACH_MSG_TYPE_MOVE_RECEIVE +#define MACH_MSG_TYPE_PORT_SEND MACH_MSG_TYPE_MOVE_SEND +#define MACH_MSG_TYPE_PORT_SEND_ONCE MACH_MSG_TYPE_MOVE_SEND_ONCE + +#define MACH_MSG_TYPE_PROTECTED_PAYLOAD 23 + +#define MACH_MSG_TYPE_LAST 23 /* Last assigned */ + +/* + * A dummy value. Mostly used to indicate that the actual value + * will be filled in later, dynamically. + */ + +#define MACH_MSG_TYPE_POLYMORPHIC ((mach_msg_type_name_t) -1) + +/* + * Is a given item a port type? + */ + +#define MACH_MSG_TYPE_PORT_ANY(x) \ + (((x) >= MACH_MSG_TYPE_MOVE_RECEIVE) && \ + ((x) <= MACH_MSG_TYPE_MAKE_SEND_ONCE)) + +#define MACH_MSG_TYPE_PORT_ANY_SEND(x) \ + (((x) >= MACH_MSG_TYPE_MOVE_SEND) && \ + ((x) <= MACH_MSG_TYPE_MAKE_SEND_ONCE)) + +#define MACH_MSG_TYPE_PORT_ANY_RIGHT(x) \ + (((x) >= MACH_MSG_TYPE_MOVE_RECEIVE) && \ + ((x) <= MACH_MSG_TYPE_MOVE_SEND_ONCE)) + +typedef integer_t mach_msg_option_t; + +#define MACH_MSG_OPTION_NONE 0x00000000 + +#define MACH_SEND_MSG 0x00000001 +#define MACH_RCV_MSG 0x00000002 + +#define MACH_SEND_TIMEOUT 0x00000010 +#define MACH_SEND_NOTIFY 0x00000020 +#define MACH_SEND_INTERRUPT 0x00000040 /* libmach implements */ +#define MACH_SEND_CANCEL 0x00000080 +#define MACH_RCV_TIMEOUT 0x00000100 +#define MACH_RCV_NOTIFY 0x00000200 +#define MACH_RCV_INTERRUPT 0x00000400 /* libmach implements */ +#define MACH_RCV_LARGE 0x00000800 + +#define MACH_SEND_ALWAYS 0x00010000 /* internal use only */ + +#ifdef __x86_64__ +#if defined(KERNEL) && defined(USER32) +#define MACH_MSG_USER_ALIGNMENT 4 +#else +#define MACH_MSG_USER_ALIGNMENT 8 +#endif +#else +#define MACH_MSG_USER_ALIGNMENT 4 +#endif + +#ifdef KERNEL +/* This is the alignment of msg descriptors and the actual data + * for both in kernel messages and user land messages. + * + * We have two types of alignment because for specific configurations + * (in particular a 64 bit kernel with 32 bit userland) we transform + * 4-byte aligned user messages into 8-byte aligned messages (and vice-versa) + * so that kernel messages are correctly aligned. + */ +#define MACH_MSG_KERNEL_ALIGNMENT sizeof(uintptr_t) + +#define mach_msg_align(x, alignment) \ + ( ( ((vm_offset_t)(x)) + ((alignment)-1) ) & ~((alignment)-1) ) +#define mach_msg_user_align(x) mach_msg_align(x, MACH_MSG_USER_ALIGNMENT) +#define mach_msg_kernel_align(x) mach_msg_align(x, MACH_MSG_KERNEL_ALIGNMENT) +#define mach_msg_user_is_misaligned(x) ((x) & ((MACH_MSG_USER_ALIGNMENT)-1)) +#define mach_msg_kernel_is_misaligned(x) ((x) & ((MACH_MSG_KERNEL_ALIGNMENT)-1)) +#endif /* KERNEL */ + +/* + * Much code assumes that mach_msg_return_t == kern_return_t. + * This definition is useful for descriptive purposes. + * + * See for the format of error codes. + * IPC errors are system 4. Send errors are subsystem 0; + * receive errors are subsystem 1. The code field is always non-zero. + * The high bits of the code field communicate extra information + * for some error codes. MACH_MSG_MASK masks off these special bits. + */ + +typedef kern_return_t mach_msg_return_t; + +#define MACH_MSG_SUCCESS 0x00000000 + +#define MACH_MSG_MASK 0x00003c00 + /* All special error code bits defined below. */ +#define MACH_MSG_IPC_SPACE 0x00002000 + /* No room in IPC name space for another capability name. */ +#define MACH_MSG_VM_SPACE 0x00001000 + /* No room in VM address space for out-of-line memory. */ +#define MACH_MSG_IPC_KERNEL 0x00000800 + /* Kernel resource shortage handling an IPC capability. */ +#define MACH_MSG_VM_KERNEL 0x00000400 + /* Kernel resource shortage handling out-of-line memory. */ + +#define MACH_SEND_IN_PROGRESS 0x10000001 + /* Thread is waiting to send. (Internal use only.) */ +#define MACH_SEND_INVALID_DATA 0x10000002 + /* Bogus in-line data. */ +#define MACH_SEND_INVALID_DEST 0x10000003 + /* Bogus destination port. */ +#define MACH_SEND_TIMED_OUT 0x10000004 + /* Message not sent before timeout expired. */ +#define MACH_SEND_WILL_NOTIFY 0x10000005 + /* Msg-accepted notification will be generated. */ +#define MACH_SEND_NOTIFY_IN_PROGRESS 0x10000006 + /* Msg-accepted notification already pending. */ +#define MACH_SEND_INTERRUPTED 0x10000007 + /* Software interrupt. */ +#define MACH_SEND_MSG_TOO_SMALL 0x10000008 + /* Data doesn't contain a complete message. */ +#define MACH_SEND_INVALID_REPLY 0x10000009 + /* Bogus reply port. */ +#define MACH_SEND_INVALID_RIGHT 0x1000000a + /* Bogus port rights in the message body. */ +#define MACH_SEND_INVALID_NOTIFY 0x1000000b + /* Bogus notify port argument. */ +#define MACH_SEND_INVALID_MEMORY 0x1000000c + /* Invalid out-of-line memory pointer. */ +#define MACH_SEND_NO_BUFFER 0x1000000d + /* No message buffer is available. */ +#define MACH_SEND_NO_NOTIFY 0x1000000e + /* Resource shortage; can't request msg-accepted notif. */ +#define MACH_SEND_INVALID_TYPE 0x1000000f + /* Invalid msg-type specification. */ +#define MACH_SEND_INVALID_HEADER 0x10000010 + /* A field in the header had a bad value. */ + +#define MACH_RCV_IN_PROGRESS 0x10004001 + /* Thread is waiting for receive. (Internal use only.) */ +#define MACH_RCV_INVALID_NAME 0x10004002 + /* Bogus name for receive port/port-set. */ +#define MACH_RCV_TIMED_OUT 0x10004003 + /* Didn't get a message within the timeout value. */ +#define MACH_RCV_TOO_LARGE 0x10004004 + /* Message buffer is not large enough for inline data. */ +#define MACH_RCV_INTERRUPTED 0x10004005 + /* Software interrupt. */ +#define MACH_RCV_PORT_CHANGED 0x10004006 + /* Port moved into a set during the receive. */ +#define MACH_RCV_INVALID_NOTIFY 0x10004007 + /* Bogus notify port argument. */ +#define MACH_RCV_INVALID_DATA 0x10004008 + /* Bogus message buffer for inline data. */ +#define MACH_RCV_PORT_DIED 0x10004009 + /* Port/set was sent away/died during receive. */ +#define MACH_RCV_IN_SET 0x1000400a + /* Port is a member of a port set. */ +#define MACH_RCV_HEADER_ERROR 0x1000400b + /* Error receiving message header. See special bits. */ +#define MACH_RCV_BODY_ERROR 0x1000400c + /* Error receiving message body. See special bits. */ + +extern mach_msg_return_t +mach_msg_trap + (mach_msg_user_header_t *msg, + mach_msg_option_t option, + mach_msg_size_t send_size, + mach_msg_size_t rcv_size, + mach_port_name_t rcv_name, + mach_msg_timeout_t timeout, + mach_port_name_t notify); + +extern mach_msg_return_t +mach_msg + (mach_msg_header_t *msg, + mach_msg_option_t option, + mach_msg_size_t send_size, + mach_msg_size_t rcv_size, + mach_port_name_t rcv_name, + mach_msg_timeout_t timeout, + mach_port_name_t notify); + +extern __typeof (mach_msg) __mach_msg; +extern __typeof (mach_msg_trap) __mach_msg_trap; + +#endif /* _MACH_MESSAGE_H_ */ diff --git a/include/mach/mig_errors.h b/include/mach/mig_errors.h new file mode 100644 index 0000000..389ce77 --- /dev/null +++ b/include/mach/mig_errors.h @@ -0,0 +1,89 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988,1987 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. + */ +/* + * Mach Interface Generator errors + * + */ + +#ifndef _MACH_MIG_ERRORS_H_ +#define _MACH_MIG_ERRORS_H_ + +#include +#include + +/* + * These error codes should be specified as system 4, subsytem 2. + * But alas backwards compatibility makes that impossible. + * The problem is old clients of new servers (eg, the kernel) + * which get strange large error codes when there is a Mig problem + * in the server. Unfortunately, the IPC system doesn't have + * the knowledge to convert the codes in this situation. + */ + +#define MIG_TYPE_ERROR -300 /* client type check failure */ +#define MIG_REPLY_MISMATCH -301 /* wrong reply message ID */ +#define MIG_REMOTE_ERROR -302 /* server detected error */ +#define MIG_BAD_ID -303 /* bad request message ID */ +#define MIG_BAD_ARGUMENTS -304 /* server type check failure */ +#define MIG_NO_REPLY -305 /* no reply should be sent */ +#define MIG_EXCEPTION -306 /* server raised exception */ +#define MIG_ARRAY_TOO_LARGE -307 /* array not large enough */ +#define MIG_SERVER_DIED -308 /* server died */ +#define MIG_DESTROY_REQUEST -309 /* destroy request with no reply */ + +typedef struct { + mach_msg_header_t Head; + mach_msg_type_t RetCodeType; + kern_return_t RetCode; +} mig_reply_header_t; + +typedef struct mig_symtab { + char *ms_routine_name; + int ms_routine_number; +#if defined(__STDC__) || defined(c_plus_plus) || defined(hc) + void +#else + int +#endif + (*ms_routine)(void); +} mig_symtab_t; + +/* + * Definition for server stub routines. These routines + * unpack the request message, call the server procedure, + * and pack the reply message. + */ +#if defined(__STDC__) || defined(c_plus_plus) +typedef void (*mig_routine_t)(mach_msg_header_t *, mach_msg_header_t *); +#else +#if defined(hc) +typedef void (*mig_routine_t)(); +#else +typedef int (*mig_routine_t)(); /* PCC cannot handle void (*)() */ +#endif +#endif + +#endif /* _MACH_MIG_ERRORS_H_ */ diff --git a/include/mach/mig_support.h b/include/mach/mig_support.h new file mode 100644 index 0000000..ed871c0 --- /dev/null +++ b/include/mach/mig_support.h @@ -0,0 +1,57 @@ +/* + * Mach Operating System + * Copyright (c) 1992 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. + */ +/* + * Abstract: + * Header file for support routines called by MiG generated interfaces. + * + */ + +#ifndef _MACH_MIG_SUPPORT_H_ +#define _MACH_MIG_SUPPORT_H_ + +#include + +#include +#include + +extern void mig_init(void *_first); + +extern void mig_allocate(vm_address_t *_addr_p, vm_size_t _size); + +extern void mig_deallocate(vm_address_t _addr, vm_size_t _size); + +extern void mig_dealloc_reply_port(mach_port_t); + +extern void mig_put_reply_port(mach_port_t); + +extern mach_port_name_t mig_get_reply_port(void); + +extern void mig_reply_setup(const mach_msg_header_t *_request, + mach_msg_header_t *reply); + +extern vm_size_t mig_strncpy(char *_dest, const char *_src, vm_size_t _len); + +#endif /* not defined(_MACH_MIG_SUPPORT_H_) */ diff --git a/include/mach/notify.defs b/include/mach/notify.defs new file mode 100644 index 0000000..6ba4cde --- /dev/null +++ b/include/mach/notify.defs @@ -0,0 +1,112 @@ +/* + * 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. + */ + +subsystem notify 64; + +#include + +#ifdef NOTIFY_IMPORTS +NOTIFY_IMPORTS +#endif + +#if SEQNOS +serverprefix do_seqnos_; +serverdemux seqnos_notify_server; +#else +serverprefix do_; +serverdemux notify_server; +#endif + +type notify_port_t = MACH_MSG_TYPE_MOVE_SEND_ONCE + ctype: mach_port_t +#ifdef NOTIFY_INTRAN + intran: NOTIFY_INTRAN +#endif +#ifdef NOTIFY_INTRAN_PAYLOAD + intranpayload: NOTIFY_INTRAN_PAYLOAD +#endif +#ifdef NOTIFY_OUTTRAN + outtran: NOTIFY_OUTTRAN +#endif +#ifdef NOTIFY_DESTRUCTOR + destructor: NOTIFY_DESTRUCTOR +#endif +; + +/* MACH_NOTIFY_FIRST: 0100 */ +skip; + +/* MACH_NOTIFY_PORT_DELETED: 0101 */ +simpleroutine mach_notify_port_deleted( + notify : notify_port_t; +#if SEQNOS + msgseqno seqno : mach_port_seqno_t; +#endif + name : mach_port_name_t); + +/* MACH_NOTIFY_MSG_ACCEPTED: 0102 */ +simpleroutine mach_notify_msg_accepted( + notify : notify_port_t; +#if SEQNOS + msgseqno seqno : mach_port_seqno_t; +#endif + name : mach_port_name_t); + +skip; /* was NOTIFY_OWNERSHIP_RIGHTS: 0103 */ + +skip; /* was NOTIFY_RECEIVE_RIGHTS: 0104 */ + +/* MACH_NOTIFY_PORT_DESTROYED: 0105 */ +simpleroutine mach_notify_port_destroyed( + notify : notify_port_t; +#if SEQNOS + msgseqno seqno : mach_port_seqno_t; +#endif + rights : mach_port_receive_t); + +/* MACH_NOTIFY_NO_SENDERS: 0106 */ +simpleroutine mach_notify_no_senders( + notify : notify_port_t; +#if SEQNOS + msgseqno seqno : mach_port_seqno_t; +#endif + mscount : mach_port_mscount_t); + +/* MACH_NOTIFY_SEND_ONCE: 0107 */ +simpleroutine mach_notify_send_once( + notify : notify_port_t +#if SEQNOS +; msgseqno seqno : mach_port_seqno_t +#endif + ); + +/* MACH_NOTIFY_DEAD_NAME: 0110 */ +simpleroutine mach_notify_dead_name( + notify : notify_port_t; +#if SEQNOS + msgseqno seqno : mach_port_seqno_t; +#endif + name : mach_port_name_t); diff --git a/include/mach/notify.h b/include/mach/notify.h new file mode 100644 index 0000000..14bcd6f --- /dev/null +++ b/include/mach/notify.h @@ -0,0 +1,92 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988,1987 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. + */ +/* + * File: mach/notify.h + * + * Kernel notification message definitions. + */ + +#ifndef _MACH_NOTIFY_H_ +#define _MACH_NOTIFY_H_ + +#include +#include + +/* + * An alternative specification of the notification interface + * may be found in mach/notify.defs. + */ + +#define MACH_NOTIFY_FIRST 0100 +#define MACH_NOTIFY_PORT_DELETED (MACH_NOTIFY_FIRST + 001 ) + /* A send or send-once right was deleted. */ +#define MACH_NOTIFY_MSG_ACCEPTED (MACH_NOTIFY_FIRST + 002) + /* A MACH_SEND_NOTIFY msg was accepted */ +#define MACH_NOTIFY_PORT_DESTROYED (MACH_NOTIFY_FIRST + 005) + /* A receive right was (would have been) deallocated */ +#define MACH_NOTIFY_NO_SENDERS (MACH_NOTIFY_FIRST + 006) + /* Receive right has no extant send rights */ +#define MACH_NOTIFY_SEND_ONCE (MACH_NOTIFY_FIRST + 007) + /* An extant send-once right died */ +#define MACH_NOTIFY_DEAD_NAME (MACH_NOTIFY_FIRST + 010) + /* Send or send-once right died, leaving a dead-name */ +#define MACH_NOTIFY_LAST (MACH_NOTIFY_FIRST + 015) + +typedef struct { + mach_msg_header_t not_header; + mach_msg_type_t not_type; /* MACH_MSG_TYPE_PORT_NAME */ + mach_port_name_t not_port; +} mach_port_deleted_notification_t; + +typedef struct { + mach_msg_header_t not_header; + mach_msg_type_t not_type; /* MACH_MSG_TYPE_PORT_NAME */ + mach_port_name_t not_port; +} mach_msg_accepted_notification_t; + +typedef struct { + mach_msg_header_t not_header; + mach_msg_type_t not_type; /* MACH_MSG_TYPE_PORT_RECEIVE */ + mach_port_t not_port; +} mach_port_destroyed_notification_t; + +typedef struct { + mach_msg_header_t not_header; + mach_msg_type_t not_type; /* MACH_MSG_TYPE_INTEGER_32 */ + unsigned int not_count; +} mach_no_senders_notification_t; + +typedef struct { + mach_msg_header_t not_header; +} mach_send_once_notification_t; + +typedef struct { + mach_msg_header_t not_header; + mach_msg_type_t not_type; /* MACH_MSG_TYPE_PORT_NAME */ + mach_port_name_t not_port; +} mach_dead_name_notification_t; + +#endif /* _MACH_NOTIFY_H_ */ diff --git a/include/mach/pc_sample.h b/include/mach/pc_sample.h new file mode 100644 index 0000000..2d56b34 --- /dev/null +++ b/include/mach/pc_sample.h @@ -0,0 +1,66 @@ +/* + * Mach Operating System + * Copyright (c) 1993,1992 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. + */ + +#ifndef _MACH_PC_SAMPLE_H_ +#define _MACH_PC_SAMPLE_H_ + +#include + +typedef unsigned int sampled_pc_flavor_t; + + +#define SAMPLED_PC_PERIODIC 0x1 /* default */ + + +#define SAMPLED_PC_VM_ZFILL_FAULTS 0x10 +#define SAMPLED_PC_VM_REACTIVATION_FAULTS 0x20 +#define SAMPLED_PC_VM_PAGEIN_FAULTS 0x40 +#define SAMPLED_PC_VM_COW_FAULTS 0x80 +#define SAMPLED_PC_VM_FAULTS_ANY 0x100 +#define SAMPLED_PC_VM_FAULTS \ + (SAMPLED_PC_VM_ZFILL_FAULTS | \ + SAMPLED_PC_VM_REACTIVATION_FAULTS |\ + SAMPLED_PC_VM_PAGEIN_FAULTS |\ + SAMPLED_PC_VM_COW_FAULTS ) + + + + +/* + * Definitions for the PC sampling interface. + */ + +typedef struct sampled_pc { + rpc_vm_offset_t id; /* task_t address */ + rpc_vm_offset_t pc; /* program counter */ + sampled_pc_flavor_t sampletype; +} sampled_pc_t; + +typedef sampled_pc_t *sampled_pc_array_t; +typedef unsigned int sampled_pc_seqno_t; + + +#endif /* _MACH_PC_SAMPLE_H_ */ diff --git a/include/mach/policy.h b/include/mach/policy.h new file mode 100644 index 0000000..da776c9 --- /dev/null +++ b/include/mach/policy.h @@ -0,0 +1,45 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988 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. + */ + +#ifndef _MACH_POLICY_H_ +#define _MACH_POLICY_H_ + +/* + * mach/policy.h + * + * Definitions for scheduing policy. + */ + +/* + * Policy definitions. Policies must be powers of 2. + */ +#define POLICY_TIMESHARE 1 +#define POLICY_FIXEDPRI 2 +#define POLICY_LAST 2 + +#define invalid_policy(policy) (((policy) <= 0) || ((policy) > POLICY_LAST)) + +#endif /* _MACH_POLICY_H_ */ diff --git a/include/mach/port.h b/include/mach/port.h new file mode 100644 index 0000000..c9bbcf1 --- /dev/null +++ b/include/mach/port.h @@ -0,0 +1,159 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988,1987 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. + */ +/* + * File: mach/port.h + * + * Definition of a port + * + * [The basic mach_port_t type should probably be machine-dependent, + * as it must be represented by a 32-bit integer.] + */ + +#ifndef _MACH_PORT_H_ +#define _MACH_PORT_H_ + +#include +#include + +/* + * Port names are the type used by userspace, they are always 32-bit wide. + */ +typedef unsigned int mach_port_name_t; +typedef mach_port_name_t *mach_port_name_array_t; +typedef const mach_port_name_t *const_mach_port_name_array_t; + +/* + * A port is represented + * - by a port name in userspace + * - by a pointer in kernel space + * While in userspace mach_port_name_t and mach_port_name are interchangable, + * in kernelspace they need to be different and appropriately converted. + */ +#ifdef KERNEL +typedef vm_offset_t mach_port_t; +#else /* KERNEL */ +typedef mach_port_name_t mach_port_t; +#endif +typedef mach_port_t *mach_port_array_t; +typedef const mach_port_t *const_mach_port_array_t; +typedef int *rpc_signature_info_t; + +/* + * MACH_PORT_NULL is a legal value that can be carried in messages. + * It indicates the absence of any port or port rights. (A port + * argument keeps the message from being "simple", even if the + * value is MACH_PORT_NULL.) The value MACH_PORT_DEAD is also + * a legal value that can be carried in messages. It indicates + * that a port right was present, but it died. + */ + +#define MACH_PORT_NULL 0 /* works with both user and kernel ports */ +#define MACH_PORT_DEAD ((mach_port_t) ~0) +#define MACH_PORT_NAME_NULL ((mach_port_name_t) 0) +#define MACH_PORT_NAME_DEAD ((mach_port_name_t) ~0) + +#define MACH_PORT_VALID(port) \ + (((port) != MACH_PORT_NULL) && ((port) != MACH_PORT_DEAD)) +#define MACH_PORT_NAME_VALID(name) \ + (((name) != MACH_PORT_NAME_NULL) && ((name) != MACH_PORT_NAME_DEAD)) + +/* + * These are the different rights a task may have. + * The MACH_PORT_RIGHT_* definitions are used as arguments + * to mach_port_allocate, mach_port_get_refs, etc, to specify + * a particular right to act upon. The mach_port_names and + * mach_port_type calls return bitmasks using the MACH_PORT_TYPE_* + * definitions. This is because a single name may denote + * multiple rights. + */ + +typedef natural_t mach_port_right_t; + +#define MACH_PORT_RIGHT_SEND ((mach_port_right_t) 0) +#define MACH_PORT_RIGHT_RECEIVE ((mach_port_right_t) 1) +#define MACH_PORT_RIGHT_SEND_ONCE ((mach_port_right_t) 2) +#define MACH_PORT_RIGHT_PORT_SET ((mach_port_right_t) 3) +#define MACH_PORT_RIGHT_DEAD_NAME ((mach_port_right_t) 4) +#define MACH_PORT_RIGHT_NUMBER ((mach_port_right_t) 5) + +typedef natural_t mach_port_type_t; +typedef mach_port_type_t *mach_port_type_array_t; + +#define MACH_PORT_TYPE(right) ((mach_port_type_t)(1 << ((right)+16))) +#define MACH_PORT_TYPE_NONE ((mach_port_type_t) 0) +#define MACH_PORT_TYPE_SEND MACH_PORT_TYPE(MACH_PORT_RIGHT_SEND) +#define MACH_PORT_TYPE_RECEIVE MACH_PORT_TYPE(MACH_PORT_RIGHT_RECEIVE) +#define MACH_PORT_TYPE_SEND_ONCE MACH_PORT_TYPE(MACH_PORT_RIGHT_SEND_ONCE) +#define MACH_PORT_TYPE_PORT_SET MACH_PORT_TYPE(MACH_PORT_RIGHT_PORT_SET) +#define MACH_PORT_TYPE_DEAD_NAME MACH_PORT_TYPE(MACH_PORT_RIGHT_DEAD_NAME) + +/* Convenient combinations. */ + +#define MACH_PORT_TYPE_SEND_RECEIVE \ + (MACH_PORT_TYPE_SEND|MACH_PORT_TYPE_RECEIVE) +#define MACH_PORT_TYPE_SEND_RIGHTS \ + (MACH_PORT_TYPE_SEND|MACH_PORT_TYPE_SEND_ONCE) +#define MACH_PORT_TYPE_PORT_RIGHTS \ + (MACH_PORT_TYPE_SEND_RIGHTS|MACH_PORT_TYPE_RECEIVE) +#define MACH_PORT_TYPE_PORT_OR_DEAD \ + (MACH_PORT_TYPE_PORT_RIGHTS|MACH_PORT_TYPE_DEAD_NAME) +#define MACH_PORT_TYPE_ALL_RIGHTS \ + (MACH_PORT_TYPE_PORT_OR_DEAD|MACH_PORT_TYPE_PORT_SET) + +/* Dummy type bits that mach_port_type/mach_port_names can return. */ + +#define MACH_PORT_TYPE_DNREQUEST 0x80000000U +#define MACH_PORT_TYPE_MAREQUEST 0x40000000 +#define MACH_PORT_TYPE_COMPAT 0x20000000 + +/* User-references for capabilities. */ + +typedef natural_t mach_port_urefs_t; +typedef integer_t mach_port_delta_t; /* change in urefs */ + +/* Attributes of ports. (See mach_port_get_receive_status.) */ + +typedef natural_t mach_port_seqno_t; /* sequence number */ +typedef unsigned int mach_port_mscount_t; /* make-send count */ +typedef unsigned int mach_port_msgcount_t; /* number of msgs */ +typedef unsigned int mach_port_rights_t; /* number of rights */ + +typedef struct mach_port_status { + mach_port_name_t mps_pset; /* containing port set */ + mach_port_seqno_t mps_seqno; /* sequence number */ + mach_port_mscount_t mps_mscount; /* make-send count */ + mach_port_msgcount_t mps_qlimit; /* queue limit */ + mach_port_msgcount_t mps_msgcount; /* number in the queue */ + mach_port_rights_t mps_sorights; /* how many send-once rights */ + boolean_t mps_srights; /* do send rights exist? */ + boolean_t mps_pdrequest; /* port-deleted requested? */ + boolean_t mps_nsrequest; /* no-senders requested? */ +} mach_port_status_t; + +#define MACH_PORT_QLIMIT_DEFAULT ((mach_port_msgcount_t) 5) +#define MACH_PORT_QLIMIT_MAX ((mach_port_msgcount_t) 16) + +#endif /* _MACH_PORT_H_ */ diff --git a/include/mach/processor_info.h b/include/mach/processor_info.h new file mode 100644 index 0000000..5f761ea --- /dev/null +++ b/include/mach/processor_info.h @@ -0,0 +1,104 @@ +/* + * Mach Operating System + * Copyright (c) 1993,1992,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. + */ +/* + * File: mach/processor_info.h + * Author: David L. Black + * Date: 1988 + * + * Data structure definitions for processor_info, processor_set_info + */ + +#ifndef _MACH_PROCESSOR_INFO_H_ +#define _MACH_PROCESSOR_INFO_H_ + +#include + +/* + * Generic information structure to allow for expansion. + */ +typedef integer_t *processor_info_t; /* varying array of int. */ + +#define PROCESSOR_INFO_MAX (1024) /* max array size */ +typedef integer_t processor_info_data_t[PROCESSOR_INFO_MAX]; + + +typedef integer_t *processor_set_info_t; /* varying array of int. */ + +#define PROCESSOR_SET_INFO_MAX (1024) /* max array size */ +typedef integer_t processor_set_info_data_t[PROCESSOR_SET_INFO_MAX]; + +/* + * Currently defined information. + */ +#define PROCESSOR_BASIC_INFO 1 /* basic information */ + +struct processor_basic_info { + cpu_type_t cpu_type; /* type of cpu */ + cpu_subtype_t cpu_subtype; /* subtype of cpu */ +/*boolean_t*/integer_t running; /* is processor running */ + integer_t slot_num; /* slot number */ +/*boolean_t*/integer_t is_master; /* is this the master processor */ +}; + +typedef struct processor_basic_info processor_basic_info_data_t; +typedef struct processor_basic_info *processor_basic_info_t; +#define PROCESSOR_BASIC_INFO_COUNT \ + (sizeof(processor_basic_info_data_t)/sizeof(integer_t)) + + +#define PROCESSOR_SET_BASIC_INFO 1 /* basic information */ + +struct processor_set_basic_info { + integer_t processor_count; /* How many processors */ + integer_t task_count; /* How many tasks */ + integer_t thread_count; /* How many threads */ + integer_t load_average; /* Scaled */ + integer_t mach_factor; /* Scaled */ +}; + +/* + * Scaling factor for load_average, mach_factor. + */ +#define LOAD_SCALE 1000 + +typedef struct processor_set_basic_info processor_set_basic_info_data_t; +typedef struct processor_set_basic_info *processor_set_basic_info_t; +#define PROCESSOR_SET_BASIC_INFO_COUNT \ + (sizeof(processor_set_basic_info_data_t)/sizeof(integer_t)) + +#define PROCESSOR_SET_SCHED_INFO 2 /* scheduling info */ + +struct processor_set_sched_info { + integer_t policies; /* allowed policies */ + integer_t max_priority; /* max priority for new threads */ +}; + +typedef struct processor_set_sched_info processor_set_sched_info_data_t; +typedef struct processor_set_sched_info *processor_set_sched_info_t; +#define PROCESSOR_SET_SCHED_INFO_COUNT \ + (sizeof(processor_set_sched_info_data_t)/sizeof(integer_t)) + +#endif /* _MACH_PROCESSOR_INFO_H_ */ diff --git a/include/mach/profil.h b/include/mach/profil.h new file mode 100644 index 0000000..866f267 --- /dev/null +++ b/include/mach/profil.h @@ -0,0 +1,212 @@ +/* + * 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. + */ + +/* + * Copyright 1991 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. + */ + + +#ifndef _MACH_PROFIL_H_ +#define _MACH_PROFIL_H_ + +#include +#include +#include + + +#define NB_PROF_BUFFER 2 /* number of buffers servicing a + * profiled thread */ +#define SIZE_PROF_BUFFER 100 /* size of a profil buffer (in int) + * This values is also defined in + * the server (ugly), be careful ! */ + + +struct prof_data { + ipc_object_t prof_port; /* where to send a full buffer */ + + struct buffer { + int *p_zone; /* points to the actual storage area */ + int p_index;/* next slot to be filled */ + boolean_t p_full; /* is the current buffer full ? */ + } prof_area[NB_PROF_BUFFER]; + + int prof_index; /* index of the buffer structure + * currently in use */ + +}; +typedef struct prof_data *prof_data_t; +#define NULLPBUF ((prof_data_t) 0) +typedef struct buffer *buffer_t; + +/* Macros */ + +#define set_pbuf_nb(pbuf, nb) \ + (((nb) >= 0 && (nb) < NB_PROF_BUFFER) \ + ? (pbuf)->prof_index = (nb), 1 \ + : 0) + + +#define get_pbuf_nb(pbuf) \ + (pbuf)->prof_index + + +extern vm_map_t kernel_map; + +#define dealloc_pbuf_area(pbuf) \ + { \ + register int i; \ + \ + for(i=0; i < NB_PROF_BUFFER ; i++) \ + kmem_free(kernel_map, \ + (vm_offset_t) (pbuf)->prof_area[i].p_zone, \ + SIZE_PROF_BUFFER*sizeof(int)); \ + kmem_free(kernel_map, \ + (vm_offset_t)(pbuf), \ + sizeof(struct prof_data)); \ + } + + +#define alloc_pbuf_area(pbuf, vmpbuf) \ + (vmpbuf) = (vm_offset_t) 0; \ + if (kmem_alloc(kernel_map, &(vmpbuf) , sizeof(struct prof_data)) == \ + KERN_SUCCESS) { \ + register int i; \ + register boolean_t end; \ + \ + (pbuf) = (prof_data_t) (vmpbuf); \ + for(i=0, end=FALSE; i < NB_PROF_BUFFER && end == FALSE; i++) { \ + (vmpbuf) = (vm_offset_t) 0; \ + if (kmem_alloc(kernel_map,&(vmpbuf),SIZE_PROF_BUFFER*sizeof(int)) == KERN_SUCCESS) { \ + (pbuf)->prof_area[i].p_zone = (int *) (vmpbuf); \ + (pbuf)->prof_area[i].p_full = FALSE; \ + } \ + else { \ + (pbuf) = NULLPBUF; \ + end = TRUE; \ + } \ + } \ + } \ + else \ + (pbuf) = NULLPBUF; + + + +/* MACRO set_pbuf_value +** +** enters the value 'val' in the buffer 'pbuf' and returns the following +** indications: 0: means that a fatal error occurred: the buffer was full +** (it hasn't been sent yet) +** 1: means that a value has been inserted successfully +** 2: means that we'v just entered the last value causing +** the current buffer to be full.(must switch to +** another buffer and signal the sender to send it) +*/ + +#define set_pbuf_value(pbuf, val) \ + { \ + register buffer_t a = &((pbuf)->prof_area[(pbuf)->prof_index]); \ + register int i = a->p_index++; \ + register boolean_t f = a->p_full; \ + \ + if (f == TRUE ) \ + *(val) = 0; \ + else { \ + a->p_zone[i] = *(val); \ + if (i == SIZE_PROF_BUFFER-1) { \ + a->p_full = TRUE; \ + *(val) = 2; \ + } \ + else \ + *(val) = 1; \ + } \ + } + + +#define reset_pbuf_area(pbuf) \ + { \ + register int *i = &((pbuf)->prof_index); \ + \ + *i = (*i == NB_PROF_BUFFER-1) ? 0 : ++(*i); \ + (pbuf)->prof_area[*i].p_index = 0; \ + } + + +/**************************************************************/ +/* Structure, elements used for queuing operations on buffers */ +/**************************************************************/ + +#define thread_t int * +/* +** This must be done in order to avoid a circular inclusion +** with file kern/thread.h . +** When using this data structure, one must cast the actual +** type, this is (int *) or (thread_t) +*/ + +struct buf_to_send { + queue_chain_t list; + thread_t thread; + int number; /* the number of the buffer to be sent */ + char wakeme; /* do wakeup when buffer has been sent */ + } ; + +#undef thread_t + + + +typedef struct buf_to_send *buf_to_send_t; + +#define NULLBTS ((buf_to_send_t) 0) + +/* +** Global variable: the head of the queue of buffers to send +** It is a queue with locks (uses macros from queue.h) and it +** is shared by hardclock() and the sender_thread() +*/ + +mpqueue_head_t prof_queue; + +#endif /* _MACH_PROF_H_ */ diff --git a/include/mach/profilparam.h b/include/mach/profilparam.h new file mode 100644 index 0000000..20a8aaf --- /dev/null +++ b/include/mach/profilparam.h @@ -0,0 +1,62 @@ +/* + * 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. + */ + +/* + * Copyright 1991 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. + */ + +#ifndef _MACH_PROFILPARAM_H_ +#define _MACH_PROFILPARAM_H_ + +/* + * These values are also used when compiling the server, be careful ! + */ + +#define NB_PROF_BUFFER 2 /* number of buffers servicing a + * profiled thread */ +#define SIZE_PROF_BUFFER 100 /* size of a profil buffer (in int) */ + +#endif /* _MACH_PROFILPARAM_H_ */ diff --git a/include/mach/std_types.defs b/include/mach/std_types.defs new file mode 100644 index 0000000..b461f06 --- /dev/null +++ b/include/mach/std_types.defs @@ -0,0 +1,101 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988 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. + */ +/* + * Mach kernel standard interface type declarations + */ + +#ifndef _MACH_STD_TYPES_DEFS_ +#define _MACH_STD_TYPES_DEFS_ + +type int32_t = MACH_MSG_TYPE_INTEGER_32; +type int64_t = MACH_MSG_TYPE_INTEGER_64; +type boolean_t = MACH_MSG_TYPE_BOOLEAN; +type unsigned = MACH_MSG_TYPE_INTEGER_32; +type uint32_t = MACH_MSG_TYPE_INTEGER_32; +type uint64_t = MACH_MSG_TYPE_INTEGER_64; + +/* Get the definitions for natural_t and integer_t */ +#include + +type kern_return_t = int; + +type pointer_t = ^array[] of MACH_MSG_TYPE_BYTE + ctype: vm_offset_t; + + +type mach_port_t = MACH_MSG_TYPE_COPY_SEND +#ifndef KERNEL_SERVER +#ifdef MACH_PAYLOAD_TO_PORT + intranpayload: mach_port_t MACH_PAYLOAD_TO_PORT +#endif /* MACH_PAYLOAD_TO_PORT */ +#endif /* KERNEL_SERVER */ +; +type mach_port_array_t = array[] of mach_port_t; + +type mach_port_name_t = MACH_MSG_TYPE_PORT_NAME; +type mach_port_name_array_t = array[] of mach_port_name_t; + +type mach_port_right_t = natural_t; + +type mach_port_type_t = natural_t; +type mach_port_type_array_t = array[] of mach_port_type_t; + +type mach_port_urefs_t = natural_t; +type mach_port_delta_t = integer_t; +type mach_port_seqno_t = natural_t; +type mach_port_mscount_t = unsigned; +type mach_port_msgcount_t = unsigned; +type mach_port_rights_t = unsigned; +type mach_msg_id_t = integer_t; +type mach_msg_type_name_t = unsigned; +type mach_msg_type_number_t = natural_t; + +type mach_port_move_receive_t = MACH_MSG_TYPE_MOVE_RECEIVE + ctype: mach_port_t; +type mach_port_copy_send_t = MACH_MSG_TYPE_COPY_SEND + ctype: mach_port_t; +type mach_port_make_send_t = MACH_MSG_TYPE_MAKE_SEND + ctype: mach_port_t; +type mach_port_move_send_t = MACH_MSG_TYPE_MOVE_SEND + ctype: mach_port_t; +type mach_port_make_send_once_t = MACH_MSG_TYPE_MAKE_SEND_ONCE + ctype: mach_port_t; +type mach_port_move_send_once_t = MACH_MSG_TYPE_MOVE_SEND_ONCE + ctype: mach_port_t; + +type mach_port_receive_t = MACH_MSG_TYPE_PORT_RECEIVE + ctype: mach_port_t; +type mach_port_send_t = MACH_MSG_TYPE_PORT_SEND + ctype: mach_port_t; +type mach_port_send_once_t = MACH_MSG_TYPE_PORT_SEND_ONCE + ctype: mach_port_t; + +type mach_port_poly_t = polymorphic + ctype: mach_port_t; + +import ; + +#endif /* _MACH_STD_TYPES_DEFS_ */ diff --git a/include/mach/std_types.h b/include/mach/std_types.h new file mode 100644 index 0000000..0d5db0a --- /dev/null +++ b/include/mach/std_types.h @@ -0,0 +1,44 @@ +/* + * Mach Operating System + * Copyright (c) 1992,1991,1990,1989,1988 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. + */ +/* + * Mach standard external interface type definitions. + * + */ + +#ifndef _MACH_STD_TYPES_H_ +#define _MACH_STD_TYPES_H_ + +#define EXPORT_BOOLEAN + +#include +#include +#include +#include + +typedef vm_offset_t pointer_t; +typedef vm_offset_t vm_address_t; + +#endif /* _MACH_STD_TYPES_H_ */ diff --git a/include/mach/syscall_sw.h b/include/mach/syscall_sw.h new file mode 100644 index 0000000..89597e9 --- /dev/null +++ b/include/mach/syscall_sw.h @@ -0,0 +1,121 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988,1987 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. + */ + +#ifndef _MACH_SYSCALL_SW_H_ +#define _MACH_SYSCALL_SW_H_ + +/* + * The machine-dependent "syscall_sw.h" file should + * define a macro for + * kernel_trap(trap_name, trap_number, arg_count) + * which will expand into assembly code for the + * trap. + * + * N.B.: When adding calls, do not put spaces in the macros. + */ + +#include + +/* + * These trap numbers should be taken from the + * table in . + */ + +kernel_trap(evc_wait,-17,1) +kernel_trap(evc_wait_clear,-18,1) + +kernel_trap(mach_msg_trap,-25,7) +kernel_trap(mach_reply_port,-26,0) +kernel_trap(mach_thread_self,-27,0) +kernel_trap(mach_task_self,-28,0) +kernel_trap(mach_host_self,-29,0) +kernel_trap(mach_print,-30,1) + +kernel_trap(swtch_pri,-59,1) +kernel_trap(swtch,-60,0) +kernel_trap(thread_switch,-61,3) +kernel_trap(nw_update,-80,3) +kernel_trap(nw_lookup,-81,2) +kernel_trap(nw_endpoint_allocate,-82,4) +kernel_trap(nw_endpoint_deallocate,-83,1) +kernel_trap(nw_buffer_allocate,-84,2) +kernel_trap(nw_buffer_deallocate,-85,2) +kernel_trap(nw_connection_open,-86,4) +kernel_trap(nw_connection_accept,-87,3) +kernel_trap(nw_connection_close,-88,1) +kernel_trap(nw_multicast_add,-89,4) +kernel_trap(nw_multicast_drop,-90,4) +kernel_trap(nw_endpoint_status,-91,3) +kernel_trap(nw_send,-92,3) +kernel_trap(nw_receive,-93,2) +kernel_trap(nw_rpc,-94,4) +kernel_trap(nw_select,-95,3) + + +/* + * These are syscall versions of Mach kernel calls. + * They only work on local tasks. + */ + +kernel_trap(syscall_vm_map,-64,11) +kernel_trap(syscall_vm_allocate,-65,4) +kernel_trap(syscall_vm_deallocate,-66,3) + +kernel_trap(syscall_task_create,-68,3) +kernel_trap(syscall_task_terminate,-69,1) +kernel_trap(syscall_task_suspend,-70,1) +kernel_trap(syscall_task_set_special_port,-71,3) + +kernel_trap(syscall_mach_port_allocate,-72,3) +kernel_trap(syscall_mach_port_deallocate,-73,2) +kernel_trap(syscall_mach_port_insert_right,-74,4) +kernel_trap(syscall_mach_port_allocate_name,-75,3) +kernel_trap(syscall_thread_depress_abort,-76,1) + +/* These are screwing up glibc somehow. */ +/*kernel_trap(syscall_device_writev_request,-39,6)*/ +/*kernel_trap(syscall_device_write_request,-40,6)*/ + +/* + * These "Mach" traps are not implemented by the kernel; + * the emulation library and Unix server implement them. + * But they are traditionally part of libmach, and use + * the Mach trap calling conventions and numbering. + */ + +#if UNIXOID_TRAPS + +kernel_trap(task_by_pid,-33,1) +kernel_trap(pid_by_task,-34,4) +kernel_trap(init_process,-41,0) +kernel_trap(map_fd,-43,5) +kernel_trap(rfs_make_symlink,-44,3) +kernel_trap(htg_syscall,-52,3) +kernel_trap(set_ras_address,-53,2) + +#endif /* UNIXOID_TRAPS */ + +#endif /* _MACH_SYSCALL_SW_H_ */ diff --git a/include/mach/task_info.h b/include/mach/task_info.h new file mode 100644 index 0000000..0e048c5 --- /dev/null +++ b/include/mach/task_info.h @@ -0,0 +1,126 @@ +/* + * Mach Operating System + * Copyright (c) 1993-1987 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. + */ +/* + * Machine-independent task information structures and definitions. + * + * The definitions in this file are exported to the user. The kernel + * will translate its internal data structures to these structures + * as appropriate. + * + */ + +#ifndef _MACH_TASK_INFO_H_ +#define _MACH_TASK_INFO_H_ + +#include +#include + +/* + * Generic information structure to allow for expansion. + */ +typedef integer_t *task_info_t; /* varying array of int */ + +#define TASK_INFO_MAX (1024) /* maximum array size */ +typedef integer_t task_info_data_t[TASK_INFO_MAX]; + +/* + * Currently defined information structures. + */ +#define TASK_BASIC_INFO 1 /* basic information */ + +struct task_basic_info { + integer_t suspend_count; /* suspend count for task */ + integer_t base_priority; /* base scheduling priority */ + rpc_vm_size_t virtual_size; /* number of virtual pages */ + rpc_vm_size_t resident_size; /* number of resident pages */ + /* Deprecated, please use user_time64 */ + rpc_time_value_t user_time; /* total user run time for + terminated threads */ + /* Deprecated, please use system_time64 */ + rpc_time_value_t system_time; /* total system run time for + terminated threads */ + /* Deprecated, please use creation_time64 */ + rpc_time_value_t creation_time; /* creation time stamp */ + time_value64_t user_time64; /* total user run time for + terminated threads */ + time_value64_t system_time64; /* total system run time for + terminated threads */ + time_value64_t creation_time64; /* creation time stamp */ +}; + +typedef struct task_basic_info task_basic_info_data_t; +typedef struct task_basic_info *task_basic_info_t; +#define TASK_BASIC_INFO_COUNT \ + (sizeof(task_basic_info_data_t) / sizeof(integer_t)) + + +#define TASK_EVENTS_INFO 2 /* various event counts */ + +struct task_events_info { + rpc_long_natural_t faults; /* number of page faults */ + rpc_long_natural_t zero_fills; /* number of zero fill pages */ + rpc_long_natural_t reactivations; /* number of reactivated pages */ + rpc_long_natural_t pageins; /* number of actual pageins */ + rpc_long_natural_t cow_faults; /* number of copy-on-write faults */ + rpc_long_natural_t messages_sent; /* number of messages sent */ + rpc_long_natural_t messages_received; /* number of messages received */ +}; +typedef struct task_events_info task_events_info_data_t; +typedef struct task_events_info *task_events_info_t; +#define TASK_EVENTS_INFO_COUNT \ + (sizeof(task_events_info_data_t) / sizeof(integer_t)) + +#define TASK_THREAD_TIMES_INFO 3 /* total times for live threads - + only accurate if suspended */ + +struct task_thread_times_info { + /* Deprecated, please use user_time64 */ + rpc_time_value_t user_time; /* total user run time for + live threads */ + /* Deprecated, please use system_time64 */ + rpc_time_value_t system_time; /* total system run time for + live threads */ + time_value64_t user_time64; /* total user run time for + live threads */ + time_value64_t system_time64; /* total system run time for + live threads */ +}; + +typedef struct task_thread_times_info task_thread_times_info_data_t; +typedef struct task_thread_times_info *task_thread_times_info_t; +#define TASK_THREAD_TIMES_INFO_COUNT \ + (sizeof(task_thread_times_info_data_t) / sizeof(integer_t)) + +/* + * Flavor definitions for task_ras_control + */ +#define TASK_RAS_CONTROL_PURGE_ALL 0 +#define TASK_RAS_CONTROL_PURGE_ONE 1 +#define TASK_RAS_CONTROL_PURGE_ALL_AND_INSTALL_ONE 2 +#define TASK_RAS_CONTROL_INSTALL_ONE 3 + +#endif /* _MACH_TASK_INFO_H_ */ + diff --git a/include/mach/task_notify.defs b/include/mach/task_notify.defs new file mode 100644 index 0000000..a4aff67 --- /dev/null +++ b/include/mach/task_notify.defs @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2014 Free Software Foundation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +subsystem +#if KERNEL_SERVER + KernelServer +#endif /* KERNEL_SERVER */ +#if KERNEL_USER + KernelUser +#endif /* KERNEL_USER */ + task_notify 4400; + +#include +#include + +type task_notify_port_t = mach_port_t + ctype: mach_port_t +#ifdef TASK_NOTIFY_INTRAN + intran: TASK_NOTIFY_INTRAN +#endif +#ifdef TASK_NOTIFY_INTRAN_PAYLOAD + intranpayload: TASK_NOTIFY_INTRAN_PAYLOAD +#endif +#ifdef TASK_NOTIFY_OUTTRAN + outtran: TASK_NOTIFY_OUTTRAN +#endif +#ifdef TASK_NOTIFY_DESTRUCTOR + destructor: TASK_NOTIFY_DESTRUCTOR +#endif +; + +#ifdef TASK_NOTIFY_IMPORTS +TASK_NOTIFY_IMPORTS +#endif + +type task_move_t = MACH_MSG_TYPE_MOVE_SEND + ctype: mach_port_t; + +/* These notifications are sent to the port registered via + `register_new_task_notification' and provide a robust parental + relation between tasks. */ +simpleroutine mach_notify_new_task( + notify : task_notify_port_t; + task : task_move_t; + parent : task_move_t); diff --git a/include/mach/task_special_ports.h b/include/mach/task_special_ports.h new file mode 100644 index 0000000..42ecc15 --- /dev/null +++ b/include/mach/task_special_ports.h @@ -0,0 +1,66 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988,1987 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. + */ +/* + * File: mach/task_special_ports.h + * + * Defines codes for special_purpose task ports. These are NOT + * port identifiers - they are only used for the task_get_special_port + * and task_set_special_port routines. + * + */ + +#ifndef _MACH_TASK_SPECIAL_PORTS_H_ +#define _MACH_TASK_SPECIAL_PORTS_H_ + +#define TASK_KERNEL_PORT 1 /* Represents task to the outside + world.*/ +#define TASK_EXCEPTION_PORT 3 /* Exception messages for task are + sent to this port. */ +#define TASK_BOOTSTRAP_PORT 4 /* Bootstrap environment for task. */ + +/* + * Definitions for ease of use + */ + +#define task_get_kernel_port(task, port) \ + (task_get_special_port((task), TASK_KERNEL_PORT, (port))) + +#define task_set_kernel_port(task, port) \ + (task_set_special_port((task), TASK_KERNEL_PORT, (port))) + +#define task_get_exception_port(task, port) \ + (task_get_special_port((task), TASK_EXCEPTION_PORT, (port))) + +#define task_set_exception_port(task, port) \ + (task_set_special_port((task), TASK_EXCEPTION_PORT, (port))) + +#define task_get_bootstrap_port(task, port) \ + (task_get_special_port((task), TASK_BOOTSTRAP_PORT, (port))) + +#define task_set_bootstrap_port(task, port) \ + (task_set_special_port((task), TASK_BOOTSTRAP_PORT, (port))) + +#endif /* _MACH_TASK_SPECIAL_PORTS_H_ */ diff --git a/include/mach/thread_info.h b/include/mach/thread_info.h new file mode 100644 index 0000000..4f322e0 --- /dev/null +++ b/include/mach/thread_info.h @@ -0,0 +1,124 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988,1987 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. + */ +/* + * File: mach/thread_info + * + * Thread information structure and definitions. + * + * The defintions in this file are exported to the user. The kernel + * will translate its internal data structures to these structures + * as appropriate. + * + */ + +#ifndef _MACH_THREAD_INFO_H_ +#define _MACH_THREAD_INFO_H_ + +#include +#include +#include + +/* + * Generic information structure to allow for expansion. + */ +typedef integer_t *thread_info_t; /* varying array of ints */ + +#define THREAD_INFO_MAX (1024) /* maximum array size */ +typedef integer_t thread_info_data_t[THREAD_INFO_MAX]; + +/* + * Currently defined information. + */ +#define THREAD_BASIC_INFO 1 /* basic information */ + +struct thread_basic_info { + /* Deprecated, please use user_time64 */ + rpc_time_value_t user_time; /* user run time */ + /* Deprecated, please use system_time64 */ + rpc_time_value_t system_time; /* system run time */ + integer_t cpu_usage; /* scaled cpu usage percentage */ + integer_t base_priority; /* base scheduling priority */ + integer_t cur_priority; /* current scheduling priority */ + integer_t run_state; /* run state (see below) */ + integer_t flags; /* various flags (see below) */ + integer_t suspend_count; /* suspend count for thread */ + integer_t sleep_time; /* number of seconds that thread + has been sleeping */ + /* Deprecated, please use creation_time64 */ + rpc_time_value_t creation_time; /* time stamp of creation */ + time_value64_t user_time64; /* user run time */ + time_value64_t system_time64; /* system run time */ + time_value64_t creation_time64; /* time stamp of creation */ +}; + +typedef struct thread_basic_info thread_basic_info_data_t; +typedef struct thread_basic_info *thread_basic_info_t; +#define THREAD_BASIC_INFO_COUNT \ + (sizeof(thread_basic_info_data_t) / sizeof(natural_t)) + +/* + * Scale factor for usage field. + */ + +#define TH_USAGE_SCALE 1000 + +/* + * Thread run states (state field). + */ + +#define TH_STATE_RUNNING 1 /* thread is running normally */ +#define TH_STATE_STOPPED 2 /* thread is stopped */ +#define TH_STATE_WAITING 3 /* thread is waiting normally */ +#define TH_STATE_UNINTERRUPTIBLE 4 /* thread is in an uninterruptible + wait */ +#define TH_STATE_HALTED 5 /* thread is halted at a + clean point */ + +/* + * Thread flags (flags field). + */ +#define TH_FLAGS_SWAPPED 0x1 /* thread is swapped out */ +#define TH_FLAGS_IDLE 0x2 /* thread is an idle thread */ + +#define THREAD_SCHED_INFO 2 + +struct thread_sched_info { + integer_t policy; /* scheduling policy */ + integer_t data; /* associated data */ + integer_t base_priority; /* base priority */ + integer_t max_priority; /* max priority */ + integer_t cur_priority; /* current priority */ +/*boolean_t*/integer_t depressed; /* depressed ? */ + integer_t depress_priority; /* priority depressed from */ + integer_t last_processor; /* last processor used by the thread */ +}; + +typedef struct thread_sched_info thread_sched_info_data_t; +typedef struct thread_sched_info *thread_sched_info_t; +#define THREAD_SCHED_INFO_COUNT \ + (sizeof(thread_sched_info_data_t) / sizeof(natural_t)) + +#endif /* _MACH_THREAD_INFO_H_ */ diff --git a/include/mach/thread_special_ports.h b/include/mach/thread_special_ports.h new file mode 100644 index 0000000..33e3a1f --- /dev/null +++ b/include/mach/thread_special_ports.h @@ -0,0 +1,59 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988,1987 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. + */ +/* + * File: mach/thread_special_ports.h + * + * Defines codes for special_purpose thread ports. These are NOT + * port identifiers - they are only used for the thread_get_special_port + * and thread_set_special_port routines. + * + */ + +#ifndef _MACH_THREAD_SPECIAL_PORTS_H_ +#define _MACH_THREAD_SPECIAL_PORTS_H_ + +#define THREAD_KERNEL_PORT 1 /* Represents the thread to the outside + world.*/ +#define THREAD_EXCEPTION_PORT 3 /* Exception messages for the thread + are sent to this port. */ + +/* + * Definitions for ease of use + */ + +#define thread_get_kernel_port(thread, port) \ + (thread_get_special_port((thread), THREAD_KERNEL_PORT, (port))) + +#define thread_set_kernel_port(thread, port) \ + (thread_set_special_port((thread), THREAD_KERNEL_PORT, (port))) + +#define thread_get_exception_port(thread, port) \ + (thread_get_special_port((thread), THREAD_EXCEPTION_PORT, (port))) + +#define thread_set_exception_port(thread, port) \ + (thread_set_special_port((thread), THREAD_EXCEPTION_PORT, (port))) + +#endif /* _MACH_THREAD_SPECIAL_PORTS_H_ */ diff --git a/include/mach/thread_status.h b/include/mach/thread_status.h new file mode 100644 index 0000000..b02f5b4 --- /dev/null +++ b/include/mach/thread_status.h @@ -0,0 +1,55 @@ +/* + * Mach Operating System + * Copyright (c) 1993-1988 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. + */ +/* + * + * This file contains the structure definitions for the user-visible + * thread state. This thread state is examined with the thread_get_state + * kernel call and may be changed with the thread_set_state kernel call. + * + */ + +#ifndef _MACH_THREAD_STATUS_H_ +#define _MACH_THREAD_STATUS_H_ + +/* + * The actual structure that comprises the thread state is defined + * in the machine dependent module. + */ +#include +#include + +/* + * Generic definition for machine-dependent thread status. + */ + +typedef natural_t *thread_state_t; /* Variable-length array */ + +#define THREAD_STATE_MAX (1024) /* Maximum array size */ +typedef natural_t thread_state_data_t[THREAD_STATE_MAX]; + +#define THREAD_STATE_FLAVOR_LIST 0 /* List of valid flavors */ + +#endif /* _MACH_THREAD_STATUS_H_ */ diff --git a/include/mach/thread_switch.h b/include/mach/thread_switch.h new file mode 100644 index 0000000..5235b87 --- /dev/null +++ b/include/mach/thread_switch.h @@ -0,0 +1,40 @@ +/* + * 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. + */ + +#ifndef _MACH_THREAD_SWITCH_H_ +#define _MACH_THREAD_SWITCH_H_ + +/* + * Constant definitions for thread_switch trap. + */ + +#define SWITCH_OPTION_NONE 0 +#define SWITCH_OPTION_DEPRESS 1 +#define SWITCH_OPTION_WAIT 2 + +#define valid_switch_option(opt) ((0 <= (opt)) && ((opt) <= 2)) + +#endif /* _MACH_THREAD_SWITCH_H_ */ diff --git a/include/mach/time_value.h b/include/mach/time_value.h new file mode 100644 index 0000000..e08707b --- /dev/null +++ b/include/mach/time_value.h @@ -0,0 +1,201 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988,1987 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. + */ + +#ifndef _MACH_TIME_VALUE_H_ +#define _MACH_TIME_VALUE_H_ + +#include + +/* + * Time value returned by kernel. + */ + +struct rpc_time_value { + /* TODO: this should be 64 bits regardless of the arch to be Y2038 proof. */ + rpc_long_integer_t seconds; + integer_t microseconds; +}; + +/* + * Time value used by kernel interfaces. Ideally they should be migrated + * to use time_value64 below. + */ +struct time_value { + long_integer_t seconds; + integer_t microseconds; +}; +typedef struct time_value time_value_t; + +#ifdef KERNEL +typedef struct rpc_time_value rpc_time_value_t; +#else +typedef struct time_value rpc_time_value_t; +#endif + +/* + * Time value used internally by the kernel that uses 64 bits to track seconds + * and nanoseconds. Note that the current resolution is only microseconds. + */ +struct time_value64 { + int64_t seconds; + int64_t nanoseconds; +}; +typedef struct time_value64 time_value64_t; + +/** + * Functions used by Mig to perform user to kernel conversion and vice-versa. + * We only do this because we may run a 64 bit kernel with a 32 bit user space. + */ +static __inline__ rpc_time_value_t convert_time_value_to_user(time_value_t tv) +{ + rpc_time_value_t user = {.seconds = tv.seconds, .microseconds = tv.microseconds}; + return user; +} +static __inline__ time_value_t convert_time_value_from_user(rpc_time_value_t tv) +{ + time_value_t kernel = {.seconds = tv.seconds, .microseconds = tv.microseconds}; + return kernel; +} + +/* + * Macros to manipulate time values. Assume that time values + * are normalized (microseconds <= 999999). + */ +#define TIME_MICROS_MAX (1000000) +#define TIME_NANOS_MAX (1000000000) + +#define time_value_assert(val) \ + assert(0 <= (val)->microseconds && (val)->microseconds < TIME_MICROS_MAX); + +#define time_value64_assert(val) \ + assert(0 <= (val)->nanoseconds && (val)->nanoseconds < TIME_NANOS_MAX); + +#define time_value_add_usec(val, micros) { \ + time_value_assert(val); \ + if (((val)->microseconds += (micros)) \ + >= TIME_MICROS_MAX) { \ + (val)->microseconds -= TIME_MICROS_MAX; \ + (val)->seconds++; \ + } \ + time_value_assert(val); \ +} + +#define time_value64_add_nanos(val, nanos) { \ + time_value64_assert(val); \ + if (((val)->nanoseconds += (nanos)) \ + >= TIME_NANOS_MAX) { \ + (val)->nanoseconds -= TIME_NANOS_MAX; \ + (val)->seconds++; \ + } \ + time_value64_assert(val); \ +} + +#define time_value64_sub_nanos(val, nanos) { \ + time_value64_assert(val); \ + if (((val)->nanoseconds -= (nanos)) < 0) { \ + (val)->nanoseconds += TIME_NANOS_MAX; \ + (val)->seconds--; \ + } \ + time_value64_assert(val); \ +} + +#define time_value_add(result, addend) { \ + time_value_assert(addend); \ + (result)->seconds += (addend)->seconds; \ + time_value_add_usec(result, (addend)->microseconds); \ + } + +#define time_value64_add(result, addend) { \ + time_value64_assert(addend); \ + (result)->seconds += (addend)->seconds; \ + time_value64_add_nanos(result, (addend)->nanoseconds); \ + } + +#define time_value64_sub(result, subtrahend) { \ + time_value64_assert(subtrahend); \ + (result)->seconds -= (subtrahend)->seconds; \ + time_value64_sub_nanos(result, (subtrahend)->nanoseconds); \ + } + +#define time_value64_init(tv) { \ + (tv)->seconds = 0; \ + (tv)->nanoseconds = 0; \ + } + +#define TIME_VALUE64_TO_TIME_VALUE(tv64, tv) do { \ + (tv)->seconds = (tv64)->seconds; \ + (tv)->microseconds = (tv64)->nanoseconds / 1000; \ +} while(0) + +#define TIME_VALUE_TO_TIME_VALUE64(tv, tv64) do { \ + (tv64)->seconds = (tv)->seconds; \ + (tv64)->nanoseconds = (tv)->microseconds * 1000; \ +} while(0) + +/* + * Time value available through the mapped-time interface. + * Read this mapped value with + * do { + * secs = mtime->seconds; + * __sync_synchronize(); + * usecs = mtime->microseconds; + * __sync_synchronize(); + * } while (secs != mtime->check_seconds); + */ + +typedef struct mapped_time_value { + integer_t seconds; + integer_t microseconds; + integer_t check_seconds; + struct time_value64 time_value; + int64_t check_seconds64; +} mapped_time_value_t; + +/* Macros for converting between struct timespec and time_value_t. */ + +#define TIME_VALUE_TO_TIMESPEC(tv, ts) do { \ + (ts)->tv_sec = (tv)->seconds; \ + (ts)->tv_nsec = (tv)->microseconds * 1000; \ +} while(0) + +#define TIMESPEC_TO_TIME_VALUE(tv, ts) do { \ + (tv)->seconds = (ts)->tv_sec; \ + (tv)->microseconds = (ts)->tv_nsec / 1000; \ +} while(0) + +/* Macros for converting between struct timespec and time_value64_t. */ + +#define TIME_VALUE64_TO_TIMESPEC(tv, ts) do { \ + (ts)->tv_sec = (tv)->seconds; \ + (ts)->tv_nsec = (tv)->nanoseconds; \ +} while(0) + +#define TIMESPEC_TO_TIME_VALUE64(tv, ts) do { \ + (tv)->seconds = (ts)->tv_sec; \ + (tv)->nanoseconds = (ts)->tv_nsec; \ +} while(0) + +#endif /* _MACH_TIME_VALUE_H_ */ diff --git a/include/mach/version.h b/include/mach/version.h new file mode 100644 index 0000000..3ef7859 --- /dev/null +++ b/include/mach/version.h @@ -0,0 +1,73 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988 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 rights + * to redistribute these changes. + */ +/* + * Each kernel has a major and minor version number. Changes in + * the major number in general indicate a change in exported features. + * Changes in minor number usually correspond to internal-only + * changes that the user need not be aware of (in general). These + * values are stored at boot time in the machine_info strucuture and + * can be obtained by user programs with the host_info kernel call. + * This mechanism is intended to be the formal way for Mach programs + * to provide for backward compatibility in future releases. + * + * [ This needs to be reconciled somehow with the major/minor version + * number stuffed into the version string - mja, 5/8/87 ] + * + * Following is an informal history of the numbers: + * + * 25-March-87 Avadis Tevanian, Jr. + * Created version numbering scheme. Started with major 1, + * minor 0. + */ + +#ifndef _MACH_VERSION_H_ +#define _MACH_VERSION_H_ + +#define KERNEL_MAJOR_VERSION 4 +#define KERNEL_MINOR_VERSION 0 + +/* + * Version number of the kernel include files. + * + * This number must be changed whenever an incompatible change is made to one + * or more of our include files which are used by application programs that + * delve into kernel memory. The number should normally be simply incremented + * but may actually be changed in any manner so long as it differs from the + * numbers previously assigned to any other versions with which the current + * version is incompatible. It is used at boot time to determine which + * versions of the system programs to install. + * + * Note that the symbol _INCLUDE_VERSION must be set to this in the symbol + * table. On the VAX for example, this is done in locore.s. + */ + +/* + * Current allocation strategy: bump either branch by 2, until non-MACH is + * excised from the CSD environment. + */ +#define INCLUDE_VERSION 0 + +#endif /* _MACH_VERSION_H_ */ diff --git a/include/mach/vm_attributes.h b/include/mach/vm_attributes.h new file mode 100644 index 0000000..9ca3ef5 --- /dev/null +++ b/include/mach/vm_attributes.h @@ -0,0 +1,63 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988,1987 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. + */ +/* + * File: mach/vm_attributes.h + * Author: Alessandro Forin + * + * Virtual memory attributes definitions. + * + * These definitions are in addition to the machine-independent + * ones (e.g. protection), and are only selectively supported + * on specific machine architectures. + * + */ + +#ifndef _MACH_VM_ATTRIBUTES_H_ +#define _MACH_VM_ATTRIBUTES_H_ + +/* + * Types of machine-dependent attributes + */ +typedef unsigned int vm_machine_attribute_t; + +#define MATTR_CACHE 1 /* cachability */ +#define MATTR_MIGRATE 2 /* migrability */ +#define MATTR_REPLICATE 4 /* replicability */ + +/* + * Values for the above, e.g. operations on attribute + */ +typedef int vm_machine_attribute_val_t; + +#define MATTR_VAL_OFF 0 /* (generic) turn attribute off */ +#define MATTR_VAL_ON 1 /* (generic) turn attribute on */ +#define MATTR_VAL_GET 2 /* (generic) return current value */ + +#define MATTR_VAL_CACHE_FLUSH 6 /* flush from all caches */ +#define MATTR_VAL_DCACHE_FLUSH 7 /* flush from data caches */ +#define MATTR_VAL_ICACHE_FLUSH 8 /* flush from instruction caches */ + +#endif /* _MACH_VM_ATTRIBUTES_H_ */ diff --git a/include/mach/vm_cache_statistics.h b/include/mach/vm_cache_statistics.h new file mode 100644 index 0000000..072976a --- /dev/null +++ b/include/mach/vm_cache_statistics.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2012 Free Software Foundation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _MACH_VM_CACHE_STATISTICS_H_ +#define _MACH_VM_CACHE_STATISTICS_H_ + +#include + +struct vm_cache_statistics { + integer_t cache_object_count; /* # of cached objects */ + integer_t cache_count; /* # of cached pages */ + integer_t active_tmp_count; /* # of active temporary pages */ + integer_t inactive_tmp_count; /* # of inactive temporary pages */ + integer_t active_perm_count; /* # of active permanent pages */ + integer_t inactive_perm_count; /* # of inactive permanent pages */ + integer_t dirty_count; /* # of dirty pages */ + integer_t laundry_count; /* # of pages being laundered */ + integer_t writeback_count; /* # of pages being written back */ + integer_t slab_count; /* # of slab allocator pages */ + integer_t slab_reclaim_count; /* # of reclaimable slab pages */ +}; + +typedef struct vm_cache_statistics *vm_cache_statistics_t; +typedef struct vm_cache_statistics vm_cache_statistics_data_t; + +#endif /* _MACH_VM_CACHE_STATISTICS_H_ */ diff --git a/include/mach/vm_inherit.h b/include/mach/vm_inherit.h new file mode 100644 index 0000000..2899290 --- /dev/null +++ b/include/mach/vm_inherit.h @@ -0,0 +1,55 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988,1987 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. + */ +/* + * File: mach/vm_inherit.h + * Author: Avadis Tevanian, Jr., Michael Wayne Young + * + * Virtual memory map inheritance definitions. + * + */ + +#ifndef _MACH_VM_INHERIT_H_ +#define _MACH_VM_INHERIT_H_ + +/* + * Types defined: + * + * vm_inherit_t inheritance codes. + */ + +typedef int vm_inherit_t; /* might want to change this */ + +/* + * 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_DEFAULT VM_INHERIT_COPY + +#endif /* _MACH_VM_INHERIT_H_ */ diff --git a/include/mach/vm_param.h b/include/mach/vm_param.h new file mode 100644 index 0000000..4cbd0ec --- /dev/null +++ b/include/mach/vm_param.h @@ -0,0 +1,102 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988,1987 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. + */ +/* + * File: mach/vm_param.h + * Author: Avadis Tevanian, Jr., Michael Wayne Young + * Date: 1985 + * + * Machine independent virtual memory parameters. + * + */ + +#ifndef _MACH_VM_PARAM_H_ +#define _MACH_VM_PARAM_H_ + +#include +#include + +/* + * The machine independent pages are referred to as PAGES. A page + * is some number of hardware pages, depending on the target machine. + * + * All references to the size of a page should be done + * with PAGE_SIZE, PAGE_SHIFT, or PAGE_MASK. + * They may be implemented as either constants or variables, + * depending on more-specific code. + * If they're variables, they had better be initialized + * by the time system-independent code starts getting called. + * + * Regardless whether it is implemented with a constant or a variable, + * the PAGE_SIZE is assumed to be a power of two throughout the + * virtual memory system implementation. + * + * More-specific code must at least provide PAGE_SHIFT; + * we can calculate the others if necessary. + * (However, if PAGE_SHIFT really refers to a variable, + * PAGE_SIZE and PAGE_MASK should also be variables + * so their values don't have to be constantly recomputed.) + */ +#ifndef PAGE_SHIFT +#error mach/machine/vm_param.h needs to define PAGE_SHIFT. +#endif + +#ifndef PAGE_SIZE +#define PAGE_SIZE (1 << PAGE_SHIFT) +#endif + +#ifndef PAGE_MASK +#define PAGE_MASK (PAGE_SIZE-1) +#endif + +/* + * Convert addresses to pages and vice versa. + * No rounding is used. + */ + +#define atop(x) (((vm_size_t)(x)) >> PAGE_SHIFT) +#define ptoa(x) ((vm_offset_t)((x) << PAGE_SHIFT)) + +/* + * Round off or truncate to the nearest page. These will work + * for either addresses or counts. (i.e. 1 byte rounds to 1 page + * bytes. + */ + +#define round_page(x) ((vm_offset_t)((((vm_offset_t)(x)) + PAGE_MASK) & ~PAGE_MASK)) +#define trunc_page(x) ((vm_offset_t)(((vm_offset_t)(x)) & ~PAGE_MASK)) + +#define round_phys(x) ((phys_addr_t)((((phys_addr_t)(x)) + PAGE_MASK) & ~PAGE_MASK)) +#define trunc_phys(x) ((phys_addr_t)(((phys_addr_t)(x)) & ~PAGE_MASK)) + +/* + * Determine whether an address is page-aligned, or a count is + * an exact page multiple. + */ + +#define page_aligned(x) ((((vm_offset_t) (x)) & PAGE_MASK) == 0) +#define phys_aligned(x) ((((phys_addr_t) (x)) & PAGE_MASK) == 0) + +#endif /* _MACH_VM_PARAM_H_ */ diff --git a/include/mach/vm_prot.h b/include/mach/vm_prot.h new file mode 100644 index 0000000..22a76a8 --- /dev/null +++ b/include/mach/vm_prot.h @@ -0,0 +1,79 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988,1987 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. + */ +/* + * File: mach/vm_prot.h + * Author: Avadis Tevanian, Jr., Michael Wayne Young + * + * Virtual memory protection definitions. + * + */ + +#ifndef _MACH_VM_PROT_H_ +#define _MACH_VM_PROT_H_ + +/* + * Types defined: + * + * vm_prot_t VM protection values. + */ + +typedef int 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) + +/* + * The maximum privileges possible, for parameter checking. + */ + +#define VM_PROT_ALL (VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE) + +/* + * An invalid protection value. + * Used only by memory_object_lock_request to indicate no change + * to page locks. Using -1 here is a bad idea because it + * looks like VM_PROT_ALL and then some. + */ +#define VM_PROT_NO_CHANGE ((vm_prot_t) 0x08) + +/* + * This protection value says whether special notification is to be used. + */ +#define VM_PROT_NOTIFY ((vm_prot_t) 0x10) +#endif /* _MACH_VM_PROT_H_ */ diff --git a/include/mach/vm_statistics.h b/include/mach/vm_statistics.h new file mode 100644 index 0000000..2039a82 --- /dev/null +++ b/include/mach/vm_statistics.h @@ -0,0 +1,75 @@ +/* + * Mach Operating System + * Copyright (c) 1993-1987 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. + */ +/* + * File: mach/vm_statistics.h + * Author: Avadis Tevanian, Jr., Michael Wayne Young, David Golub + * + * Virtual memory statistics structure. + * + */ + +#ifndef _MACH_VM_STATISTICS_H_ +#define _MACH_VM_STATISTICS_H_ + +#include + +struct vm_statistics { + integer_t pagesize; /* page size in bytes */ + integer_t free_count; /* # of pages free */ + integer_t active_count; /* # of pages active */ + integer_t inactive_count; /* # of pages inactive */ + integer_t wire_count; /* # of pages wired down */ + integer_t zero_fill_count; /* # of zero fill pages */ + integer_t reactivations; /* # of pages reactivated */ + integer_t pageins; /* # of pageins */ + integer_t pageouts; /* # of pageouts */ + integer_t faults; /* # of faults */ + integer_t cow_faults; /* # of copy-on-writes */ + integer_t lookups; /* object cache lookups */ + integer_t hits; /* object cache hits */ +}; + +typedef struct vm_statistics *vm_statistics_t; +typedef struct vm_statistics vm_statistics_data_t; + +#ifdef MACH_KERNEL +extern vm_statistics_data_t vm_stat; +#endif /* MACH_KERNEL */ + +/* + * 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 { + integer_t resident_count; /* # of pages mapped (total)*/ + integer_t wired_count; /* # of pages wired */ +}; + +typedef struct pmap_statistics *pmap_statistics_t; +#endif /* _MACH_VM_STATISTICS_H_ */ diff --git a/include/mach/vm_sync.h b/include/mach/vm_sync.h new file mode 100644 index 0000000..0c7451c --- /dev/null +++ b/include/mach/vm_sync.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2018 Free Software Foundation + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * All Rights Reserved. + */ + +#ifndef _MACH_VM_SYNC_H_ +#define _MACH_VM_SYNC_H_ + +/* + * Types defined: + * + * vm_sync_t VM synchronization flags + */ + +typedef int vm_sync_t; + +/* + * Synchronization values + */ + +#define VM_SYNC_ASYNCHRONOUS ((vm_sync_t) 0x01) +#define VM_SYNC_SYNCHRONOUS ((vm_sync_t) 0x02) +#define VM_SYNC_INVALIDATE ((vm_sync_t) 0x04) +#if 0 +/* Not supported yet. */ +#define VM_SYNC_KILLPAGES ((vm_sync_t) 0x08) +#define VM_SYNC_DEACTIVATE ((vm_sync_t) 0x10) +#define VM_SYNC_CONTIGUOUS ((vm_sync_t) 0x20) +#define VM_SYNC_REUSABLEPAGES ((vm_sync_t) 0x40) +#endif + +#endif /* _MACH_VM_SYNC_H_ */ diff --git a/include/mach/vm_wire.h b/include/mach/vm_wire.h new file mode 100644 index 0000000..1552dfa --- /dev/null +++ b/include/mach/vm_wire.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2017 Free Software Foundation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _MACH_VM_WIRE_H_ +#define _MACH_VM_WIRE_H_ + +typedef int vm_wire_t; + +#define VM_WIRE_NONE 0 +#define VM_WIRE_CURRENT 1 +#define VM_WIRE_FUTURE 2 + +#define VM_WIRE_ALL (VM_WIRE_CURRENT | VM_WIRE_FUTURE) + +#endif /* _MACH_VM_WIRE_H_ */ diff --git a/include/mach/xen.h b/include/mach/xen.h new file mode 100644 index 0000000..4462082 --- /dev/null +++ b/include/mach/xen.h @@ -0,0 +1,95 @@ + +/* + * Copyright (C) 2006-2009, 2011 Free Software Foundation + * + * This program is free software ; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation ; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY ; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the program ; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _MACH_XEN_H +#define _MACH_XEN_H +#ifdef MACH_XEN +#include +#include +#include + +extern struct start_info boot_info; + +extern volatile struct shared_info hyp_shared_info; + +#ifdef MACH_PV_PAGETABLES +/* Memory translations */ + +/* pa are physical addresses, from 0 to size of memory */ +/* ma are machine addresses, i.e. _real_ hardware adresses */ +/* la are linear addresses, i.e. without segmentation */ + +/* This might also be useful out of Xen */ +#if VM_MIN_KERNEL_ADDRESS != LINEAR_MIN_KERNEL_ADDRESS +extern unsigned long la_shift; +#else +#define la_shift LINEAR_MIN_KERNEL_ADDRESS +#endif +#define la_to_pa(a) ((vm_offset_t)(((vm_offset_t)(a)) - la_shift)) +#define pa_to_la(a) ((vm_offset_t)(((vm_offset_t)(a)) + la_shift)) + +#define kv_to_la(a) pa_to_la(_kvtophys(a)) +#define la_to_kv(a) phystokv(la_to_pa(a)) + +#ifdef MACH_PSEUDO_PHYS +#ifdef __i386__ +#if PAE +#define PFN_LIST MACH2PHYS_VIRT_START_PAE +#else +#define PFN_LIST MACH2PHYS_VIRT_START_NONPAE +#endif +#else +#define PFN_LIST MACH2PHYS_VIRT_START +#endif +#if VM_MIN_KERNEL_ADDRESS != LINEAR_MIN_KERNEL_ADDRESS +extern unsigned long *pfn_list; +#else +#define pfn_list ((unsigned long *) PFN_LIST) +#endif +#define mfn_to_pfn(n) (pfn_list[n]) + +extern unsigned long *mfn_list; +#define pfn_to_mfn(n) (mfn_list[n]) +#else +#define mfn_to_pfn(n) (n) +#define pfn_to_mfn(n) (n) +#endif /* MACH_PSEUDO_PHYS */ + +#define pa_to_mfn(a) (pfn_to_mfn(atop(a))) +#ifdef PAE +#define pa_to_ma(a) ({ vm_offset_t __a = (vm_offset_t) (a); (((pt_entry_t) pa_to_mfn(__a)) << PAGE_SHIFT) | (__a & PAGE_MASK); }) +#define ma_to_pa(a) ({ pt_entry_t __a = (pt_entry_t) (a); (mfn_to_pfn(__a >> PAGE_SHIFT) << PAGE_SHIFT) | (__a & PAGE_MASK); }) +#else +#define pa_to_ma(a) ({ vm_offset_t __a = (vm_offset_t) (a); ptoa(pa_to_mfn(__a)) | (__a & PAGE_MASK); }) +#define ma_to_pa(a) ({ vm_offset_t __a = (vm_offset_t) (a); (mfn_to_pfn(atop((__a))) << PAGE_SHIFT) | (__a & PAGE_MASK); }) +#endif + +#define kv_to_mfn(a) pa_to_mfn(_kvtophys(a)) +#define kv_to_ma(a) pa_to_ma(_kvtophys(a)) +#else /* MACH_PV_PAGETABLES */ +#define mfn_to_pfn(n) (n) +#define pfn_to_mfn(n) (n) +#endif /* MACH_PV_PAGETABLES */ + +#define mfn_to_kv(mfn) phystokv(ptoa(mfn_to_pfn(mfn))) + +#include + +#endif /* MACH_XEN */ +#endif /* _MACH_XEN_H */ diff --git a/include/mach_debug/hash_info.h b/include/mach_debug/hash_info.h new file mode 100644 index 0000000..8e6f19c --- /dev/null +++ b/include/mach_debug/hash_info.h @@ -0,0 +1,41 @@ +/* + * 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. + */ + +#ifndef _MACH_DEBUG_HASH_INFO_H_ +#define _MACH_DEBUG_HASH_INFO_H_ + +/* + * Remember to update the mig type definitions + * in mach_debug_types.defs when adding/removing fields. + */ + +typedef struct hash_info_bucket { + unsigned int hib_count; /* number of records in bucket */ +} hash_info_bucket_t; + +typedef hash_info_bucket_t *hash_info_bucket_array_t; + +#endif /* _MACH_DEBUG_HASH_INFO_H_ */ diff --git a/include/mach_debug/mach_debug.defs b/include/mach_debug/mach_debug.defs new file mode 100644 index 0000000..2de7df5 --- /dev/null +++ b/include/mach_debug/mach_debug.defs @@ -0,0 +1,228 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988 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. + */ +/* + * Matchmaker definitions file for Mach kernel debugging interface. + */ + +subsystem +#if KERNEL_SERVER + KernelServer +#endif /* KERNEL_SERVER */ + mach_debug 3000; + +#include +#include +#include + +skip; /* host_ipc_statistics */ +skip; /* host_ipc_statistics_reset */ +skip; /* host_callout_info */ +skip; /* host_callout_statistics */ +skip; /* host_callout_statistics_reset */ +skip; /* host_zone_info */ +skip; /* host_ipc_bucket_info */ + +#if !defined(MACH_IPC_DEBUG) || MACH_IPC_DEBUG + +/* + * Returns the exact number of extant send rights + * for the given receive right. + */ + +routine mach_port_get_srights( + task : ipc_space_t; + name : mach_port_name_t; + out srights : mach_port_rights_t); + +skip; /* host_ipc_hash_info */ + +/* + * Returns information about the marequest hash table. + */ + +routine host_ipc_marequest_info( + host : host_t; + out max_requests : unsigned; + out info : hash_info_bucket_array_t, + CountInOut, Dealloc); + +skip; /* mach_port_space_info */ + +/* + * Returns information about the dead-name requests + * registered with the named receive right. + */ + +routine mach_port_dnrequest_info( + task : ipc_space_t; + name : mach_port_name_t; + out total : unsigned; /* total size of table */ + out used : unsigned); /* amount used */ + +#else /* !defined(MACH_IPC_DEBUG) || MACH_IPC_DEBUG */ +skip; /* mach_port_get_srights */ +skip; /* host_ipc_hash_info */ +skip; /* host_ipc_marequest_info */ +skip; /* mach_port_space_info */ +skip; /* mach_port_dnrequest_info */ +#endif /* !defined(MACH_IPC_DEBUG) || MACH_IPC_DEBUG */ + +skip; /* mach_vm_region_info */ +skip; /* vm_mapped_pages_info */ + +/* + * Returns stack usage information: + * reserved Amount of stack space reserved for pcb. + * total Number of stacks. + * space Total VM space for stacks. + * resident Resident VM space for stacks. + * maxusage Maximum amount of stack used. + * maxstack Address in the kernel of the largest stack. + */ + +routine host_stack_usage( + host : host_t; + out reserved : vm_size_t; + out total : unsigned; + out space : vm_size_t; + out resident : vm_size_t; + out maxusage : vm_size_t; + out maxstack : vm_offset_t); + +routine processor_set_stack_usage( + pset : processor_set_name_t; + out total : unsigned; + out space : vm_size_t; + out resident : vm_size_t; + out maxusage : vm_size_t; + out maxstack : vm_offset_t); + +#if !defined(MACH_VM_DEBUG) || MACH_VM_DEBUG + +/* + * Returns information about the global VP table. + */ + +routine host_virtual_physical_table_info( + host : host_t; + out info : hash_info_bucket_array_t, + CountInOut, Dealloc); + +#else /* !defined(MACH_VM_DEBUG) || MACH_VM_DEBUG */ +skip; /* host_virtual_physical_table_info */ +#endif /* !defined(MACH_VM_DEBUG) || MACH_VM_DEBUG */ + +/* The old host_load_symbol_table with a different ABI for symtab_name_t */ +skip; + +#if !defined(MACH_IPC_DEBUG) || MACH_IPC_DEBUG + +/* + * Return the type and address of the kernel object + * that the given send/receive right represents. + */ + +routine mach_port_kernel_object( + task : ipc_space_t; + name : mach_port_name_t; + out object_type : unsigned; + out object_addr : vm_offset_t); + +#else /* !defined(MACH_IPC_DEBUG) || MACH_IPC_DEBUG */ +skip; /* mach_port_kernel_object */ +#endif /* !defined(MACH_IPC_DEBUG) || MACH_IPC_DEBUG */ + +#if !defined(MACH_VM_DEBUG) || MACH_VM_DEBUG + +/* + * Returns information about a region of memory. + */ + +routine mach_vm_region_info( + task : vm_task_t; + address : vm_address_t; + out region : vm_region_info_t; + /* avoid out-translation of the argument */ + out object : memory_object_name_t = + MACH_MSG_TYPE_MOVE_SEND + ctype: mach_port_t); + +routine mach_vm_object_info( + object : memory_object_name_t; + out info : vm_object_info_t; + /* avoid out-translation of the argument */ + out shadow : memory_object_name_t = + MACH_MSG_TYPE_MOVE_SEND + ctype: mach_port_t; + /* avoid out-translation of the argument */ + out copy : memory_object_name_t = + MACH_MSG_TYPE_MOVE_SEND + ctype: mach_port_t); + +routine mach_vm_object_pages( + object : memory_object_name_t; + out pages : vm_page_info_array_t, + CountInOut, Dealloc); + +#else /* !defined(MACH_VM_DEBUG) || MACH_VM_DEBUG */ +skip; /* mach_vm_region_info */ +skip; /* mach_vm_object_info */ +skip; /* mach_vm_object_pages */ +#endif /* !defined(MACH_VM_DEBUG) || MACH_VM_DEBUG */ + +/* + * Returns information about the memory allocation caches. + */ +routine host_slab_info( + host : host_t; + out info : cache_info_array_t, + CountInOut, Dealloc); + +#if !defined(MACH_KDB) || MACH_KDB +/* + * Loads a symbol table for an external file into the kernel debugger. + * The symbol table data is an array of characters. It is assumed that + * the caller and the kernel debugger agree on its format. + */ + +routine host_load_symbol_table( + host : host_priv_t; + task : task_t; + name : symtab_name_t; + symtab : pointer_t); + +#else /* !defined(MACH_KDB) || MACH_KDB */ +skip; /* host_load_symbol_table */ +#endif /* !defined(MACH_KDB) || MACH_KDB */ + +#if !defined(MACH_VM_DEBUG) || MACH_VM_DEBUG +routine mach_vm_object_pages_phys( + object : memory_object_name_t; + out pages : vm_page_phys_info_array_t, + CountInOut, Dealloc); +#else /* !defined(MACH_VM_DEBUG) || MACH_VM_DEBUG */ +skip; /* mach_vm_object_pages_phys */ +#endif /* !defined(MACH_VM_DEBUG) || MACH_VM_DEBUG */ diff --git a/include/mach_debug/mach_debug_types.defs b/include/mach_debug/mach_debug_types.defs new file mode 100644 index 0000000..d897380 --- /dev/null +++ b/include/mach_debug/mach_debug_types.defs @@ -0,0 +1,121 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988 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. + */ +/* + * Mach kernel debugging interface type declarations + */ + +#ifndef _MACH_DEBUG_MACH_DEBUG_TYPES_DEFS_ +#define _MACH_DEBUG_MACH_DEBUG_TYPES_DEFS_ + +#include + +#define CACHE_NAME_MAX_LEN 32 +type cache_name_t = struct[CACHE_NAME_MAX_LEN] of char; +#undef CACHE_NAME_MAX_LEN +type cache_info_t = struct { + integer_t flags; + rpc_vm_size_t cpu_pool_size; + rpc_vm_size_t obj_size; + rpc_vm_size_t align; + rpc_vm_size_t buf_size; + rpc_vm_size_t slab_size; + rpc_long_natural_t bufs_per_slab; + rpc_long_natural_t nr_objs; + rpc_long_natural_t nr_bufs; + rpc_long_natural_t nr_slabs; + rpc_long_natural_t nr_free_slabs; + cache_name_t name; +}; +type cache_info_array_t = array[] of cache_info_t; + +type hash_info_bucket_t = struct { + unsigned hib_count; +}; +type hash_info_bucket_array_t = array[] of hash_info_bucket_t; + +type vm_region_info_t = struct { + rpc_vm_offset_t vri_start; + rpc_vm_offset_t vri_end; + vm_prot_t vri_protection; + vm_prot_t vri_max_protection; + vm_inherit_t vri_inheritance; + unsigned vri_wired_count; + unsigned vri_user_wired_count; + rpc_vm_offset_t vri_object; + rpc_vm_offset_t vri_offset; + integer_t vri_needs_copy; + unsigned vri_sharing; +}; +type vm_region_info_array_t = array[] of vm_region_info_t; + +type vm_object_info_state_t = uint32_t; +type vm_object_info_t = struct { + rpc_vm_offset_t voi_object; + rpc_vm_size_t voi_pagesize; + rpc_vm_size_t voi_size; + unsigned voi_ref_count; + unsigned voi_resident_page_count; + unsigned voi_absent_count; + rpc_vm_offset_t voi_copy; + rpc_vm_offset_t voi_shadow; + rpc_vm_offset_t voi_shadow_offset; + rpc_vm_offset_t voi_paging_offset; + memory_object_copy_strategy_t voi_copy_strategy; + rpc_vm_offset_t voi_last_alloc; + unsigned voi_paging_in_progress; + vm_object_info_state_t voi_state; +}; +type vm_object_info_array_t = array[] of vm_object_info_t; + +type vm_page_info_state_t = uint32_t; + +type vm_page_info_t = struct { + rpc_vm_offset_t vpi_offset; + rpc_vm_offset_t vpi_phys_addr; + unsigned vpi_wire_count; + vm_prot_t vpi_page_lock; + vm_prot_t vpi_unlock_request; + vm_page_info_state_t vpi_state; +}; +type vm_page_info_array_t = array[] of vm_page_info_t; + +type vm_page_phys_info_t = struct { + rpc_vm_offset_t vpi_offset; + rpc_phys_addr_t vpi_phys_addr; + unsigned vpi_wire_count; + vm_prot_t vpi_page_lock; + vm_prot_t vpi_unlock_request; + vm_page_info_state_t vpi_state; +}; +type vm_page_phys_info_array_t = array[] of vm_page_phys_info_t; + +type symtab_name_t = c_string[32]; + +type kernel_debug_name_t = c_string[*: 64]; + +import ; + +#endif /* _MACH_DEBUG_MACH_DEBUG_TYPES_DEFS_ */ diff --git a/include/mach_debug/mach_debug_types.h b/include/mach_debug/mach_debug_types.h new file mode 100644 index 0000000..98124ad --- /dev/null +++ b/include/mach_debug/mach_debug_types.h @@ -0,0 +1,52 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988 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. + */ +/* + * Mach kernel debugging interface type declarations + */ + +#ifndef _MACH_DEBUG_MACH_DEBUG_TYPES_H_ +#define _MACH_DEBUG_MACH_DEBUG_TYPES_H_ + +#include +#include +#include + +typedef char symtab_name_t[32]; +typedef const char *const_symtab_name_t; + +/* + * A fixed-length string data type intended for names given to + * kernel objects. + * + * Note that it is not guaranteed that the in-kernel data + * structure will hold KERNEL_DEBUG_NAME_MAX bytes. The given + * name will be truncated to fit into the target data structure. + */ +#define KERNEL_DEBUG_NAME_MAX (64) +typedef char kernel_debug_name_t[KERNEL_DEBUG_NAME_MAX]; +typedef const char *const_kernel_debug_name_t; + +#endif /* _MACH_DEBUG_MACH_DEBUG_TYPES_H_ */ diff --git a/include/mach_debug/slab_info.h b/include/mach_debug/slab_info.h new file mode 100644 index 0000000..0f6b5a2 --- /dev/null +++ b/include/mach_debug/slab_info.h @@ -0,0 +1,56 @@ +/* + * 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. + */ + +#ifndef _MACH_DEBUG_SLAB_INFO_H_ +#define _MACH_DEBUG_SLAB_INFO_H_ + +#include + +/* + * Remember to update the mig type definitions + * in mach_debug_types.defs when adding/removing fields. + */ + +#define CACHE_NAME_MAX_LEN 32 + +typedef struct cache_info { + int flags; + rpc_vm_size_t cpu_pool_size; + rpc_vm_size_t obj_size; + rpc_vm_size_t align; + rpc_vm_size_t buf_size; + rpc_vm_size_t slab_size; + rpc_long_natural_t bufs_per_slab; + rpc_long_natural_t nr_objs; + rpc_long_natural_t nr_bufs; + rpc_long_natural_t nr_slabs; + rpc_long_natural_t nr_free_slabs; + char name[CACHE_NAME_MAX_LEN]; +} cache_info_t; + +typedef cache_info_t *cache_info_array_t; + +#endif /* _MACH_DEBUG_SLAB_INFO_H_ */ diff --git a/include/mach_debug/vm_info.h b/include/mach_debug/vm_info.h new file mode 100644 index 0000000..cf45a2c --- /dev/null +++ b/include/mach_debug/vm_info.h @@ -0,0 +1,143 @@ +/* + * 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. + */ +/* + * File: mach_debug/vm_info.h + * Author: Rich Draves + * Date: March, 1990 + * + * Definitions for the VM debugging interface. + */ + +#ifndef _MACH_DEBUG_VM_INFO_H_ +#define _MACH_DEBUG_VM_INFO_H_ + +#include +#include +#include +#include +#include +#include + +/* + * Remember to update the mig type definitions + * in mach_debug_types.defs when adding/removing fields. + */ + +typedef struct vm_region_info { + rpc_vm_offset_t vri_start; /* start of region */ + rpc_vm_offset_t vri_end; /* end of region */ + + vm_prot_t vri_protection; /* protection code */ + vm_prot_t vri_max_protection; /* maximum protection */ + vm_inherit_t vri_inheritance; /* inheritance */ + unsigned int vri_wired_count; /* number of times wired */ + unsigned int vri_user_wired_count; /* number of times user has wired */ + + rpc_vm_offset_t vri_object; /* the mapped object */ + rpc_vm_offset_t vri_offset; /* offset into object */ +/*boolean_t*/integer_t vri_needs_copy; /* does object need to be copied? */ + unsigned int vri_sharing; /* share map references */ +} vm_region_info_t; + +typedef vm_region_info_t *vm_region_info_array_t; + + +typedef uint32_t vm_object_info_state_t; + +#define VOI_STATE_PAGER_CREATED 0x00000001 +#define VOI_STATE_PAGER_INITIALIZED 0x00000002 +#define VOI_STATE_PAGER_READY 0x00000004 +#define VOI_STATE_CAN_PERSIST 0x00000008 +#define VOI_STATE_INTERNAL 0x00000010 +#define VOI_STATE_TEMPORARY 0x00000020 +#define VOI_STATE_ALIVE 0x00000040 +#define VOI_STATE_LOCK_IN_PROGRESS 0x00000080 +#define VOI_STATE_LOCK_RESTART 0x00000100 + +typedef struct vm_object_info { + rpc_vm_offset_t voi_object; /* this object */ + rpc_vm_size_t voi_pagesize; /* object's page size */ + rpc_vm_size_t voi_size; /* object size (valid if internal) */ + unsigned int voi_ref_count; /* number of references */ + unsigned int voi_resident_page_count; /* number of resident pages */ + unsigned int voi_absent_count; /* number requested but not filled */ + rpc_vm_offset_t voi_copy; /* copy object */ + rpc_vm_offset_t voi_shadow; /* shadow object */ + rpc_vm_offset_t voi_shadow_offset; /* offset into shadow object */ + rpc_vm_offset_t voi_paging_offset; /* offset into memory object */ + memory_object_copy_strategy_t voi_copy_strategy; + /* how to handle data copy */ + rpc_vm_offset_t voi_last_alloc; /* offset of last allocation */ + unsigned int voi_paging_in_progress; /* paging references */ + vm_object_info_state_t voi_state; /* random state bits */ +} vm_object_info_t; + +typedef vm_object_info_t *vm_object_info_array_t; + +typedef uint32_t vm_page_info_state_t; + +#define VPI_STATE_BUSY 0x00000001 +#define VPI_STATE_WANTED 0x00000002 +#define VPI_STATE_TABLED 0x00000004 +#define VPI_STATE_FICTITIOUS 0x00000008 +#define VPI_STATE_PRIVATE 0x00000010 +#define VPI_STATE_ABSENT 0x00000020 +#define VPI_STATE_ERROR 0x00000040 +#define VPI_STATE_DIRTY 0x00000080 +#define VPI_STATE_PRECIOUS 0x00000100 +#define VPI_STATE_OVERWRITING 0x00000200 +#define VPI_STATE_INACTIVE 0x00000400 +#define VPI_STATE_ACTIVE 0x00000800 +#define VPI_STATE_LAUNDRY 0x00001000 +#define VPI_STATE_FREE 0x00002000 +#define VPI_STATE_REFERENCE 0x00004000 + +#define VPI_STATE_PAGER 0x80000000 /* pager has the page */ + +/* XXX: This structure holds a 32bit vpi_phys_addr. */ +typedef struct vm_page_info { + rpc_vm_offset_t vpi_offset; /* offset in object */ + rpc_vm_offset_t vpi_phys_addr; /* physical address */ + unsigned int vpi_wire_count; /* number of times wired */ + vm_prot_t vpi_page_lock; /* XP access restrictions */ + vm_prot_t vpi_unlock_request; /* outstanding unlock requests */ + vm_page_info_state_t vpi_state; /* random state bits */ +} vm_page_info_t; + +typedef vm_page_info_t *vm_page_info_array_t; + +typedef struct vm_page_phys_info { + rpc_vm_offset_t vpi_offset; /* offset in object */ + rpc_phys_addr_t vpi_phys_addr; /* physical address */ + unsigned int vpi_wire_count; /* number of times wired */ + vm_prot_t vpi_page_lock; /* XP access restrictions */ + vm_prot_t vpi_unlock_request; /* outstanding unlock requests */ + vm_page_info_state_t vpi_state; /* random state bits */ +} vm_page_phys_info_t; + +typedef vm_page_phys_info_t *vm_page_phys_info_array_t; + +#endif /* _MACH_DEBUG_VM_INFO_H_ */ diff --git a/include/string.h b/include/string.h new file mode 100644 index 0000000..91c5fe4 --- /dev/null +++ b/include/string.h @@ -0,0 +1,55 @@ +/* + * String Handling Functions. + * Copyright (C) 2006 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Barry deFreese. + */ +/* + * String handling functions. + * + */ + +#ifndef _MACH_SA_SYS_STRING_H_ +#define _MACH_SA_SYS_STRING_H_ + +#include + +extern void *memcpy (void *dest, const void *src, size_t n); + +extern void *memmove (void *dest, const void *src, size_t n); + +extern int memcmp (const void *s1, const void *s2, size_t n) __attribute__ ((pure)); + +extern void *memset (void *s, int c, size_t n); + +extern char *strchr (const char *s, int c); + +extern char *strcpy (char *dest, const char *src); + +extern char *strncpy (char *dest, const char *src, size_t n); + +extern char *strsep (char **strp, const char *delim); + +extern int strcmp (const char *s1, const char *s2) __attribute__ ((pure)); + +extern int strncmp (const char *s1, const char *s2, size_t n) __attribute__ ((pure)); + +extern size_t strlen (const char *s) __attribute__ ((pure)); + +extern char *strstr(const char *haystack, const char *needle); + +#endif /* _MACH_SA_SYS_STRING_H_ */ diff --git a/include/sys/reboot.h b/include/sys/reboot.h new file mode 100644 index 0000000..21d421a --- /dev/null +++ b/include/sys/reboot.h @@ -0,0 +1,135 @@ +/* + * Mach Operating System + * Copyright (c) 1993,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. + */ +/* + * Copyright (c) 1982, 1986, 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. + * 4. The name of the Laboratory may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 7.5 (Berkeley) 6/27/88 + */ +/* + * Warning: The contents of this file are deprecated; + * it should only ever be used for BSD and Mach 3 compatibility. + * As the above copyright notice suggests, this file originated in BSD; + * it is mostly the same, except the flags after RB_DFLTROOT + * have diverged from BSD. + */ +#ifndef _MACH_SYS_REBOOT_H_ +#define _MACH_SYS_REBOOT_H_ + +/* + * Arguments to reboot system call. + * These are converted to switches, and passed to startup program, + * and on to init. + */ +#define RB_AUTOBOOT 0 /* flags for system auto-booting itself */ + +#define RB_ASKNAME 0x01 /* -a: ask for file name to reboot from */ +#define RB_SINGLE 0x02 /* -s: reboot to single user only */ +#define RB_KDB 0x04 /* -d: kernel debugger symbols loaded */ +#define RB_HALT 0x08 /* -h: enter KDB at bootup */ + /* for host_reboot(): don't reboot, + just halt */ +#define RB_INITNAME 0x10 /* -i: name given for /etc/init (unused) */ +#define RB_DFLTROOT 0x20 /* use compiled-in rootdev */ +#define RB_NOBOOTRC 0x20 /* -b: don't run /etc/rc.boot */ +#define RB_ALTBOOT 0x40 /* use /boot.old vs /boot */ +#define RB_UNIPROC 0x80 /* -u: start only one processor */ + +#define RB_SHIFT 8 /* second byte is for ux */ + +#define RB_DEBUGGER 0x1000 /* for host_reboot(): enter kernel + debugger from user level */ + +/* Corresponding BSD definitions, where they disagree with the Mach flags. */ +#define BSD_RB_NOSYNC 0x04 /* dont sync before reboot */ +#define BSD_RB_KDB 0x40 /* give control to kernel debugger */ +#define BSD_RB_RDONLY 0x80 /* mount root fs read-only */ +#define BSD_RB_DUMP 0x100 /* dump kernel memory before reboot */ +#define BSD_RB_MINIROOT 0x200 /* mini-root present in memory at boot time */ +#define BSD_RB_CONFIG 0x400 /* invoke user configuration routing */ + + +/* + * 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_int)0xf0000000U) +#define B_DEVMAGIC ((u_int)0xa0000000U) + +#define MAKEBOOTDEV(type, adaptor, controller, unit, partition) \ + (((type) << B_TYPESHIFT) | ((adaptor) << B_ADAPTORSHIFT) | \ + ((controller) << B_CONTROLLERSHIFT) | ((unit) << B_UNITSHIFT) | \ + ((partition) << B_PARTITIONSHIFT) | B_DEVMAGIC) + +#endif /* _MACH_SYS_REBOOT_H_ */ diff --git a/include/sys/types.h b/include/sys/types.h new file mode 100644 index 0000000..8d5af37 --- /dev/null +++ b/include/sys/types.h @@ -0,0 +1,88 @@ +/* + * Mach Operating System + * Copyright (c) 1993 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. + */ +#ifndef _MACH_SA_SYS_TYPES_H_ +#define _MACH_SA_SYS_TYPES_H_ + +#include +#include + +#ifndef _SIZE_T +#define _SIZE_T +typedef unsigned long size_t; +#endif + +#ifndef _SSIZE_T +#define _SSIZE_T +typedef integer_t ssize_t; +#endif + +typedef unsigned short dev_t; /* device id */ +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 natural_t off_t; /* file offset */ +typedef unsigned long uid_t; /* user id */ + + +/* Symbols allowed but not required by POSIX */ + +typedef char * caddr_t; /* address of a (signed) char */ + +#ifndef _TIME_T +#define _TIME_T +typedef unsigned long long time_t; +#endif + +#define RAND_MAX 0x7fffffff + +/* Symbols not allowed by POSIX */ +#ifndef _POSIX_SOURCE + +/* + * Common type definitions that lots of old files seem to want. + */ + +typedef unsigned char u_char; /* unsigned char */ +typedef unsigned short u_short; /* unsigned short */ +typedef unsigned int u_int; /* unsigned int */ +typedef unsigned long u_long; /* unsigned long */ + +typedef unsigned int daddr_t; /* disk address */ + +#define major(i) (((i) >> 8) & 0xFF) +#define minor(i) ((i) & 0xFF) +#define makedev(i,j) ((((i) & 0xFF) << 8) | ((j) & 0xFF)) + +#define NBBY 8 + +#ifndef NULL +#define NULL ((void *) 0) /* the null pointer */ +#endif + +#endif /* _POSIX_SOURCE */ + +#endif /* _MACH_SA_SYS_TYPES_H_ */ diff --git a/ipc/.gitignore b/ipc/.gitignore new file mode 100644 index 0000000..b750932 --- /dev/null +++ b/ipc/.gitignore @@ -0,0 +1,2 @@ +notify.none.defs.c +notify.none.msgids diff --git a/ipc/ipc_entry.c b/ipc/ipc_entry.c new file mode 100644 index 0000000..f13c442 --- /dev/null +++ b/ipc/ipc_entry.c @@ -0,0 +1,214 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University. + * Copyright (c) 1993,1994 The University of Utah and + * the Computer Systems Laboratory (CSL). + * 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, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF + * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM 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. + */ +/* + * File: ipc/ipc_entry.c + * Author: Rich Draves + * Date: 1989 + * + * Primitive functions to manipulate translation entries. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct kmem_cache ipc_entry_cache; + +/* + * Routine: ipc_entry_alloc + * Purpose: + * Allocate an entry out of the space. + * Conditions: + * The space must be write-locked. May allocate memory. + * Returns: + * KERN_SUCCESS An entry was allocated. + * KERN_INVALID_TASK The space is dead. + * KERN_NO_SPACE No room for an entry in the space. + * KERN_RESOURCE_SHORTAGE Couldn't allocate memory for an entry. + */ + +kern_return_t +ipc_entry_alloc( + ipc_space_t space, + mach_port_name_t *namep, + ipc_entry_t *entryp) +{ + kern_return_t kr; + ipc_entry_t entry; + rdxtree_key_t key; + + if (!space->is_active) { + return KERN_INVALID_TASK; + } + + kr = ipc_entry_get(space, namep, entryp); + if (kr == KERN_SUCCESS) + return kr; + + entry = ie_alloc(); + if (entry == IE_NULL) { + return KERN_RESOURCE_SHORTAGE; + } + + kr = rdxtree_insert_alloc(&space->is_map, entry, &key); + if (kr) { + ie_free(entry); + return kr; + } + space->is_size += 1; + + entry->ie_bits = 0; + entry->ie_object = IO_NULL; + entry->ie_request = 0; + entry->ie_name = (mach_port_name_t) key; + + *entryp = entry; + *namep = (mach_port_name_t) key; + return KERN_SUCCESS; +} + +/* + * Routine: ipc_entry_alloc_name + * Purpose: + * Allocates/finds an entry with a specific name. + * If an existing entry is returned, its type will be nonzero. + * Conditions: + * The space must be write-locked. May allocate memory. + * Returns: + * KERN_SUCCESS Found existing entry with same name. + * KERN_SUCCESS Allocated a new entry. + * KERN_INVALID_TASK The space is dead. + * KERN_RESOURCE_SHORTAGE Couldn't allocate memory. + */ + +kern_return_t +ipc_entry_alloc_name( + ipc_space_t space, + mach_port_name_t name, + ipc_entry_t *entryp) +{ + kern_return_t kr; + ipc_entry_t entry, e, *prevp; + void **slot; + assert(MACH_PORT_NAME_VALID(name)); + + if (!space->is_active) { + return KERN_INVALID_TASK; + } + + slot = rdxtree_lookup_slot(&space->is_map, (rdxtree_key_t) name); + if (slot != NULL) + entry = *(ipc_entry_t *) slot; + + if (slot == NULL || entry == IE_NULL) { + entry = ie_alloc(); + if (entry == IE_NULL) { + return KERN_RESOURCE_SHORTAGE; + } + + entry->ie_bits = 0; + entry->ie_object = IO_NULL; + entry->ie_request = 0; + entry->ie_name = name; + + if (slot != NULL) + rdxtree_replace_slot(slot, entry); + else { + kr = rdxtree_insert(&space->is_map, + (rdxtree_key_t) name, entry); + if (kr != KERN_SUCCESS) { + ie_free(entry); + return kr; + } + } + space->is_size += 1; + + *entryp = entry; + return KERN_SUCCESS; + } + + if (IE_BITS_TYPE(entry->ie_bits)) { + /* Used entry. */ + *entryp = entry; + return KERN_SUCCESS; + } + + /* Free entry. Rip the entry out of the free list. */ + for (prevp = &space->is_free_list, e = space->is_free_list; + e != entry; + ({ prevp = &e->ie_next_free; e = e->ie_next_free; })) + continue; + + *prevp = entry->ie_next_free; + space->is_free_list_size -= 1; + + entry->ie_bits = 0; + assert(entry->ie_object == IO_NULL); + assert(entry->ie_name == name); + entry->ie_request = 0; + + space->is_size += 1; + *entryp = entry; + return KERN_SUCCESS; +} + +#if MACH_KDB +#include +#include + +#define printf kdbprintf + +ipc_entry_t +db_ipc_object_by_name( + const task_t task, + mach_port_name_t name) +{ + ipc_space_t space = task->itk_space; + ipc_entry_t entry; + + entry = ipc_entry_lookup(space, name); + if(entry != IE_NULL) { + iprintf("(task 0x%x, name 0x%x) ==> object 0x%x", + entry->ie_object); + return (ipc_entry_t) entry->ie_object; + } + return entry; +} +#endif /* MACH_KDB */ diff --git a/ipc/ipc_entry.h b/ipc/ipc_entry.h new file mode 100644 index 0000000..9f7b593 --- /dev/null +++ b/ipc/ipc_entry.h @@ -0,0 +1,110 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University. + * Copyright (c) 1993,1994 The University of Utah and + * the Computer Systems Laboratory (CSL). + * 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, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF + * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM 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. + */ +/* + * File: ipc/ipc_entry.h + * Author: Rich Draves + * Date: 1989 + * + * Definitions for translation entries, which represent + * tasks' capabilities for ports and port sets. + */ + +#ifndef _IPC_IPC_ENTRY_H_ +#define _IPC_IPC_ENTRY_H_ + +#include +#include +#include +#include +#include +#include +#include + +/* + * Spaces hold capabilities for ipc_object_t's (ports and port sets). + * Each ipc_entry_t records a capability. + */ + +typedef unsigned int ipc_entry_bits_t; +typedef ipc_table_elems_t ipc_entry_num_t; /* number of entries */ + +typedef struct ipc_entry { + mach_port_name_t ie_name; + ipc_entry_bits_t ie_bits; + struct ipc_object *ie_object; + union { + struct ipc_entry *next_free; + /*XXX ipc_port_request_index_t request;*/ + unsigned int request; + } index; +} *ipc_entry_t; + +#define IE_NULL ((ipc_entry_t) 0) + +#define ie_request index.request +#define ie_next_free index.next_free + +#define IE_BITS_UREFS_MASK 0x0000ffff /* 16 bits of user-reference */ +#define IE_BITS_UREFS(bits) ((bits) & IE_BITS_UREFS_MASK) + +#define IE_BITS_TYPE_MASK 0x001f0000 /* 5 bits of capability type */ +#define IE_BITS_TYPE(bits) ((bits) & IE_BITS_TYPE_MASK) + +#define IE_BITS_MAREQUEST 0x00200000 /* 1 bit for msg-accepted */ + +#define IE_BITS_RIGHT_MASK 0x003fffff /* relevant to the right */ + +#if PORT_GENERATIONS +#error "not supported" +#define IE_BITS_GEN_MASK 0xff000000U /* 8 bits for generation */ +#define IE_BITS_GEN(bits) ((bits) & IE_BITS_GEN_MASK) +#define IE_BITS_GEN_ONE 0x01000000 /* low bit of generation */ +#else +#define IE_BITS_GEN_MASK 0 +#define IE_BITS_GEN(bits) 0 +#define IE_BITS_GEN_ONE 0 +#endif + + +extern struct kmem_cache ipc_entry_cache; +#define ie_alloc() ((ipc_entry_t) kmem_cache_alloc(&ipc_entry_cache)) +#define ie_free(e) kmem_cache_free(&ipc_entry_cache, (vm_offset_t) (e)) + +extern kern_return_t +ipc_entry_alloc(ipc_space_t space, mach_port_name_t *namep, ipc_entry_t *entryp); + +extern kern_return_t +ipc_entry_alloc_name(ipc_space_t space, mach_port_name_t name, ipc_entry_t *entryp); + +ipc_entry_t +db_ipc_object_by_name( + task_t task, + mach_port_name_t name); + +#endif /* _IPC_IPC_ENTRY_H_ */ diff --git a/ipc/ipc_init.c b/ipc/ipc_init.c new file mode 100644 index 0000000..8e628ad --- /dev/null +++ b/ipc/ipc_init.c @@ -0,0 +1,117 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University. + * Copyright (c) 1993,1994 The University of Utah and + * the Computer Systems Laboratory (CSL). + * 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, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF + * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM 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. + */ +/* + * File: ipc/ipc_init.c + * Author: Rich Draves + * Date: 1989 + * + * Functions to initialize the IPC system. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +static struct vm_map ipc_kernel_map_store; +vm_map_t ipc_kernel_map = &ipc_kernel_map_store; +const vm_size_t ipc_kernel_map_size = 8 * 1024 * 1024; + +/* + * Routine: ipc_bootstrap + * Purpose: + * Initialization needed before the kernel task + * can be created. + */ + +void +ipc_bootstrap(void) +{ + kern_return_t kr; + + ipc_port_multiple_lock_init(); + + ipc_port_timestamp_lock_init(); + ipc_port_timestamp_data = 0; + + kmem_cache_init(&ipc_space_cache, "ipc_space", + sizeof(struct ipc_space), 0, NULL, 0); + + kmem_cache_init(&ipc_entry_cache, "ipc_entry", + sizeof(struct ipc_entry), 0, NULL, 0); + + kmem_cache_init(&ipc_object_caches[IOT_PORT], "ipc_port", + sizeof(struct ipc_port), 0, NULL, 0); + + kmem_cache_init(&ipc_object_caches[IOT_PORT_SET], "ipc_pset", + sizeof(struct ipc_pset), 0, NULL, 0); + + /* create special spaces */ + + kr = ipc_space_create_special(&ipc_space_kernel); + assert(kr == KERN_SUCCESS); + + kr = ipc_space_create_special(&ipc_space_reply); + assert(kr == KERN_SUCCESS); + + /* initialize modules with hidden data structures */ + + ipc_table_init(); + ipc_notify_init(); + ipc_marequest_init(); +} + +/* + * Routine: ipc_init + * Purpose: + * Final initialization of the IPC system. + */ + +void +ipc_init(void) +{ + vm_offset_t min, max; + + kmem_submap(ipc_kernel_map, kernel_map, &min, &max, + ipc_kernel_map_size); + + ipc_host_init(); +} diff --git a/ipc/ipc_init.h b/ipc/ipc_init.h new file mode 100644 index 0000000..8dd64bb --- /dev/null +++ b/ipc/ipc_init.h @@ -0,0 +1,50 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University. + * Copyright (c) 1993,1994 The University of Utah and + * the Computer Systems Laboratory (CSL). + * 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, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF + * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM 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. + */ +/* + * File: ipc/ipc_init.h + * Author: Rich Draves + * Date: 1989 + * + * Declarations of functions to initialize the IPC system. + */ + +#ifndef _IPC_IPC_INIT_H_ +#define _IPC_IPC_INIT_H_ + +/* + * Exported interfaces + */ + +/* IPC initialization needed before creation of kernel task */ +extern void ipc_bootstrap(void); + +/* Remaining IPC initialization */ +extern void ipc_init(void); + +#endif /* _IPC_IPC_INIT_H_ */ diff --git a/ipc/ipc_kmsg.c b/ipc/ipc_kmsg.c new file mode 100644 index 0000000..bd84380 --- /dev/null +++ b/ipc/ipc_kmsg.c @@ -0,0 +1,2904 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University. + * Copyright (c) 1993,1994 The University of Utah and + * the Computer Systems Laboratory (CSL). + * 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, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF + * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM 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. + */ +/* + * File: ipc/ipc_kmsg.c + * Author: Rich Draves + * Date: 1989 + * + * Operations on kernel messages. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#if MACH_KDB +#include +#include +#endif + + +ipc_kmsg_t ipc_kmsg_cache[NCPUS]; + +/* + * Routine: ipc_kmsg_enqueue + * Purpose: + * Enqueue a kmsg. + */ + +void +ipc_kmsg_enqueue( + ipc_kmsg_queue_t queue, + ipc_kmsg_t kmsg) +{ + ipc_kmsg_enqueue_macro(queue, kmsg); +} + +/* + * Routine: ipc_kmsg_dequeue + * Purpose: + * Dequeue and return a kmsg. + */ + +ipc_kmsg_t +ipc_kmsg_dequeue( + ipc_kmsg_queue_t queue) +{ + ipc_kmsg_t first; + + first = ipc_kmsg_queue_first(queue); + + if (first != IKM_NULL) + ipc_kmsg_rmqueue_first_macro(queue, first); + + return first; +} + +/* + * Routine: ipc_kmsg_rmqueue + * Purpose: + * Pull a kmsg out of a queue. + */ + +void +ipc_kmsg_rmqueue( + ipc_kmsg_queue_t queue, + ipc_kmsg_t kmsg) +{ + ipc_kmsg_t next, prev; + + assert(queue->ikmq_base != IKM_NULL); + + next = kmsg->ikm_next; + prev = kmsg->ikm_prev; + + if (next == kmsg) { + assert(prev == kmsg); + assert(queue->ikmq_base == kmsg); + + queue->ikmq_base = IKM_NULL; + } else { + if (queue->ikmq_base == kmsg) + queue->ikmq_base = next; + + next->ikm_prev = prev; + prev->ikm_next = next; + } + ikm_mark_bogus (kmsg); +} + +/* + * Routine: ipc_kmsg_queue_next + * Purpose: + * Return the kmsg following the given kmsg. + * (Or IKM_NULL if it is the last one in the queue.) + */ + +ipc_kmsg_t +ipc_kmsg_queue_next( + ipc_kmsg_queue_t queue, + ipc_kmsg_t kmsg) +{ + ipc_kmsg_t next; + + assert(queue->ikmq_base != IKM_NULL); + + next = kmsg->ikm_next; + if (queue->ikmq_base == next) + next = IKM_NULL; + + return next; +} + +/* + * Routine: ipc_kmsg_destroy + * Purpose: + * Destroys a kernel message. Releases all rights, + * references, and memory held by the message. + * Frees the message. + * Conditions: + * No locks held. + */ + +void +ipc_kmsg_destroy( + ipc_kmsg_t kmsg) +{ + ipc_kmsg_queue_t queue; + boolean_t empty; + + /* + * ipc_kmsg_clean can cause more messages to be destroyed. + * Curtail recursion by queueing messages. If a message + * is already queued, then this is a recursive call. + */ + + queue = ¤t_thread()->ith_messages; + empty = ipc_kmsg_queue_empty(queue); + ipc_kmsg_enqueue(queue, kmsg); + + if (empty) { + /* must leave kmsg in queue while cleaning it */ + + while ((kmsg = ipc_kmsg_queue_first(queue)) != IKM_NULL) { + ipc_kmsg_clean(kmsg); + ipc_kmsg_rmqueue(queue, kmsg); + ikm_free(kmsg); + } + } +} + +/* + * Routine: ipc_kmsg_clean_body + * Purpose: + * Cleans the body of a kernel message. + * Releases all rights, references, and memory. + * + * The last type/data pair might stretch past eaddr. + * (See the usage in ipc_kmsg_copyout.) + * Conditions: + * No locks held. + */ + +static void +ipc_kmsg_clean_body( + vm_offset_t saddr, + vm_offset_t eaddr) +{ + while (saddr < eaddr) { + mach_msg_type_long_t *type; + mach_msg_type_name_t name; + mach_msg_type_size_t size; + mach_msg_type_number_t number; + boolean_t is_inline, is_port; + vm_size_t length; + + type = (mach_msg_type_long_t *) saddr; + is_inline = ((mach_msg_type_t*)type)->msgt_inline; + if (((mach_msg_type_t*)type)->msgt_longform) { + name = type->msgtl_name; + size = type->msgtl_size; + number = type->msgtl_number; + saddr += sizeof(mach_msg_type_long_t); + if (mach_msg_kernel_is_misaligned(sizeof(mach_msg_type_long_t))) { + saddr = mach_msg_kernel_align(saddr); + } + } else { + name = ((mach_msg_type_t*)type)->msgt_name; + size = ((mach_msg_type_t*)type)->msgt_size; + number = ((mach_msg_type_t*)type)->msgt_number; + saddr += sizeof(mach_msg_type_t); + if (mach_msg_kernel_is_misaligned(sizeof(mach_msg_type_t))) { + saddr = mach_msg_kernel_align(saddr); + } + } + + /* calculate length of data in bytes, rounding up */ + + length = ((number * size) + 7) >> 3; + + is_port = MACH_MSG_TYPE_PORT_ANY(name); + + if (is_port) { + ipc_object_t *objects; + mach_msg_type_number_t i; + + if (is_inline) { + objects = (ipc_object_t *) saddr; + /* sanity check */ + while (eaddr < (vm_offset_t)&objects[number]) number--; + } else { + objects = (ipc_object_t *) + * (vm_offset_t *) saddr; + } + + /* destroy port rights carried in the message */ + + for (i = 0; i < number; i++) { + ipc_object_t object = objects[i]; + + if (!IO_VALID(object)) + continue; + + ipc_object_destroy(object, name); + } + } + + if (is_inline) { + saddr += length; + } else { + vm_offset_t data = * (vm_offset_t *) saddr; + + /* destroy memory carried in the message */ + + if (length == 0) + assert(data == 0); + else if (is_port) + kfree(data, length); + else + vm_map_copy_discard((vm_map_copy_t) data); + + saddr += sizeof(vm_offset_t); + } + saddr = mach_msg_kernel_align(saddr); + } +} + +/* + * Routine: ipc_kmsg_clean + * Purpose: + * Cleans a kernel message. Releases all rights, + * references, and memory held by the message. + * Conditions: + * No locks held. + */ + +void +ipc_kmsg_clean(ipc_kmsg_t kmsg) +{ + ipc_marequest_t marequest; + ipc_object_t object; + mach_msg_bits_t mbits = kmsg->ikm_header.msgh_bits; + + marequest = kmsg->ikm_marequest; + if (marequest != IMAR_NULL) + ipc_marequest_destroy(marequest); + + object = (ipc_object_t) kmsg->ikm_header.msgh_remote_port; + if (IO_VALID(object)) + ipc_object_destroy(object, MACH_MSGH_BITS_REMOTE(mbits)); + + object = (ipc_object_t) kmsg->ikm_header.msgh_local_port; + if (IO_VALID(object)) + ipc_object_destroy(object, MACH_MSGH_BITS_LOCAL(mbits)); + + if (mbits & MACH_MSGH_BITS_COMPLEX) { + vm_offset_t saddr, eaddr; + + saddr = (vm_offset_t) (&kmsg->ikm_header + 1); + eaddr = (vm_offset_t) &kmsg->ikm_header + + kmsg->ikm_header.msgh_size; + + ipc_kmsg_clean_body(saddr, eaddr); + } +} + +/* + * Routine: ipc_kmsg_clean_partial + * Purpose: + * Cleans a partially-acquired kernel message. + * eaddr is the address of the type specification + * in the body of the message that contained the error. + * If dolast, the memory and port rights in this last + * type spec are also cleaned. In that case, number + * specifies the number of port rights to clean. + * Conditions: + * Nothing locked. + */ + +static void +ipc_kmsg_clean_partial( + ipc_kmsg_t kmsg, + vm_offset_t eaddr, + boolean_t dolast, + mach_msg_type_number_t number) +{ + ipc_object_t object; + mach_msg_bits_t mbits = kmsg->ikm_header.msgh_bits; + vm_offset_t saddr; + + assert(kmsg->ikm_marequest == IMAR_NULL); + + object = (ipc_object_t) kmsg->ikm_header.msgh_remote_port; + assert(IO_VALID(object)); + ipc_object_destroy(object, MACH_MSGH_BITS_REMOTE(mbits)); + + object = (ipc_object_t) kmsg->ikm_header.msgh_local_port; + if (IO_VALID(object)) + ipc_object_destroy(object, MACH_MSGH_BITS_LOCAL(mbits)); + + saddr = (vm_offset_t) (&kmsg->ikm_header + 1); + ipc_kmsg_clean_body(saddr, eaddr); + + if (dolast) { + mach_msg_type_long_t *type; + mach_msg_type_name_t name; + mach_msg_type_size_t size; + mach_msg_type_number_t rnumber; + boolean_t is_inline, is_port; + vm_size_t length; + + type = (mach_msg_type_long_t *) eaddr; + is_inline = ((mach_msg_type_t*)type)->msgt_inline; + if (((mach_msg_type_t*)type)->msgt_longform) { + name = type->msgtl_name; + size = type->msgtl_size; + rnumber = type->msgtl_number; + eaddr += sizeof(mach_msg_type_long_t); + if (mach_msg_kernel_is_misaligned(sizeof(mach_msg_type_long_t))) { + eaddr = mach_msg_kernel_align(eaddr); + } + } else { + name = ((mach_msg_type_t*)type)->msgt_name; + size = ((mach_msg_type_t*)type)->msgt_size; + rnumber = ((mach_msg_type_t*)type)->msgt_number; + eaddr += sizeof(mach_msg_type_t); + if (mach_msg_kernel_is_misaligned(sizeof(mach_msg_type_t))) { + eaddr = mach_msg_kernel_align(eaddr); + } + } + + /* calculate length of data in bytes, rounding up */ + + length = ((rnumber * size) + 7) >> 3; + + is_port = MACH_MSG_TYPE_PORT_ANY(name); + + if (is_port) { + ipc_object_t *objects; + mach_msg_type_number_t i; + + objects = (ipc_object_t *) + (is_inline ? eaddr : * (vm_offset_t *) eaddr); + + /* destroy port rights carried in the message */ + + for (i = 0; i < number; i++) { + ipc_object_t obj = objects[i]; + + if (!IO_VALID(obj)) + continue; + + ipc_object_destroy(obj, name); + } + } + + if (!is_inline) { + vm_offset_t data = * (vm_offset_t *) eaddr; + + /* destroy memory carried in the message */ + + if (length == 0) + assert(data == 0); + else if (is_port) + kfree(data, length); + else + vm_map_copy_discard((vm_map_copy_t) data); + } + } +} + +/* + * Routine: ipc_kmsg_free + * Purpose: + * Free a kernel message buffer. + * Conditions: + * Nothing locked. + */ + +void +ipc_kmsg_free(ipc_kmsg_t kmsg) +{ + vm_size_t size = kmsg->ikm_size; + + switch (size) { + + case IKM_SIZE_NETWORK: + /* return it to the network code */ + net_kmsg_put(kmsg); + break; + + default: + kfree((vm_offset_t) kmsg, size); + break; + } +} + +/* + * Routine: ipc_kmsg_get + * Purpose: + * Allocates a kernel message buffer. + * Copies a user message to the message buffer. + * Conditions: + * Nothing locked. + * Returns: + * MACH_MSG_SUCCESS Acquired a message buffer. + * MACH_SEND_MSG_TOO_SMALL Message smaller than a header. + * MACH_SEND_MSG_TOO_SMALL Message size not long-word multiple. + * MACH_SEND_NO_BUFFER Couldn't allocate a message buffer. + * MACH_SEND_INVALID_DATA Couldn't copy message data. + */ + +mach_msg_return_t +ipc_kmsg_get( + mach_msg_user_header_t *msg, + mach_msg_size_t size, + ipc_kmsg_t *kmsgp) +{ + ipc_kmsg_t kmsg; + mach_msg_size_t ksize = size * IKM_EXPAND_FACTOR; + + if ((size < sizeof(mach_msg_user_header_t)) || mach_msg_user_is_misaligned(size)) + return MACH_SEND_MSG_TOO_SMALL; + + if (ksize <= IKM_SAVED_MSG_SIZE) { + kmsg = ikm_cache_alloc(); + if (kmsg == IKM_NULL) + return MACH_SEND_NO_BUFFER; + } else { + kmsg = ikm_alloc(ksize); + if (kmsg == IKM_NULL) + return MACH_SEND_NO_BUFFER; + ikm_init(kmsg, ksize); + } + + if (copyinmsg(msg, &kmsg->ikm_header, size, kmsg->ikm_size)) { + ikm_free(kmsg); + return MACH_SEND_INVALID_DATA; + } + + *kmsgp = kmsg; + return MACH_MSG_SUCCESS; +} + +/* + * Routine: ipc_kmsg_get_from_kernel + * Purpose: + * Allocates a kernel message buffer. + * Copies a kernel message to the message buffer. + * Only resource errors are allowed. + * Conditions: + * Nothing locked. + * Returns: + * MACH_MSG_SUCCESS Acquired a message buffer. + * MACH_SEND_NO_BUFFER Couldn't allocate a message buffer. + */ + +extern mach_msg_return_t +ipc_kmsg_get_from_kernel( + mach_msg_header_t *msg, + mach_msg_size_t size, + ipc_kmsg_t *kmsgp) +{ + ipc_kmsg_t kmsg; + + assert(size >= sizeof(mach_msg_header_t)); + assert(!mach_msg_kernel_is_misaligned(size)); + + kmsg = ikm_alloc(size); + if (kmsg == IKM_NULL) + return MACH_SEND_NO_BUFFER; + ikm_init(kmsg, size); + + memcpy(&kmsg->ikm_header, msg, size); + + kmsg->ikm_header.msgh_size = size; + *kmsgp = kmsg; + return MACH_MSG_SUCCESS; +} + +/* + * Routine: ipc_kmsg_put + * Purpose: + * Copies a message buffer to a user message. + * Copies only the specified number of bytes. + * Frees the message buffer. + * Conditions: + * Nothing locked. The message buffer must have clean + * header (ikm_marequest) fields. + * Returns: + * MACH_MSG_SUCCESS Copied data out of message buffer. + * MACH_RCV_INVALID_DATA Couldn't copy to user message. + */ + +mach_msg_return_t +ipc_kmsg_put( + mach_msg_user_header_t *msg, + ipc_kmsg_t kmsg, + mach_msg_size_t size) +{ + mach_msg_return_t mr; + + ikm_check_initialized(kmsg, kmsg->ikm_size); + + if (copyoutmsg(&kmsg->ikm_header, msg, size)) + mr = MACH_RCV_INVALID_DATA; + else + mr = MACH_MSG_SUCCESS; + + ikm_cache_free(kmsg); + + return mr; +} + +/* + * Routine: ipc_kmsg_put_to_kernel + * Purpose: + * Copies a message buffer to a kernel message. + * Frees the message buffer. + * No errors allowed. + * Conditions: + * Nothing locked. + */ + +void +ipc_kmsg_put_to_kernel( + mach_msg_header_t *msg, + ipc_kmsg_t kmsg, + mach_msg_size_t size) +{ +#if DIPC + assert(!KMSG_IN_DIPC(kmsg)); +#endif /* DIPC */ + + memcpy(msg, &kmsg->ikm_header, size); + + ikm_free(kmsg); +} + +/* + * Routine: ipc_kmsg_copyin_header + * Purpose: + * "Copy-in" port rights in the header of a message. + * Operates atomically; if it doesn't succeed the + * message header and the space are left untouched. + * If it does succeed the remote/local port fields + * contain object pointers instead of port names, + * and the bits field is updated. The destination port + * will be a valid port pointer. + * + * The notify argument implements the MACH_SEND_CANCEL option. + * If it is not MACH_PORT_NULL, it should name a receive right. + * If the processing of the destination port would generate + * a port-deleted notification (because the right for the + * destination port is destroyed and it had a request for + * a dead-name notification registered), and the port-deleted + * notification would be sent to the named receive right, + * then it isn't sent and the send-once right for the notify + * port is quietly destroyed. + * Conditions: + * Nothing locked. + * Returns: + * MACH_MSG_SUCCESS Successful copyin. + * MACH_SEND_INVALID_HEADER + * Illegal value in the message header bits. + * MACH_SEND_INVALID_DEST The space is dead. + * MACH_SEND_INVALID_NOTIFY + * Notify is non-null and doesn't name a receive right. + * (Either KERN_INVALID_NAME or KERN_INVALID_RIGHT.) + * MACH_SEND_INVALID_DEST Can't copyin destination port. + * (Either KERN_INVALID_NAME or KERN_INVALID_RIGHT.) + * MACH_SEND_INVALID_REPLY Can't copyin reply port. + * (Either KERN_INVALID_NAME or KERN_INVALID_RIGHT.) + */ + +mach_msg_return_t +ipc_kmsg_copyin_header( + mach_msg_header_t *msg, + ipc_space_t space, + mach_port_name_t notify) +{ + mach_msg_bits_t mbits = msg->msgh_bits &~ MACH_MSGH_BITS_CIRCULAR; + /* + * TODO: For 64 bits, msgh_remote_port as written by user space + * is 4 bytes long but here we assume it is the same size as a pointer. + * When copying the message to the kernel, we need to perform the + * conversion so that port names are parsed correctly. + * + * When copying the message out of the kernel to user space, we also need + * to be careful with the reverse translation. + */ + + mach_port_name_t dest_name = (mach_port_name_t)msg->msgh_remote_port; + mach_port_name_t reply_name = (mach_port_name_t)msg->msgh_local_port; + kern_return_t kr; + +#ifndef MIGRATING_THREADS + /* first check for common cases */ + + if (notify == MACH_PORT_NULL) switch (MACH_MSGH_BITS_PORTS(mbits)) { + case MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0): { + ipc_entry_t entry; + ipc_entry_bits_t bits; + ipc_port_t dest_port; + + /* sending an asynchronous message */ + + if (reply_name != MACH_PORT_NULL) + break; + + is_read_lock(space); + if (!space->is_active) + goto abort_async; + + entry = ipc_entry_lookup (space, dest_name); + if (entry == IE_NULL) + { + ipc_entry_lookup_failed (msg, dest_name); + goto abort_async; + } + bits = entry->ie_bits; + + /* check type bits */ + if (IE_BITS_TYPE (bits) != MACH_PORT_TYPE_SEND) + goto abort_async; + + /* optimized ipc_right_copyin */ + + assert(IE_BITS_UREFS(bits) > 0); + + dest_port = (ipc_port_t) entry->ie_object; + assert(dest_port != IP_NULL); + + ip_lock(dest_port); + /* can unlock space now without compromising atomicity */ + is_read_unlock(space); + + if (!ip_active(dest_port)) { + ip_unlock(dest_port); + break; + } + + assert(dest_port->ip_srights > 0); + dest_port->ip_srights++; + ip_reference(dest_port); + ip_unlock(dest_port); + + msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) | + MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND, 0)); + msg->msgh_remote_port = (mach_port_t) dest_port; + return MACH_MSG_SUCCESS; + + abort_async: + is_read_unlock(space); + break; + } + + case MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, + MACH_MSG_TYPE_MAKE_SEND_ONCE): { + ipc_entry_t entry; + ipc_entry_bits_t bits; + ipc_port_t dest_port, reply_port; + + /* sending a request message */ + + is_read_lock(space); + if (!space->is_active) + goto abort_request; + + entry = ipc_entry_lookup (space, dest_name); + if (entry == IE_NULL) + { + ipc_entry_lookup_failed (msg, dest_name); + goto abort_request; + } + bits = entry->ie_bits; + + /* check type bits */ + if (IE_BITS_TYPE (bits) != MACH_PORT_TYPE_SEND) + goto abort_request; + + assert(IE_BITS_UREFS(bits) > 0); + + dest_port = (ipc_port_t) entry->ie_object; + assert(dest_port != IP_NULL); + + entry = ipc_entry_lookup (space, reply_name); + if (entry == IE_NULL) + { + ipc_entry_lookup_failed (msg, reply_name); + goto abort_request; + } + bits = entry->ie_bits; + + /* check type bits */ + if (IE_BITS_TYPE (bits) != MACH_PORT_TYPE_RECEIVE) + goto abort_request; + + reply_port = (ipc_port_t) entry->ie_object; + assert(reply_port != IP_NULL); + + /* + * To do an atomic copyin, need simultaneous + * locks on both ports and the space. If + * dest_port == reply_port, and simple locking is + * enabled, then we will abort. Otherwise it's + * OK to unlock twice. + */ + + ip_lock(dest_port); + if (!ip_active(dest_port) || !ip_lock_try(reply_port)) { + ip_unlock(dest_port); + goto abort_request; + } + /* can unlock space now without compromising atomicity */ + is_read_unlock(space); + + assert(dest_port->ip_srights > 0); + dest_port->ip_srights++; + ip_reference(dest_port); + ip_unlock(dest_port); + + assert(ip_active(reply_port)); + assert(reply_port->ip_receiver_name == reply_name); + assert(reply_port->ip_receiver == space); + + reply_port->ip_sorights++; + ip_reference(reply_port); + ip_unlock(reply_port); + + msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) | + MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND, + MACH_MSG_TYPE_PORT_SEND_ONCE)); + msg->msgh_remote_port = (mach_port_t) dest_port; + msg->msgh_local_port = (mach_port_t) reply_port; + return MACH_MSG_SUCCESS; + + abort_request: + is_read_unlock(space); + break; + } + + case MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE, 0): { + ipc_entry_t entry; + ipc_entry_bits_t bits; + ipc_port_t dest_port; + + /* sending a reply message */ + + if (reply_name != MACH_PORT_NULL) + break; + + is_write_lock(space); + if (!space->is_active) + goto abort_reply; + + entry = ipc_entry_lookup (space, dest_name); + if (entry == IE_NULL) + { + ipc_entry_lookup_failed (msg, dest_name); + goto abort_reply; + } + bits = entry->ie_bits; + + /* check and type bits */ + if (IE_BITS_TYPE (bits) != MACH_PORT_TYPE_SEND_ONCE) + goto abort_reply; + + /* optimized ipc_right_copyin */ + + assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE); + assert(IE_BITS_UREFS(bits) == 1); + assert((bits & IE_BITS_MAREQUEST) == 0); + + if (entry->ie_request != 0) + goto abort_reply; + + dest_port = (ipc_port_t) entry->ie_object; + assert(dest_port != IP_NULL); + + ip_lock(dest_port); + if (!ip_active(dest_port)) { + ip_unlock(dest_port); + goto abort_reply; + } + + assert(dest_port->ip_sorights > 0); + ip_unlock(dest_port); + + entry->ie_object = IO_NULL; + ipc_entry_dealloc (space, dest_name, entry); + is_write_unlock(space); + + msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) | + MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, + 0)); + msg->msgh_remote_port = (mach_port_t) dest_port; + return MACH_MSG_SUCCESS; + + abort_reply: + is_write_unlock(space); + break; + } + + default: + /* don't bother optimizing */ + break; + } +#endif /* MIGRATING_THREADS */ + + { + mach_msg_type_name_t dest_type = MACH_MSGH_BITS_REMOTE(mbits); + mach_msg_type_name_t reply_type = MACH_MSGH_BITS_LOCAL(mbits); + ipc_object_t dest_port, reply_port; + ipc_port_t dest_soright, reply_soright; + ipc_port_t notify_port = 0; /* '=0' to quiet gcc warnings */ + + if (!MACH_MSG_TYPE_PORT_ANY_SEND(dest_type)) + return MACH_SEND_INVALID_HEADER; + + if ((reply_type == 0) ? + (reply_name != MACH_PORT_NULL) : + !MACH_MSG_TYPE_PORT_ANY_SEND(reply_type)) + return MACH_SEND_INVALID_HEADER; + + is_write_lock(space); + if (!space->is_active) + goto invalid_dest; + + if (notify != MACH_PORT_NULL) { + ipc_entry_t entry; + + if (((entry = ipc_entry_lookup(space, notify)) == IE_NULL) || + ((entry->ie_bits & MACH_PORT_TYPE_RECEIVE) == 0)) { + if (entry == IE_NULL) + ipc_entry_lookup_failed (msg, notify); + is_write_unlock(space); + return MACH_SEND_INVALID_NOTIFY; + } + + notify_port = (ipc_port_t) entry->ie_object; + } + + if (dest_name == reply_name) { + ipc_entry_t entry; + mach_port_name_t name = dest_name; + + /* + * Destination and reply ports are the same! + * This is a little tedious to make atomic, because + * there are 25 combinations of dest_type/reply_type. + * However, most are easy. If either is move-sonce, + * then there must be an error. If either are + * make-send or make-sonce, then we must be looking + * at a receive right so the port can't die. + * The hard cases are the combinations of + * copy-send and make-send. + */ + + entry = ipc_entry_lookup(space, name); + if (entry == IE_NULL) { + ipc_entry_lookup_failed (msg, name); + goto invalid_dest; + } + + assert(reply_type != 0); /* because name not null */ + + if (!ipc_right_copyin_check(space, name, entry, reply_type)) + goto invalid_reply; + + if ((dest_type == MACH_MSG_TYPE_MOVE_SEND_ONCE) || + (reply_type == MACH_MSG_TYPE_MOVE_SEND_ONCE)) { + /* + * Why must there be an error? To get a valid + * destination, this entry must name a live + * port (not a dead name or dead port). However + * a successful move-sonce will destroy a + * live entry. Therefore the other copyin, + * whatever it is, would fail. We've already + * checked for reply port errors above, + * so report a destination error. + */ + + goto invalid_dest; + } else if ((dest_type == MACH_MSG_TYPE_MAKE_SEND) || + (dest_type == MACH_MSG_TYPE_MAKE_SEND_ONCE) || + (reply_type == MACH_MSG_TYPE_MAKE_SEND) || + (reply_type == MACH_MSG_TYPE_MAKE_SEND_ONCE)) { + kr = ipc_right_copyin(space, name, entry, + dest_type, FALSE, + &dest_port, &dest_soright); + if (kr != KERN_SUCCESS) + goto invalid_dest; + + /* + * Either dest or reply needs a receive right. + * We know the receive right is there, because + * of the copyin_check and copyin calls. Hence + * the port is not in danger of dying. If dest + * used the receive right, then the right needed + * by reply (and verified by copyin_check) will + * still be there. + */ + + assert(IO_VALID(dest_port)); + assert(entry->ie_bits & MACH_PORT_TYPE_RECEIVE); + assert(dest_soright == IP_NULL); + + kr = ipc_right_copyin(space, name, entry, + reply_type, TRUE, + &reply_port, &reply_soright); + + assert(kr == KERN_SUCCESS); + assert(reply_port == dest_port); + assert(entry->ie_bits & MACH_PORT_TYPE_RECEIVE); + assert(reply_soright == IP_NULL); + } else if ((dest_type == MACH_MSG_TYPE_COPY_SEND) && + (reply_type == MACH_MSG_TYPE_COPY_SEND)) { + /* + * To make this atomic, just do one copy-send, + * and dup the send right we get out. + */ + + kr = ipc_right_copyin(space, name, entry, + dest_type, FALSE, + &dest_port, &dest_soright); + if (kr != KERN_SUCCESS) + goto invalid_dest; + + assert(entry->ie_bits & MACH_PORT_TYPE_SEND); + assert(dest_soright == IP_NULL); + + /* + * It's OK if the port we got is dead now, + * so reply_port is IP_DEAD, because the msg + * won't go anywhere anyway. + */ + + reply_port = (ipc_object_t) + ipc_port_copy_send((ipc_port_t) dest_port); + reply_soright = IP_NULL; + } else if ((dest_type == MACH_MSG_TYPE_MOVE_SEND) && + (reply_type == MACH_MSG_TYPE_MOVE_SEND)) { + /* + * This is an easy case. Just use our + * handy-dandy special-purpose copyin call + * to get two send rights for the price of one. + */ + + kr = ipc_right_copyin_two(space, name, entry, + &dest_port, &dest_soright); + if (kr != KERN_SUCCESS) + goto invalid_dest; + + /* the entry might need to be deallocated */ + + if (IE_BITS_TYPE(entry->ie_bits) + == MACH_PORT_TYPE_NONE) + ipc_entry_dealloc(space, name, entry); + + reply_port = dest_port; + reply_soright = IP_NULL; + } else { + ipc_port_t soright; + + assert(((dest_type == MACH_MSG_TYPE_COPY_SEND) && + (reply_type == MACH_MSG_TYPE_MOVE_SEND)) || + ((dest_type == MACH_MSG_TYPE_MOVE_SEND) && + (reply_type == MACH_MSG_TYPE_COPY_SEND))); + + /* + * To make this atomic, just do a move-send, + * and dup the send right we get out. + */ + + kr = ipc_right_copyin(space, name, entry, + MACH_MSG_TYPE_MOVE_SEND, FALSE, + &dest_port, &soright); + if (kr != KERN_SUCCESS) + goto invalid_dest; + + /* the entry might need to be deallocated */ + + if (IE_BITS_TYPE(entry->ie_bits) + == MACH_PORT_TYPE_NONE) + ipc_entry_dealloc(space, name, entry); + + /* + * It's OK if the port we got is dead now, + * so reply_port is IP_DEAD, because the msg + * won't go anywhere anyway. + */ + + reply_port = (ipc_object_t) + ipc_port_copy_send((ipc_port_t) dest_port); + + if (dest_type == MACH_MSG_TYPE_MOVE_SEND) { + dest_soright = soright; + reply_soright = IP_NULL; + } else { + dest_soright = IP_NULL; + reply_soright = soright; + } + } + } else if (!MACH_PORT_NAME_VALID(reply_name)) { + ipc_entry_t entry; + + /* + * No reply port! This is an easy case + * to make atomic. Just copyin the destination. + */ + + entry = ipc_entry_lookup(space, dest_name); + if (entry == IE_NULL) { + ipc_entry_lookup_failed (msg, dest_name); + goto invalid_dest; + } + + kr = ipc_right_copyin(space, dest_name, entry, + dest_type, FALSE, + &dest_port, &dest_soright); + if (kr != KERN_SUCCESS) + goto invalid_dest; + + /* the entry might need to be deallocated */ + + if (IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_NONE) + ipc_entry_dealloc(space, dest_name, entry); + + reply_port = (ipc_object_t) invalid_name_to_port(reply_name); + reply_soright = IP_NULL; + } else { + ipc_entry_t dest_entry, reply_entry; + ipc_port_t saved_reply; + + /* + * This is the tough case to make atomic. + * The difficult problem is serializing with port death. + * At the time we copyin dest_port, it must be alive. + * If reply_port is alive when we copyin it, then + * we are OK, because we serialize before the death + * of both ports. Assume reply_port is dead at copyin. + * Then if dest_port dies/died after reply_port died, + * we are OK, because we serialize between the death + * of the two ports. So the bad case is when dest_port + * dies after its copyin, reply_port dies before its + * copyin, and dest_port dies before reply_port. Then + * the copyins operated as if dest_port was alive + * and reply_port was dead, which shouldn't have happened + * because they died in the other order. + * + * We handle the bad case by undoing the copyins + * (which is only possible because the ports are dead) + * and failing with MACH_SEND_INVALID_DEST, serializing + * after the death of the ports. + * + * Note that it is easy for a user task to tell if + * a copyin happened before or after a port died. + * For example, suppose both dest and reply are + * send-once rights (types are both move-sonce) and + * both rights have dead-name requests registered. + * If a port dies before copyin, a dead-name notification + * is generated and the dead name's urefs are incremented, + * and if the copyin happens first, a port-deleted + * notification is generated. + * + * Note that although the entries are different, + * dest_port and reply_port might still be the same. + */ + + dest_entry = ipc_entry_lookup(space, dest_name); + if (dest_entry == IE_NULL) { + ipc_entry_lookup_failed (msg, dest_name); + goto invalid_dest; + } + + reply_entry = ipc_entry_lookup(space, reply_name); + if (reply_entry == IE_NULL) + { + ipc_entry_lookup_failed (msg, reply_name); + goto invalid_reply; + } + + assert(dest_entry != reply_entry); /* names are not equal */ + assert(reply_type != 0); /* because reply_name not null */ + + if (!ipc_right_copyin_check(space, reply_name, reply_entry, + reply_type)) + goto invalid_reply; + + kr = ipc_right_copyin(space, dest_name, dest_entry, + dest_type, FALSE, + &dest_port, &dest_soright); + if (kr != KERN_SUCCESS) + goto invalid_dest; + + assert(IO_VALID(dest_port)); + + saved_reply = (ipc_port_t) reply_entry->ie_object; + /* might be IP_NULL, if this is a dead name */ + if (saved_reply != IP_NULL) + ipc_port_reference(saved_reply); + + kr = ipc_right_copyin(space, reply_name, reply_entry, + reply_type, TRUE, + &reply_port, &reply_soright); + assert(kr == KERN_SUCCESS); + + if ((saved_reply != IP_NULL) && (reply_port == IO_DEAD)) { + ipc_port_t dest = (ipc_port_t) dest_port; + ipc_port_timestamp_t timestamp; + boolean_t must_undo; + + /* + * The reply port died before copyin. + * Check if dest port died before reply. + */ + + ip_lock(saved_reply); + assert(!ip_active(saved_reply)); + timestamp = saved_reply->ip_timestamp; + ip_unlock(saved_reply); + + ip_lock(dest); + must_undo = (!ip_active(dest) && + IP_TIMESTAMP_ORDER(dest->ip_timestamp, + timestamp)); + ip_unlock(dest); + + if (must_undo) { + /* + * Our worst nightmares are realized. + * Both destination and reply ports + * are dead, but in the wrong order, + * so we must undo the copyins and + * possibly generate a dead-name notif. + */ + + ipc_right_copyin_undo( + space, dest_name, dest_entry, + dest_type, dest_port, + dest_soright); + /* dest_entry may be deallocated now */ + + ipc_right_copyin_undo( + space, reply_name, reply_entry, + reply_type, reply_port, + reply_soright); + /* reply_entry may be deallocated now */ + + is_write_unlock(space); + + if (dest_soright != IP_NULL) + ipc_notify_dead_name(dest_soright, + dest_name); + assert(reply_soright == IP_NULL); + + ipc_port_release(saved_reply); + return MACH_SEND_INVALID_DEST; + } + } + + /* the entries might need to be deallocated */ + + if (IE_BITS_TYPE(reply_entry->ie_bits) == MACH_PORT_TYPE_NONE) + ipc_entry_dealloc(space, reply_name, reply_entry); + + if (IE_BITS_TYPE(dest_entry->ie_bits) == MACH_PORT_TYPE_NONE) + ipc_entry_dealloc(space, dest_name, dest_entry); + + if (saved_reply != IP_NULL) + ipc_port_release(saved_reply); + } + + /* + * At this point, dest_port, reply_port, + * dest_soright, reply_soright are all initialized. + * Any defunct entries have been deallocated. + * The space is still write-locked, and we need to + * make the MACH_SEND_CANCEL check. The notify_port pointer + * is still usable, because the copyin code above won't ever + * deallocate a receive right, so its entry still exists + * and holds a ref. Note notify_port might even equal + * dest_port or reply_port. + */ + + if ((notify != MACH_PORT_NULL) && + (dest_soright == notify_port)) { + ipc_port_release_sonce(dest_soright); + dest_soright = IP_NULL; + } + + is_write_unlock(space); + + if (dest_soright != IP_NULL) + ipc_notify_port_deleted(dest_soright, dest_name); + + if (reply_soright != IP_NULL) + ipc_notify_port_deleted(reply_soright, reply_name); + + dest_type = ipc_object_copyin_type(dest_type); + reply_type = ipc_object_copyin_type(reply_type); + + msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) | + MACH_MSGH_BITS(dest_type, reply_type)); + msg->msgh_remote_port = (mach_port_t) dest_port; + msg->msgh_local_port = (mach_port_t) reply_port; + } + + return MACH_MSG_SUCCESS; + + invalid_dest: + is_write_unlock(space); + return MACH_SEND_INVALID_DEST; + + invalid_reply: + is_write_unlock(space); + return MACH_SEND_INVALID_REPLY; +} + +static mach_msg_return_t +ipc_kmsg_copyin_body( + ipc_kmsg_t kmsg, + ipc_space_t space, + vm_map_t map) +{ + ipc_object_t dest; + vm_offset_t saddr, eaddr; + boolean_t complex; + boolean_t use_page_lists, steal_pages; + + dest = (ipc_object_t) kmsg->ikm_header.msgh_remote_port; + complex = FALSE; + use_page_lists = ipc_kobject_vm_page_list(ip_kotype((ipc_port_t)dest)); + steal_pages = ipc_kobject_vm_page_steal(ip_kotype((ipc_port_t)dest)); + + saddr = (vm_offset_t) (&kmsg->ikm_header + 1); + eaddr = (vm_offset_t) &kmsg->ikm_header + kmsg->ikm_header.msgh_size; + + // We make assumptions about the alignment of the header. + _Static_assert(!mach_msg_kernel_is_misaligned(sizeof(mach_msg_header_t)), + "mach_msg_header_t needs to be MACH_MSG_KERNEL_ALIGNMENT aligned."); + + while (saddr < eaddr) { + vm_offset_t taddr = saddr; + mach_msg_type_long_t *type; + mach_msg_type_name_t name; + mach_msg_type_size_t size; + mach_msg_type_number_t number; + boolean_t is_inline, longform, dealloc, is_port; + vm_offset_t data; + uint64_t length; + kern_return_t kr; + + type = (mach_msg_type_long_t *) saddr; + + if (((eaddr - saddr) < sizeof(mach_msg_type_t)) || + ((longform = ((mach_msg_type_t*)type)->msgt_longform) && + ((eaddr - saddr) < sizeof(mach_msg_type_long_t)))) { + ipc_kmsg_clean_partial(kmsg, taddr, FALSE, 0); + return MACH_SEND_MSG_TOO_SMALL; + } + + is_inline = ((mach_msg_type_t*)type)->msgt_inline; + dealloc = ((mach_msg_type_t*)type)->msgt_deallocate; + if (longform) { + name = type->msgtl_name; + size = type->msgtl_size; + number = type->msgtl_number; + saddr += sizeof(mach_msg_type_long_t); + if (mach_msg_kernel_is_misaligned(sizeof(mach_msg_type_long_t))) { + saddr = mach_msg_kernel_align(saddr); + } + } else { + name = ((mach_msg_type_t*)type)->msgt_name; + size = ((mach_msg_type_t*)type)->msgt_size; + number = ((mach_msg_type_t*)type)->msgt_number; + saddr += sizeof(mach_msg_type_t); + if (mach_msg_kernel_is_misaligned(sizeof(mach_msg_type_t))) { + saddr = mach_msg_kernel_align(saddr); + } + } + + is_port = MACH_MSG_TYPE_PORT_ANY(name); + + if ((is_port && (size != PORT_T_SIZE_IN_BITS)) || +#ifndef __x86_64__ + (longform && ((type->msgtl_header.msgt_name != 0) || + (type->msgtl_header.msgt_size != 0) || + (type->msgtl_header.msgt_number != 0))) || +#endif + (((mach_msg_type_t*)type)->msgt_unused != 0) || + (dealloc && is_inline)) { + ipc_kmsg_clean_partial(kmsg, taddr, FALSE, 0); + return MACH_SEND_INVALID_TYPE; + } + + /* calculate length of data in bytes, rounding up */ + + length = (((uint64_t) number * size) + 7) >> 3; + + if (is_inline) { + vm_size_t amount = length; + + if ((eaddr - saddr) < amount) { + ipc_kmsg_clean_partial(kmsg, taddr, FALSE, 0); + return MACH_SEND_MSG_TOO_SMALL; + } + + data = saddr; + saddr += amount; + } else { + vm_offset_t addr; + + if ((eaddr - saddr) < sizeof(vm_offset_t)) { + ipc_kmsg_clean_partial(kmsg, taddr, FALSE, 0); + return MACH_SEND_MSG_TOO_SMALL; + } + + /* grab the out-of-line data */ + + addr = * (vm_offset_t *) saddr; + + if (length == 0) + data = 0; + else if (is_port) { + data = kalloc(length); + if (data == 0) + goto invalid_memory; + + if (sizeof(mach_port_name_t) != sizeof(mach_port_t)) + { + mach_port_name_t *src = (mach_port_name_t*)addr; + mach_port_t *dst = (mach_port_t*)data; + for (int i=0; imsgtl_name = newname; + else + ((mach_msg_type_t*)type)->msgt_name = newname; + + for (i = 0; i < number; i++) { + mach_port_name_t port = ((mach_port_t*)data)[i]; + ipc_object_t object; + + if (!MACH_PORT_NAME_VALID(port)) { + objects[i] = (ipc_object_t)invalid_name_to_port(port); + continue; + } + + kr = ipc_object_copyin(space, port, + name, &object); + if (kr != KERN_SUCCESS) { + ipc_kmsg_clean_partial(kmsg, taddr, + TRUE, i); + return MACH_SEND_INVALID_RIGHT; + } + + if ((newname == MACH_MSG_TYPE_PORT_RECEIVE) && + ipc_port_check_circularity( + (ipc_port_t) object, + (ipc_port_t) dest)) + kmsg->ikm_header.msgh_bits |= + MACH_MSGH_BITS_CIRCULAR; + + objects[i] = object; + } + + complex = TRUE; + } + saddr = mach_msg_kernel_align(saddr); + } + + if (!complex) + kmsg->ikm_header.msgh_bits &= ~MACH_MSGH_BITS_COMPLEX; + + return MACH_MSG_SUCCESS; +} + +/* + * Routine: ipc_kmsg_copyin + * Purpose: + * "Copy-in" port rights and out-of-line memory + * in the message. + * + * In all failure cases, the message is left holding + * no rights or memory. However, the message buffer + * is not deallocated. If successful, the message + * contains a valid destination port. + * Conditions: + * Nothing locked. + * Returns: + * MACH_MSG_SUCCESS Successful copyin. + * MACH_SEND_INVALID_HEADER + * Illegal value in the message header bits. + * MACH_SEND_INVALID_NOTIFY Bad notify port. + * MACH_SEND_INVALID_DEST Can't copyin destination port. + * MACH_SEND_INVALID_REPLY Can't copyin reply port. + * MACH_SEND_INVALID_MEMORY Can't grab out-of-line memory. + * MACH_SEND_INVALID_RIGHT Can't copyin port right in body. + * MACH_SEND_INVALID_TYPE Bad type specification. + * MACH_SEND_MSG_TOO_SMALL Body is too small for types/data. + */ + +mach_msg_return_t +ipc_kmsg_copyin( + ipc_kmsg_t kmsg, + ipc_space_t space, + vm_map_t map, + mach_port_name_t notify) +{ + mach_msg_return_t mr; + + mr = ipc_kmsg_copyin_header(&kmsg->ikm_header, space, notify); + if (mr != MACH_MSG_SUCCESS) + return mr; + + if ((kmsg->ikm_header.msgh_bits & MACH_MSGH_BITS_COMPLEX) == 0) + return MACH_MSG_SUCCESS; + + return ipc_kmsg_copyin_body(kmsg, space, map); +} + +/* + * Routine: ipc_kmsg_copyin_from_kernel + * Purpose: + * "Copy-in" port rights and out-of-line memory + * in a message sent from the kernel. + * + * Because the message comes from the kernel, + * the implementation assumes there are no errors + * or peculiarities in the message. + * + * Returns TRUE if queueing the message + * would result in a circularity. + * Conditions: + * Nothing locked. + */ + +void +ipc_kmsg_copyin_from_kernel(ipc_kmsg_t kmsg) +{ + mach_msg_bits_t bits = kmsg->ikm_header.msgh_bits; + mach_msg_type_name_t rname = MACH_MSGH_BITS_REMOTE(bits); + mach_msg_type_name_t lname = MACH_MSGH_BITS_LOCAL(bits); + ipc_object_t remote = (ipc_object_t) kmsg->ikm_header.msgh_remote_port; + ipc_object_t local = (ipc_object_t) kmsg->ikm_header.msgh_local_port; + vm_offset_t saddr, eaddr; + + /* translate the destination and reply ports */ + + ipc_object_copyin_from_kernel(remote, rname); + if (IO_VALID(local)) + ipc_object_copyin_from_kernel(local, lname); + + /* + * The common case is a complex message with no reply port, + * because that is what the memory_object interface uses. + */ + + if (bits == (MACH_MSGH_BITS_COMPLEX | + MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0))) { + bits = (MACH_MSGH_BITS_COMPLEX | + MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND, 0)); + + kmsg->ikm_header.msgh_bits = bits; + } else { + bits = (MACH_MSGH_BITS_OTHER(bits) | + MACH_MSGH_BITS(ipc_object_copyin_type(rname), + ipc_object_copyin_type(lname))); + + kmsg->ikm_header.msgh_bits = bits; + if ((bits & MACH_MSGH_BITS_COMPLEX) == 0) + return; + } + + saddr = (vm_offset_t) (&kmsg->ikm_header + 1); + eaddr = (vm_offset_t) &kmsg->ikm_header + kmsg->ikm_header.msgh_size; + + while (saddr < eaddr) { + mach_msg_type_long_t *type; + mach_msg_type_name_t name; + mach_msg_type_size_t size; + mach_msg_type_number_t number; + boolean_t is_inline, longform, is_port; + vm_offset_t data; + vm_size_t length; + + type = (mach_msg_type_long_t *) saddr; + is_inline = ((mach_msg_type_t*)type)->msgt_inline; + longform = ((mach_msg_type_t*)type)->msgt_longform; + /* type->msgtl_header.msgt_deallocate not used */ + if (longform) { + name = type->msgtl_name; + size = type->msgtl_size; + number = type->msgtl_number; + saddr += sizeof(mach_msg_type_long_t); + if (mach_msg_kernel_is_misaligned(sizeof(mach_msg_type_long_t))) { + saddr = mach_msg_kernel_align(saddr); + } + } else { + name = ((mach_msg_type_t*)type)->msgt_name; + size = ((mach_msg_type_t*)type)->msgt_size; + number = ((mach_msg_type_t*)type)->msgt_number; + saddr += sizeof(mach_msg_type_t); + if (mach_msg_kernel_is_misaligned(sizeof(mach_msg_type_t))) { + saddr = mach_msg_kernel_align(saddr); + } + } + + /* calculate length of data in bytes, rounding up */ + + length = ((number * size) + 7) >> 3; + + is_port = MACH_MSG_TYPE_PORT_ANY(name); + + if (is_inline) { + data = saddr; + saddr += length; + } else { + /* + * The sender should supply ready-made memory + * for us, so we don't need to do anything. + */ + + data = * (vm_offset_t *) saddr; + saddr += sizeof(vm_offset_t); + } + + if (is_port) { + mach_msg_type_name_t newname = + ipc_object_copyin_type(name); + ipc_object_t *objects = (ipc_object_t *) data; + mach_msg_type_number_t i; + + if (longform) + type->msgtl_name = newname; + else + ((mach_msg_type_t*)type)->msgt_name = newname; + for (i = 0; i < number; i++) { + ipc_object_t object = objects[i]; + + if (!IO_VALID(object)) + continue; + + ipc_object_copyin_from_kernel(object, name); + + if ((newname == MACH_MSG_TYPE_PORT_RECEIVE) && + ipc_port_check_circularity( + (ipc_port_t) object, + (ipc_port_t) remote)) + kmsg->ikm_header.msgh_bits |= + MACH_MSGH_BITS_CIRCULAR; + } + } + saddr = mach_msg_kernel_align(saddr); + } +} + +/* + * Routine: ipc_kmsg_copyout_header + * Purpose: + * "Copy-out" port rights in the header of a message. + * Operates atomically; if it doesn't succeed the + * message header and the space are left untouched. + * If it does succeed the remote/local port fields + * contain port names instead of object pointers, + * and the bits field is updated. + * + * The notify argument implements the MACH_RCV_NOTIFY option. + * If it is not MACH_PORT_NULL, it should name a receive right. + * If the process of receiving the reply port creates a + * new right in the receiving task, then the new right is + * automatically registered for a dead-name notification, + * with the notify port supplying the send-once right. + * Conditions: + * Nothing locked. + * Returns: + * MACH_MSG_SUCCESS Copied out port rights. + * MACH_RCV_INVALID_NOTIFY + * Notify is non-null and doesn't name a receive right. + * (Either KERN_INVALID_NAME or KERN_INVALID_RIGHT.) + * MACH_RCV_HEADER_ERROR|MACH_MSG_IPC_SPACE + * The space is dead. + * MACH_RCV_HEADER_ERROR|MACH_MSG_IPC_SPACE + * No room in space for another name. + * MACH_RCV_HEADER_ERROR|MACH_MSG_IPC_KERNEL + * Couldn't allocate memory for the reply port. + * MACH_RCV_HEADER_ERROR|MACH_MSG_IPC_KERNEL + * Couldn't allocate memory for the dead-name request. + */ + +mach_msg_return_t +ipc_kmsg_copyout_header( + mach_msg_header_t *msg, + ipc_space_t space, + mach_port_name_t notify) +{ + mach_msg_bits_t mbits = msg->msgh_bits; + ipc_port_t dest = (ipc_port_t) msg->msgh_remote_port; + + assert(IP_VALID(dest)); + +#ifndef MIGRATING_THREADS + /* first check for common cases */ + + if (notify == MACH_PORT_NULL) switch (MACH_MSGH_BITS_PORTS(mbits)) { + case MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND, 0): { + mach_port_name_t dest_name; + ipc_port_t nsrequest; + rpc_uintptr_t payload; + + /* receiving an asynchronous message */ + + ip_lock(dest); + if (!ip_active(dest)) { + ip_unlock(dest); + break; + } + + /* optimized ipc_object_copyout_dest */ + + assert(dest->ip_srights > 0); + ip_release(dest); + + if (dest->ip_receiver == space) + dest_name = dest->ip_receiver_name; + else + dest_name = MACH_PORT_NULL; + payload = dest->ip_protected_payload; + + if ((--dest->ip_srights == 0) && + ((nsrequest = dest->ip_nsrequest) != IP_NULL)) { + mach_port_mscount_t mscount; + + dest->ip_nsrequest = IP_NULL; + mscount = dest->ip_mscount; + ip_unlock(dest); + + ipc_notify_no_senders(nsrequest, mscount); + } else + ip_unlock(dest); + + if (! ipc_port_flag_protected_payload(dest)) { + msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) | + MACH_MSGH_BITS(0, MACH_MSG_TYPE_PORT_SEND)); + msg->msgh_local_port = dest_name; + } else { + msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) | + MACH_MSGH_BITS( + 0, MACH_MSG_TYPE_PROTECTED_PAYLOAD)); + msg->msgh_protected_payload = payload; + } + msg->msgh_remote_port = MACH_PORT_NULL; + return MACH_MSG_SUCCESS; + } + + case MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND, + MACH_MSG_TYPE_PORT_SEND_ONCE): { + ipc_entry_t entry; + ipc_port_t reply = (ipc_port_t) msg->msgh_local_port; + mach_port_name_t dest_name, reply_name; + ipc_port_t nsrequest; + rpc_uintptr_t payload; + + /* receiving a request message */ + + if (!IP_VALID(reply)) + break; + + is_write_lock(space); + if (!space->is_active || space->is_free_list == NULL) { + is_write_unlock(space); + break; + } + + /* + * To do an atomic copyout, need simultaneous + * locks on both ports and the space. If + * dest == reply, and simple locking is + * enabled, then we will abort. Otherwise it's + * OK to unlock twice. + */ + + ip_lock(dest); + if (!ip_active(dest) || !ip_lock_try(reply)) { + ip_unlock(dest); + is_write_unlock(space); + break; + } + + if (!ip_active(reply)) { + ip_unlock(reply); + ip_unlock(dest); + is_write_unlock(space); + break; + } + + assert(reply->ip_sorights > 0); + ip_unlock(reply); + + kern_return_t kr; + kr = ipc_entry_get (space, &reply_name, &entry); + if (kr) { + ip_unlock(reply); + ip_unlock(dest); + is_write_unlock(space); + break; + } + + { + mach_port_gen_t gen; + + assert((entry->ie_bits &~ IE_BITS_GEN_MASK) == 0); + gen = entry->ie_bits + IE_BITS_GEN_ONE; + + /* optimized ipc_right_copyout */ + + entry->ie_bits = gen | (MACH_PORT_TYPE_SEND_ONCE | 1); + } + + assert(MACH_PORT_NAME_VALID(reply_name)); + entry->ie_object = (ipc_object_t) reply; + is_write_unlock(space); + + /* optimized ipc_object_copyout_dest */ + + assert(dest->ip_srights > 0); + ip_release(dest); + + if (dest->ip_receiver == space) + dest_name = dest->ip_receiver_name; + else + dest_name = MACH_PORT_NULL; + payload = dest->ip_protected_payload; + + if ((--dest->ip_srights == 0) && + ((nsrequest = dest->ip_nsrequest) != IP_NULL)) { + mach_port_mscount_t mscount; + + dest->ip_nsrequest = IP_NULL; + mscount = dest->ip_mscount; + ip_unlock(dest); + + ipc_notify_no_senders(nsrequest, mscount); + } else + ip_unlock(dest); + + if (! ipc_port_flag_protected_payload(dest)) { + msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) | + MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, + MACH_MSG_TYPE_PORT_SEND)); + msg->msgh_local_port = dest_name; + } else { + msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) | + MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, + MACH_MSG_TYPE_PROTECTED_PAYLOAD)); + msg->msgh_protected_payload = payload; + } + msg->msgh_remote_port = reply_name; + return MACH_MSG_SUCCESS; + } + + case MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, 0): { + mach_port_name_t dest_name; + rpc_uintptr_t payload; + + /* receiving a reply message */ + + ip_lock(dest); + if (!ip_active(dest)) { + ip_unlock(dest); + break; + } + + /* optimized ipc_object_copyout_dest */ + + assert(dest->ip_sorights > 0); + + payload = dest->ip_protected_payload; + + if (dest->ip_receiver == space) { + ip_release(dest); + dest->ip_sorights--; + dest_name = dest->ip_receiver_name; + ip_unlock(dest); + } else { + ip_unlock(dest); + + ipc_notify_send_once(dest); + dest_name = MACH_PORT_NULL; + } + + if (! ipc_port_flag_protected_payload(dest)) { + msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) | + MACH_MSGH_BITS(0, + MACH_MSG_TYPE_PORT_SEND_ONCE)); + msg->msgh_local_port = dest_name; + } else { + msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) | + MACH_MSGH_BITS(0, + MACH_MSG_TYPE_PROTECTED_PAYLOAD)); + msg->msgh_protected_payload = payload; + } + msg->msgh_remote_port = MACH_PORT_NULL; + return MACH_MSG_SUCCESS; + } + + default: + /* don't bother optimizing */ + break; + } +#endif /* MIGRATING_THREADS */ + + { + mach_msg_type_name_t dest_type = MACH_MSGH_BITS_REMOTE(mbits); + mach_msg_type_name_t reply_type = MACH_MSGH_BITS_LOCAL(mbits); + ipc_port_t reply = (ipc_port_t) msg->msgh_local_port; + mach_port_name_t dest_name, reply_name; + rpc_uintptr_t payload; + + if (IP_VALID(reply)) { + ipc_port_t notify_port; + ipc_entry_t entry; + kern_return_t kr; + + /* + * Handling notify (for MACH_RCV_NOTIFY) is tricky. + * The problem is atomically making a send-once right + * from the notify port and installing it for a + * dead-name request in the new entry, because this + * requires two port locks (on the notify port and + * the reply port). However, we can safely make + * and consume send-once rights for the notify port + * as long as we hold the space locked. This isn't + * an atomicity problem, because the only way + * to detect that a send-once right has been created + * and then consumed if it wasn't needed is by getting + * at the receive right to look at ip_sorights, and + * because the space is write-locked status calls can't + * lookup the notify port receive right. When we make + * the send-once right, we lock the notify port, + * so any status calls in progress will be done. + */ + + is_write_lock(space); + + for (;;) { + ipc_port_request_index_t request; + + if (!space->is_active) { + is_write_unlock(space); + return (MACH_RCV_HEADER_ERROR| + MACH_MSG_IPC_SPACE); + } + + if (notify != MACH_PORT_NULL) { + notify_port = ipc_port_lookup_notify(space, + notify); + if (notify_port == IP_NULL) { + is_write_unlock(space); + return MACH_RCV_INVALID_NOTIFY; + } + } else + notify_port = IP_NULL; + + if ((reply_type != MACH_MSG_TYPE_PORT_SEND_ONCE) && + ipc_right_reverse(space, (ipc_object_t) reply, + &reply_name, &entry)) { + /* reply port is locked and active */ + + /* + * We don't need the notify_port + * send-once right, but we can't release + * it here because reply port is locked. + * Wait until after the copyout to + * release the notify port right. + */ + + assert(entry->ie_bits & + MACH_PORT_TYPE_SEND_RECEIVE); + break; + } + + ip_lock(reply); + if (!ip_active(reply)) { + ip_release(reply); + ip_check_unlock(reply); + + if (notify_port != IP_NULL) + ipc_port_release_sonce(notify_port); + + ip_lock(dest); + is_write_unlock(space); + + reply = IP_DEAD; + reply_name = MACH_PORT_NAME_DEAD; + goto copyout_dest; + } + + kr = ipc_entry_alloc(space, &reply_name, &entry); + if (kr != KERN_SUCCESS) { + ip_unlock(reply); + + if (notify_port != IP_NULL) + ipc_port_release_sonce(notify_port); + + is_write_unlock(space); + if (kr == KERN_RESOURCE_SHORTAGE) + return (MACH_RCV_HEADER_ERROR| + MACH_MSG_IPC_KERNEL); + else + return (MACH_RCV_HEADER_ERROR| + MACH_MSG_IPC_SPACE); + } + + assert(IE_BITS_TYPE(entry->ie_bits) + == MACH_PORT_TYPE_NONE); + assert(entry->ie_object == IO_NULL); + + if (notify_port == IP_NULL) { + /* not making a dead-name request */ + + entry->ie_object = (ipc_object_t) reply; + break; + } + + kr = ipc_port_dnrequest(reply, reply_name, + notify_port, &request); + if (kr != KERN_SUCCESS) { + ip_unlock(reply); + + ipc_port_release_sonce(notify_port); + + ipc_entry_dealloc(space, reply_name, entry); + is_write_unlock(space); + + ip_lock(reply); + if (!ip_active(reply)) { + /* will fail next time around loop */ + + ip_unlock(reply); + is_write_lock(space); + continue; + } + + kr = ipc_port_dngrow(reply); + /* port is unlocked */ + if (kr != KERN_SUCCESS) + return (MACH_RCV_HEADER_ERROR| + MACH_MSG_IPC_KERNEL); + + is_write_lock(space); + continue; + } + + notify_port = IP_NULL; /* don't release right below */ + + entry->ie_object = (ipc_object_t) reply; + entry->ie_request = request; + break; + } + + /* space and reply port are locked and active */ + + ip_reference(reply); /* hold onto the reply port */ + + kr = ipc_right_copyout(space, reply_name, entry, + reply_type, TRUE, (ipc_object_t) reply); + /* reply port is unlocked */ + assert(kr == KERN_SUCCESS); + + if (notify_port != IP_NULL) + ipc_port_release_sonce(notify_port); + + ip_lock(dest); + is_write_unlock(space); + } else { + /* + * No reply port! This is an easy case. + * We only need to have the space locked + * when checking notify and when locking + * the destination (to ensure atomicity). + */ + + is_read_lock(space); + if (!space->is_active) { + is_read_unlock(space); + return MACH_RCV_HEADER_ERROR|MACH_MSG_IPC_SPACE; + } + + if (notify != MACH_PORT_NULL) { + ipc_entry_t entry; + + /* must check notify even though it won't be used */ + + if (((entry = ipc_entry_lookup(space, notify)) + == IE_NULL) || + ((entry->ie_bits & MACH_PORT_TYPE_RECEIVE) == 0)) { + if (entry == IE_NULL) + ipc_entry_lookup_failed (msg, notify); + is_read_unlock(space); + return MACH_RCV_INVALID_NOTIFY; + } + } + + ip_lock(dest); + is_read_unlock(space); + + reply_name = invalid_port_to_name(msg->msgh_local_port); + } + + /* + * At this point, the space is unlocked and the destination + * port is locked. (Lock taken while space was locked.) + * reply_name is taken care of; we still need dest_name. + * We still hold a ref for reply (if it is valid). + * + * If the space holds receive rights for the destination, + * we return its name for the right. Otherwise the task + * managed to destroy or give away the receive right between + * receiving the message and this copyout. If the destination + * is dead, return MACH_PORT_DEAD, and if the receive right + * exists somewhere else (another space, in transit) + * return MACH_PORT_NULL. + * + * Making this copyout operation atomic with the previous + * copyout of the reply port is a bit tricky. If there was + * no real reply port (it wasn't IP_VALID) then this isn't + * an issue. If the reply port was dead at copyout time, + * then we are OK, because if dest is dead we serialize + * after the death of both ports and if dest is alive + * we serialize after reply died but before dest's (later) death. + * So assume reply was alive when we copied it out. If dest + * is alive, then we are OK because we serialize before + * the ports' deaths. So assume dest is dead when we look at it. + * If reply dies/died after dest, then we are OK because + * we serialize after dest died but before reply dies. + * So the hard case is when reply is alive at copyout, + * dest is dead at copyout, and reply died before dest died. + * In this case pretend that dest is still alive, so + * we serialize while both ports are alive. + * + * Because the space lock is held across the copyout of reply + * and locking dest, the receive right for dest can't move + * in or out of the space while the copyouts happen, so + * that isn't an atomicity problem. In the last hard case + * above, this implies that when dest is dead that the + * space couldn't have had receive rights for dest at + * the time reply was copied-out, so when we pretend + * that dest is still alive, we can return MACH_PORT_NULL. + * + * If dest == reply, then we have to make it look like + * either both copyouts happened before the port died, + * or both happened after the port died. This special + * case works naturally if the timestamp comparison + * is done correctly. + */ + + copyout_dest: + payload = dest->ip_protected_payload; + + if (ip_active(dest)) { + ipc_object_copyout_dest(space, (ipc_object_t) dest, + dest_type, &dest_name); + /* dest is unlocked */ + } else { + ipc_port_timestamp_t timestamp; + + timestamp = dest->ip_timestamp; + ip_release(dest); + ip_check_unlock(dest); + + if (IP_VALID(reply)) { + ip_lock(reply); + if (ip_active(reply) || + IP_TIMESTAMP_ORDER(timestamp, + reply->ip_timestamp)) + dest_name = MACH_PORT_NAME_DEAD; + else + dest_name = MACH_PORT_NAME_NULL; + ip_unlock(reply); + } else + dest_name = MACH_PORT_NAME_DEAD; + } + + if (IP_VALID(reply)) + ipc_port_release(reply); + + if (! ipc_port_flag_protected_payload(dest)) { + msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) | + MACH_MSGH_BITS(reply_type, dest_type)); + msg->msgh_local_port = dest_name; + } else { + msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) | + MACH_MSGH_BITS(reply_type, + MACH_MSG_TYPE_PROTECTED_PAYLOAD)); + msg->msgh_protected_payload = payload; + } + + msg->msgh_remote_port = reply_name; + } + + return MACH_MSG_SUCCESS; +} + +/* + * Routine: ipc_kmsg_copyout_object + * Purpose: + * Copy-out a port right. Always returns a name, + * even for unsuccessful return codes. Always + * consumes the supplied object. + * Conditions: + * Nothing locked. + * Returns: + * MACH_MSG_SUCCESS The space acquired the right + * (name is valid) or the object is dead (MACH_PORT_DEAD). + * MACH_MSG_IPC_SPACE No room in space for the right, + * or the space is dead. (Name is MACH_PORT_NULL.) + * MACH_MSG_IPC_KERNEL Kernel resource shortage. + * (Name is MACH_PORT_NULL.) + */ + +mach_msg_return_t +ipc_kmsg_copyout_object( + ipc_space_t space, + ipc_object_t object, + mach_msg_type_name_t msgt_name, + mach_port_name_t *namep) +{ + if (!IO_VALID(object)) { + *namep = invalid_port_to_name((mach_port_t)object); + return MACH_MSG_SUCCESS; + } + +#ifndef MIGRATING_THREADS + /* + * Attempt quick copyout of send rights. We optimize for a + * live port for which the receiver holds send (and not + * receive) rights in his local table. + */ + + if (msgt_name != MACH_MSG_TYPE_PORT_SEND) + goto slow_copyout; + + { + ipc_port_t port = (ipc_port_t) object; + ipc_entry_t entry; + + is_write_lock(space); + if (!space->is_active) { + is_write_unlock(space); + goto slow_copyout; + } + + ip_lock(port); + if (!ip_active(port) || + (entry = ipc_reverse_lookup(space, + (ipc_object_t) port)) == NULL) { + ip_unlock(port); + is_write_unlock(space); + goto slow_copyout; + } + *namep = entry->ie_name; + + /* + * Copyout the send right, incrementing urefs + * unless it would overflow, and consume the right. + */ + + assert(port->ip_srights > 1); + port->ip_srights--; + ip_release(port); + ip_unlock(port); + + assert(entry->ie_bits & MACH_PORT_TYPE_SEND); + assert(IE_BITS_UREFS(entry->ie_bits) > 0); + assert(IE_BITS_UREFS(entry->ie_bits) < MACH_PORT_UREFS_MAX); + + { + ipc_entry_bits_t bits = entry->ie_bits + 1; + + if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) + entry->ie_bits = bits; + } + + is_write_unlock(space); + return MACH_MSG_SUCCESS; + } + + slow_copyout: +#endif /* MIGRATING_THREADS */ + + { + kern_return_t kr; + + kr = ipc_object_copyout(space, object, msgt_name, TRUE, namep); + if (kr != KERN_SUCCESS) { + ipc_object_destroy(object, msgt_name); + + if (kr == KERN_INVALID_CAPABILITY) + *namep = MACH_PORT_NAME_DEAD; + else { + *namep = MACH_PORT_NAME_NULL; + + if (kr == KERN_RESOURCE_SHORTAGE) + return MACH_MSG_IPC_KERNEL; + else + return MACH_MSG_IPC_SPACE; + } + } + + return MACH_MSG_SUCCESS; + } +} + +/* + * Routine: ipc_kmsg_copyout_body + * Purpose: + * "Copy-out" port rights and out-of-line memory + * in the body of a message. + * + * The error codes are a combination of special bits. + * The copyout proceeds despite errors. + * Conditions: + * Nothing locked. + * Returns: + * MACH_MSG_SUCCESS Successful copyout. + * MACH_MSG_IPC_SPACE No room for port right in name space. + * MACH_MSG_VM_SPACE No room for memory in address space. + * MACH_MSG_IPC_KERNEL Resource shortage handling port right. + * MACH_MSG_VM_KERNEL Resource shortage handling memory. + */ + +mach_msg_return_t +ipc_kmsg_copyout_body( + ipc_kmsg_t kmsg, + ipc_space_t space, + vm_map_t map) +{ + mach_msg_return_t mr = MACH_MSG_SUCCESS; + kern_return_t kr; + vm_offset_t saddr, eaddr; + + saddr = (vm_offset_t) (&kmsg->ikm_header + 1); + eaddr = (vm_offset_t) &kmsg->ikm_header + + kmsg->ikm_header.msgh_size; + + while (saddr < eaddr) { + vm_offset_t taddr = saddr; + mach_msg_type_long_t *type; + mach_msg_type_name_t name; + mach_msg_type_size_t size; + mach_msg_type_number_t number; + boolean_t is_inline, longform, is_port; + uint64_t length; + vm_offset_t addr; + + type = (mach_msg_type_long_t *) saddr; + is_inline = ((mach_msg_type_t*)type)->msgt_inline; + longform = ((mach_msg_type_t*)type)->msgt_longform; + if (longform) { + name = type->msgtl_name; + size = type->msgtl_size; + number = type->msgtl_number; + saddr += sizeof(mach_msg_type_long_t); + if (mach_msg_kernel_is_misaligned(sizeof(mach_msg_type_long_t))) { + saddr = mach_msg_kernel_align(saddr); + } + } else { + name = ((mach_msg_type_t*)type)->msgt_name; + size = ((mach_msg_type_t*)type)->msgt_size; + number = ((mach_msg_type_t*)type)->msgt_number; + saddr += sizeof(mach_msg_type_t); + if (mach_msg_kernel_is_misaligned(sizeof(mach_msg_type_t))) { + saddr = mach_msg_kernel_align(saddr); + } + } + + /* calculate length of data in bytes, rounding up */ + + length = (((uint64_t) number * size) + 7) >> 3; + + is_port = MACH_MSG_TYPE_PORT_ANY(name); + + if (is_port) { + ipc_object_t *objects; + mach_msg_type_number_t i; + + if (!is_inline && (length != 0)) { + /* first allocate memory in the map */ + uint64_t allocated = length; + + _Static_assert(sizeof(mach_port_name_t) <= sizeof(mach_port_t), + "Size of mach_port_t should be equal or larger than mach_port_name_t."); + allocated -= (sizeof(mach_port_t) - sizeof(mach_port_name_t)) * number; + + kr = vm_allocate(map, &addr, allocated, TRUE); + if (kr != KERN_SUCCESS) { + ipc_kmsg_clean_body(taddr, saddr); + goto vm_copyout_failure; + } + } + + objects = (ipc_object_t *) + (is_inline ? saddr : * (vm_offset_t *) saddr); + + /* copyout port rights carried in the message */ + + for (i = 0; i < number; i++) { + ipc_object_t object = objects[i]; + + mr |= ipc_kmsg_copyout_object_to_port(space, object, + name, (mach_port_t *)&objects[i]); + } + } + + if (is_inline) { + ((mach_msg_type_t*)type)->msgt_deallocate = FALSE; + saddr += length; + } else { + vm_offset_t data; + + data = * (vm_offset_t *) saddr; + + /* copyout memory carried in the message */ + + if (length == 0) { + assert(data == 0); + addr = 0; + } else if (is_port) { + /* copyout to memory allocated above */ + + if (sizeof(mach_port_name_t) != sizeof(mach_port_t)) { + mach_port_t *src = (mach_port_t*)data; + mach_port_name_t *dst = (mach_port_name_t*)addr; + for (int i=0; imsgtl_size = 0; + else + ((mach_msg_type_t*)type)->msgt_size = 0; + + if (kr == KERN_RESOURCE_SHORTAGE) + mr |= MACH_MSG_VM_KERNEL; + else + mr |= MACH_MSG_VM_SPACE; + } + } + + ((mach_msg_type_t*)type)->msgt_deallocate = TRUE; + * (vm_offset_t *) saddr = addr; + saddr += sizeof(vm_offset_t); + } + + /* Next element is always correctly aligned */ + saddr = mach_msg_kernel_align(saddr); + } + + return mr; +} + +/* + * Routine: ipc_kmsg_copyout + * Purpose: + * "Copy-out" port rights and out-of-line memory + * in the message. + * Conditions: + * Nothing locked. + * Returns: + * MACH_MSG_SUCCESS Copied out all rights and memory. + * MACH_RCV_INVALID_NOTIFY Bad notify port. + * Rights and memory in the message are intact. + * MACH_RCV_HEADER_ERROR + special bits + * Rights and memory in the message are intact. + * MACH_RCV_BODY_ERROR + special bits + * The message header was successfully copied out. + * As much of the body was handled as possible. + */ + +mach_msg_return_t +ipc_kmsg_copyout( + ipc_kmsg_t kmsg, + ipc_space_t space, + vm_map_t map, + mach_port_name_t notify) +{ + mach_msg_bits_t mbits = kmsg->ikm_header.msgh_bits; + mach_msg_return_t mr; + + mr = ipc_kmsg_copyout_header(&kmsg->ikm_header, space, notify); + if (mr != MACH_MSG_SUCCESS) + return mr; + + if (mbits & MACH_MSGH_BITS_COMPLEX) { + mr = ipc_kmsg_copyout_body(kmsg, space, map); + if (mr != MACH_MSG_SUCCESS) + mr |= MACH_RCV_BODY_ERROR; + } + + return mr; +} + +/* + * Routine: ipc_kmsg_copyout_pseudo + * Purpose: + * Does a pseudo-copyout of the message. + * This is like a regular copyout, except + * that the ports in the header are handled + * as if they are in the body. They aren't reversed. + * + * The error codes are a combination of special bits. + * The copyout proceeds despite errors. + * Conditions: + * Nothing locked. + * Returns: + * MACH_MSG_SUCCESS Successful copyout. + * MACH_MSG_IPC_SPACE No room for port right in name space. + * MACH_MSG_VM_SPACE No room for memory in address space. + * MACH_MSG_IPC_KERNEL Resource shortage handling port right. + * MACH_MSG_VM_KERNEL Resource shortage handling memory. + */ + +mach_msg_return_t +ipc_kmsg_copyout_pseudo( + ipc_kmsg_t kmsg, + ipc_space_t space, + vm_map_t map) +{ + mach_msg_bits_t mbits = kmsg->ikm_header.msgh_bits; + ipc_object_t dest = (ipc_object_t) kmsg->ikm_header.msgh_remote_port; + ipc_object_t reply = (ipc_object_t) kmsg->ikm_header.msgh_local_port; + mach_msg_type_name_t dest_type = MACH_MSGH_BITS_REMOTE(mbits); + mach_msg_type_name_t reply_type = MACH_MSGH_BITS_LOCAL(mbits); + mach_port_name_t dest_name, reply_name; + mach_msg_return_t mr; + + assert(IO_VALID(dest)); + + mr = (ipc_kmsg_copyout_object(space, dest, dest_type, &dest_name) | + ipc_kmsg_copyout_object(space, reply, reply_type, &reply_name)); + + kmsg->ikm_header.msgh_bits = mbits &~ MACH_MSGH_BITS_CIRCULAR; + kmsg->ikm_header.msgh_remote_port = dest_name; + kmsg->ikm_header.msgh_local_port = reply_name; + + if (mbits & MACH_MSGH_BITS_COMPLEX) { + mr |= ipc_kmsg_copyout_body(kmsg, space, map); + } + + return mr; +} + +/* + * Routine: ipc_kmsg_copyout_dest + * Purpose: + * Copies out the destination port in the message. + * Destroys all other rights and memory in the message. + * Conditions: + * Nothing locked. + */ + +void +ipc_kmsg_copyout_dest( + ipc_kmsg_t kmsg, + ipc_space_t space) +{ + mach_msg_bits_t mbits = kmsg->ikm_header.msgh_bits; + ipc_object_t dest = (ipc_object_t) kmsg->ikm_header.msgh_remote_port; + ipc_object_t reply = (ipc_object_t) kmsg->ikm_header.msgh_local_port; + mach_msg_type_name_t dest_type = MACH_MSGH_BITS_REMOTE(mbits); + mach_msg_type_name_t reply_type = MACH_MSGH_BITS_LOCAL(mbits); + mach_port_name_t dest_name, reply_name; + + assert(IO_VALID(dest)); + + io_lock(dest); + if (io_active(dest)) { + ipc_object_copyout_dest(space, dest, dest_type, &dest_name); + /* dest is unlocked */ + } else { + io_release(dest); + io_check_unlock(dest); + dest_name = MACH_PORT_NAME_DEAD; + } + + if (IO_VALID(reply)) { + ipc_object_destroy(reply, reply_type); + reply_name = MACH_PORT_NAME_NULL; + } else + reply_name = invalid_port_to_name((mach_port_t)reply); + + kmsg->ikm_header.msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) | + MACH_MSGH_BITS(reply_type, dest_type)); + kmsg->ikm_header.msgh_local_port = dest_name; + kmsg->ikm_header.msgh_remote_port = reply_name; + + if (mbits & MACH_MSGH_BITS_COMPLEX) { + vm_offset_t saddr, eaddr; + + saddr = (vm_offset_t) (&kmsg->ikm_header + 1); + eaddr = (vm_offset_t) &kmsg->ikm_header + + kmsg->ikm_header.msgh_size; + + ipc_kmsg_clean_body(saddr, eaddr); + } +} + +#if MACH_KDB + +static char * +ipc_type_name( + int type_name, + boolean_t received) +{ + switch (type_name) { + case MACH_MSG_TYPE_BOOLEAN: + return "boolean"; + + case MACH_MSG_TYPE_INTEGER_16: + return "short"; + + case MACH_MSG_TYPE_INTEGER_32: + return "int32"; + + case MACH_MSG_TYPE_INTEGER_64: + return "int64"; + + case MACH_MSG_TYPE_CHAR: + return "char"; + + case MACH_MSG_TYPE_BYTE: + return "byte"; + + case MACH_MSG_TYPE_REAL: + return "real"; + + case MACH_MSG_TYPE_STRING: + return "string"; + + case MACH_MSG_TYPE_PORT_NAME: + return "port_name"; + + case MACH_MSG_TYPE_MOVE_RECEIVE: + if (received) { + return "port_receive"; + } else { + return "move_receive"; + } + + case MACH_MSG_TYPE_MOVE_SEND: + if (received) { + return "port_send"; + } else { + return "move_send"; + } + + case MACH_MSG_TYPE_MOVE_SEND_ONCE: + if (received) { + return "port_send_once"; + } else { + return "move_send_once"; + } + + case MACH_MSG_TYPE_COPY_SEND: + return "copy_send"; + + case MACH_MSG_TYPE_MAKE_SEND: + return "make_send"; + + case MACH_MSG_TYPE_MAKE_SEND_ONCE: + return "make_send_once"; + + default: + return (char *) 0; + } +} + +static void +ipc_print_type_name( + int type_name) +{ + char *name = ipc_type_name(type_name, TRUE); + if (name) { + printf("%s", name); + } else { + printf("type%d", type_name); + } +} + +/* + * ipc_kmsg_print [ debug ] + */ +void +ipc_kmsg_print(ipc_kmsg_t kmsg) +{ + db_printf("kmsg=0x%x\n", kmsg); + db_printf("ikm_next=0x%x,prev=0x%x,size=%d,marequest=0x%x", + kmsg->ikm_next, + kmsg->ikm_prev, + kmsg->ikm_size, + kmsg->ikm_marequest); + db_printf("\n"); + ipc_msg_print(&kmsg->ikm_header); +} + +/* + * ipc_msg_print [ debug ] + */ +void +ipc_msg_print(mach_msg_header_t *msgh) +{ + vm_offset_t saddr, eaddr; + + db_printf("msgh_bits=0x%x: ", msgh->msgh_bits); + if (msgh->msgh_bits & MACH_MSGH_BITS_COMPLEX) { + db_printf("complex,"); + } + if (msgh->msgh_bits & MACH_MSGH_BITS_CIRCULAR) { + db_printf("circular,"); + } + if (msgh->msgh_bits & MACH_MSGH_BITS_COMPLEX_PORTS) { + db_printf("complex_ports,"); + } + if (msgh->msgh_bits & MACH_MSGH_BITS_COMPLEX_DATA) { + db_printf("complex_data,"); + } + if (msgh->msgh_bits & MACH_MSGH_BITS_MIGRATED) { + db_printf("migrated,"); + } + if (msgh->msgh_bits & MACH_MSGH_BITS_UNUSED) { + db_printf("unused=0x%x,", + msgh->msgh_bits & MACH_MSGH_BITS_UNUSED); + } + db_printf("l=0x%x,r=0x%x\n", + MACH_MSGH_BITS_LOCAL(msgh->msgh_bits), + MACH_MSGH_BITS_REMOTE(msgh->msgh_bits)); + + db_printf("msgh_id=%d,size=%d,seqno=%d,", + msgh->msgh_id, + msgh->msgh_size, + msgh->msgh_seqno); + + if (msgh->msgh_remote_port) { + db_printf("remote=0x%x(", msgh->msgh_remote_port); + ipc_print_type_name(MACH_MSGH_BITS_REMOTE(msgh->msgh_bits)); + db_printf("),"); + } else { + db_printf("remote=null,\n"); + } + + if (msgh->msgh_local_port) { + db_printf("local=0x%x(", msgh->msgh_local_port); + ipc_print_type_name(MACH_MSGH_BITS_LOCAL(msgh->msgh_bits)); + db_printf(")\n"); + } else { + db_printf("local=null\n"); + } + + saddr = (vm_offset_t) (msgh + 1); + eaddr = (vm_offset_t) msgh + msgh->msgh_size; + + while (saddr < eaddr) { + mach_msg_type_long_t *type; + mach_msg_type_name_t name; + mach_msg_type_size_t size; + mach_msg_type_number_t number; + boolean_t is_inline, longform, dealloc, is_port; + vm_size_t length; + + type = (mach_msg_type_long_t *) saddr; + + if (((eaddr - saddr) < sizeof(mach_msg_type_t)) || + ((longform = ((mach_msg_type_t*)type)->msgt_longform) && + ((eaddr - saddr) < sizeof(mach_msg_type_long_t)))) { + db_printf("*** msg too small\n"); + return; + } + + is_inline = ((mach_msg_type_t*)type)->msgt_inline; + dealloc = ((mach_msg_type_t*)type)->msgt_deallocate; + if (longform) { + name = type->msgtl_name; + size = type->msgtl_size; + number = type->msgtl_number; + saddr += sizeof(mach_msg_type_long_t); + if (mach_msg_kernel_is_misaligned(sizeof(mach_msg_type_long_t))) { + saddr = mach_msg_kernel_align(saddr); + } + } else { + name = ((mach_msg_type_t*)type)->msgt_name; + size = ((mach_msg_type_t*)type)->msgt_size; + number = ((mach_msg_type_t*)type)->msgt_number; + saddr += sizeof(mach_msg_type_t); + if (mach_msg_kernel_is_misaligned(sizeof(mach_msg_type_t))) { + saddr = mach_msg_kernel_align(saddr); + } + } + + db_printf("-- type="); + ipc_print_type_name(name); + if (! is_inline) { + db_printf(",ool"); + } + if (dealloc) { + db_printf(",dealloc"); + } + if (longform) { + db_printf(",longform"); + } + db_printf(",size=%d,number=%d,addr=0x%x\n", + size, + number, + saddr); + + is_port = MACH_MSG_TYPE_PORT_ANY(name); + + if ((is_port && (size != PORT_T_SIZE_IN_BITS)) || +#ifndef __x86_64__ + (longform && ((type->msgtl_header.msgt_name != 0) || + (type->msgtl_header.msgt_size != 0) || + (type->msgtl_header.msgt_number != 0))) || +#endif + (((mach_msg_type_t*)type)->msgt_unused != 0) || + (dealloc && is_inline)) { + db_printf("*** invalid type\n"); + return; + } + + /* calculate length of data in bytes, rounding up */ + + length = ((number * size) + 7) >> 3; + + if (is_inline) { + vm_size_t amount; + unsigned i, numwords; + + /* round up to int boundaries for printing */ + amount = (length + 3) &~ 3; + if ((eaddr - saddr) < amount) { + db_printf("*** too small\n"); + return; + } + numwords = amount / sizeof(int); + if (numwords > 8) { + numwords = 8; + } + for (i = 0; i < numwords; i++) { + db_printf("0x%x\n", ((int *) saddr)[i]); + } + if (numwords < amount / sizeof(int)) { + db_printf("...\n"); + } + saddr += amount; + } else { + if ((eaddr - saddr) < sizeof(vm_offset_t)) { + db_printf("*** too small\n"); + return; + } + db_printf("0x%x\n", * (vm_offset_t *) saddr); + saddr += sizeof(vm_offset_t); + } + saddr = mach_msg_kernel_align(saddr); + } +} +#endif /* MACH_KDB */ diff --git a/ipc/ipc_kmsg.h b/ipc/ipc_kmsg.h new file mode 100644 index 0000000..9ee1aa4 --- /dev/null +++ b/ipc/ipc_kmsg.h @@ -0,0 +1,345 @@ +/* + * 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. + */ +/* + * File: ipc/ipc_kmsg.h + * Author: Rich Draves + * Date: 1989 + * + * Definitions for kernel messages. + */ + +#ifndef _IPC_IPC_KMSG_H_ +#define _IPC_IPC_KMSG_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * This structure is only the header for a kmsg buffer; + * the actual buffer is normally larger. The rest of the buffer + * holds the body of the message. + * + * In a kmsg, the port fields hold pointers to ports instead + * of port names. These pointers hold references. + * + * The ikm_header.msgh_remote_port field is the destination + * of the message. + */ + +typedef struct ipc_kmsg { + struct ipc_kmsg *ikm_next, *ikm_prev; + vm_size_t ikm_size; + ipc_marequest_t ikm_marequest; + mach_msg_header_t ikm_header; +} *ipc_kmsg_t; + +#define IKM_NULL ((ipc_kmsg_t) 0) + +#define IKM_OVERHEAD \ + (sizeof(struct ipc_kmsg) - sizeof(mach_msg_header_t)) + +#define ikm_plus_overhead(size) ((vm_size_t)((size) + IKM_OVERHEAD)) +#define ikm_less_overhead(size) ((mach_msg_size_t)((size) - IKM_OVERHEAD)) + +#if MACH_IPC_TEST +/* + * For debugging. + */ +#define IKM_BOGUS ((ipc_kmsg_t) 0xffffff10) + +#define ikm_mark_bogus(kmsg) \ +MACRO_BEGIN \ + (kmsg)->ikm_next = IKM_BOGUS; \ + (kmsg)->ikm_prev = IKM_BOGUS; \ +MACRO_END + +#else /* MACH_IPC_TEST */ + +#define ikm_mark_bogus(kmsg) ; + +#endif /* MACH_IPC_TEST */ + +/* + * We keep a per-processor cache of kernel message buffers. + * The cache saves the overhead/locking of using kalloc/kfree. + * The per-processor cache seems to miss less than a per-thread cache, + * and it also uses less memory. Access to the cache doesn't + * require locking. + */ + +extern ipc_kmsg_t ipc_kmsg_cache[NCPUS]; + +#define ikm_cache() ipc_kmsg_cache[cpu_number()] + +#define ikm_cache_alloc_try() \ +MACRO_BEGIN \ + ipc_kmsg_t __kmsg = ikm_cache(); \ + if (__kmsg != IKM_NULL) { \ + ikm_cache() = IKM_NULL; \ + ikm_check_initialized(__kmsg, IKM_SAVED_KMSG_SIZE); \ + } \ + __kmsg; \ +MACRO_END + +#define ikm_cache_alloc() \ +MACRO_BEGIN \ + ipc_kmsg_t __kmsg = ikm_cache_alloc_try(); \ + if (!__kmsg) { \ + __kmsg = ikm_alloc(IKM_SAVED_MSG_SIZE); \ + if (__kmsg != IKM_NULL) \ + ikm_init(__kmsg, IKM_SAVED_MSG_SIZE); \ + } \ + __kmsg; \ +MACRO_END + +#define ikm_cache_free_try(kmsg) \ +MACRO_BEGIN \ + int __success = 0; \ + if (ikm_cache() == IKM_NULL) { \ + ikm_cache() = (kmsg); \ + __success = 1; \ + } \ + __success; \ +MACRO_END + +#define ikm_cache_free(kmsg) \ +MACRO_BEGIN \ + if (((kmsg)->ikm_size == IKM_SAVED_KMSG_SIZE) && \ + (ikm_cache() == IKM_NULL)) \ + ikm_cache() = (kmsg); \ + else \ + ikm_free(kmsg); \ +MACRO_END + +/* + * The size of the kernel message buffers that will be cached. + * IKM_SAVED_KMSG_SIZE includes overhead; IKM_SAVED_MSG_SIZE doesn't. + * + * We use the page size for IKM_SAVED_KMSG_SIZE to make sure the + * page is pinned to a single processor. + */ + +#define IKM_SAVED_KMSG_SIZE PAGE_SIZE +#define IKM_SAVED_MSG_SIZE ikm_less_overhead(IKM_SAVED_KMSG_SIZE) + +#define ikm_alloc(size) \ + ((ipc_kmsg_t) kalloc(ikm_plus_overhead(size))) + +/* + * The conversion between userland and kernel-land has to convert from port + * names to ports. This may increase the size that needs to be allocated + * on the kernel size. At worse the message is full of port names to be + * converted. + */ +#define IKM_EXPAND_FACTOR ((sizeof(mach_port_t) + sizeof(mach_port_name_t) - 1) / sizeof(mach_port_name_t)) +/* But make sure it's not the converse. */ +_Static_assert(sizeof(mach_port_t) >= sizeof(mach_port_name_t)); + +#define ikm_init(kmsg, size) \ +MACRO_BEGIN \ + ikm_init_special((kmsg), ikm_plus_overhead(size)); \ +MACRO_END + +#define ikm_init_special(kmsg, size) \ +MACRO_BEGIN \ + (kmsg)->ikm_size = (size); \ + (kmsg)->ikm_marequest = IMAR_NULL; \ +MACRO_END + +#define ikm_check_initialized(kmsg, size) \ +MACRO_BEGIN \ + assert((kmsg)->ikm_size == (size)); \ + assert((kmsg)->ikm_marequest == IMAR_NULL); \ +MACRO_END + +/* + * Non-positive message sizes are special. They indicate that + * the message buffer doesn't come from ikm_alloc and + * requires some special handling to free. + * + * ipc_kmsg_free is the non-macro form of ikm_free. + * It frees kmsgs of all varieties. + */ + +#define IKM_SIZE_NORMA 0 +#define IKM_SIZE_NETWORK -1 + +#define ikm_free(kmsg) \ +MACRO_BEGIN \ + vm_size_t _size = (kmsg)->ikm_size; \ + \ + if ((integer_t)_size > 0) \ + kfree((vm_offset_t) (kmsg), _size); \ + else \ + ipc_kmsg_free(kmsg); \ +MACRO_END + +/* + * struct ipc_kmsg_queue is defined in ipc/ipc_kmsg_queue.h + */ + +#include + +typedef struct ipc_kmsg_queue *ipc_kmsg_queue_t; + +#define IKMQ_NULL ((ipc_kmsg_queue_t) 0) + + +#define ipc_kmsg_queue_init(queue) \ +MACRO_BEGIN \ + (queue)->ikmq_base = IKM_NULL; \ +MACRO_END + +#define ipc_kmsg_queue_empty(queue) ((queue)->ikmq_base == IKM_NULL) + +/* Enqueue a kmsg */ +extern void ipc_kmsg_enqueue( + ipc_kmsg_queue_t queue, + ipc_kmsg_t kmsg); + +/* Dequeue and return a kmsg */ +extern ipc_kmsg_t ipc_kmsg_dequeue( + ipc_kmsg_queue_t queue); + +/* Pull a kmsg out of a queue */ +extern void ipc_kmsg_rmqueue( + ipc_kmsg_queue_t queue, + ipc_kmsg_t kmsg); + +#define ipc_kmsg_queue_first(queue) ((queue)->ikmq_base) + +/* Return the kmsg following the given kmsg */ +extern ipc_kmsg_t ipc_kmsg_queue_next( + ipc_kmsg_queue_t queue, + ipc_kmsg_t kmsg); + +#define ipc_kmsg_rmqueue_first_macro(queue, kmsg) \ +MACRO_BEGIN \ + ipc_kmsg_t _next; \ + \ + assert((queue)->ikmq_base == (kmsg)); \ + \ + _next = (kmsg)->ikm_next; \ + if (_next == (kmsg)) { \ + assert((kmsg)->ikm_prev == (kmsg)); \ + (queue)->ikmq_base = IKM_NULL; \ + } else { \ + ipc_kmsg_t _prev = (kmsg)->ikm_prev; \ + \ + (queue)->ikmq_base = _next; \ + _next->ikm_prev = _prev; \ + _prev->ikm_next = _next; \ + } \ + ikm_mark_bogus (kmsg); \ +MACRO_END + +#define ipc_kmsg_enqueue_macro(queue, kmsg) \ +MACRO_BEGIN \ + ipc_kmsg_t _first = (queue)->ikmq_base; \ + \ + if (_first == IKM_NULL) { \ + (queue)->ikmq_base = (kmsg); \ + (kmsg)->ikm_next = (kmsg); \ + (kmsg)->ikm_prev = (kmsg); \ + } else { \ + ipc_kmsg_t _last = _first->ikm_prev; \ + \ + (kmsg)->ikm_next = _first; \ + (kmsg)->ikm_prev = _last; \ + _first->ikm_prev = (kmsg); \ + _last->ikm_next = (kmsg); \ + } \ +MACRO_END + +extern void +ipc_kmsg_destroy(ipc_kmsg_t); + +extern void +ipc_kmsg_clean(ipc_kmsg_t); + +extern void +ipc_kmsg_free(ipc_kmsg_t); + +extern mach_msg_return_t +ipc_kmsg_get(mach_msg_user_header_t *, mach_msg_size_t, ipc_kmsg_t *); + +extern mach_msg_return_t +ipc_kmsg_get_from_kernel(mach_msg_header_t *, mach_msg_size_t, ipc_kmsg_t *); + +extern mach_msg_return_t +ipc_kmsg_put(mach_msg_user_header_t *, ipc_kmsg_t, mach_msg_size_t); + +extern void +ipc_kmsg_put_to_kernel(mach_msg_header_t *, ipc_kmsg_t, mach_msg_size_t); + +extern mach_msg_return_t +ipc_kmsg_copyin_header(mach_msg_header_t *, ipc_space_t, mach_port_name_t); + +extern mach_msg_return_t +ipc_kmsg_copyin(ipc_kmsg_t, ipc_space_t, vm_map_t, mach_port_name_t); + +extern void +ipc_kmsg_copyin_from_kernel(ipc_kmsg_t); + +extern mach_msg_return_t +ipc_kmsg_copyout_header(mach_msg_header_t *, ipc_space_t, mach_port_name_t); + +extern mach_msg_return_t +ipc_kmsg_copyout_object(ipc_space_t, ipc_object_t, + mach_msg_type_name_t, mach_port_name_t *); + +static inline mach_msg_return_t +ipc_kmsg_copyout_object_to_port(ipc_space_t space, ipc_object_t object, + mach_msg_type_name_t msgt_name, mach_port_t *portp) +{ + mach_port_name_t name;; + mach_msg_return_t mr; + mr = ipc_kmsg_copyout_object(space, object, msgt_name, &name); + *portp = (mach_port_t)name; + return mr; +} + +extern mach_msg_return_t +ipc_kmsg_copyout_body(ipc_kmsg_t, ipc_space_t, vm_map_t); + +extern mach_msg_return_t +ipc_kmsg_copyout(ipc_kmsg_t, ipc_space_t, vm_map_t, mach_port_name_t); + +extern mach_msg_return_t +ipc_kmsg_copyout_pseudo(ipc_kmsg_t, ipc_space_t, vm_map_t); + +extern void +ipc_kmsg_copyout_dest(ipc_kmsg_t, ipc_space_t); + +#endif /* _IPC_IPC_KMSG_H_ */ diff --git a/ipc/ipc_kmsg_queue.h b/ipc/ipc_kmsg_queue.h new file mode 100644 index 0000000..b4b3df1 --- /dev/null +++ b/ipc/ipc_kmsg_queue.h @@ -0,0 +1,31 @@ +/* + * Mach Operating System + * Copyright (c) 1993 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. + */ +#ifndef _IPC_KMSG_QUEUE_H_ +#define _IPC_KMSG_QUEUE_H_ +struct ipc_kmsg_queue { + struct ipc_kmsg *ikmq_base; }; +#endif /* _IPC_KMSG_QUEUE_H_ */ + diff --git a/ipc/ipc_machdep.h b/ipc/ipc_machdep.h new file mode 100755 index 0000000..2871fc3 --- /dev/null +++ b/ipc/ipc_machdep.h @@ -0,0 +1,39 @@ +/* + * 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. + */ + +#ifndef _IPC_IPC_MACHDEP_H_ +#define _IPC_IPC_MACHDEP_H_ + +#include + +/* + * At times, we need to know the size of a port in bits + */ + +#define PORT_T_SIZE_IN_BITS (sizeof(mach_port_t)*8) +#define PORT_NAME_T_SIZE_IN_BITS (sizeof(mach_port_name_t)*8) + +#endif /* _IPC_IPC_MACHDEP_H_ */ diff --git a/ipc/ipc_marequest.c b/ipc/ipc_marequest.c new file mode 100644 index 0000000..c096fe2 --- /dev/null +++ b/ipc/ipc_marequest.c @@ -0,0 +1,437 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University. + * Copyright (c) 1993,1994 The University of Utah and + * the Computer Systems Laboratory (CSL). + * 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, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF + * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM 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. + */ +/* + * File: ipc/ipc_marequest.c + * Author: Rich Draves + * Date: 1989 + * + * Functions to handle msg-accepted requests. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if MACH_IPC_DEBUG +#include +#include +#include +#include +#include +#endif + + +struct kmem_cache ipc_marequest_cache; + +#define imar_alloc() ((ipc_marequest_t) kmem_cache_alloc(&ipc_marequest_cache)) +#define imar_free(imar) kmem_cache_free(&ipc_marequest_cache, (vm_offset_t) (imar)) + +typedef unsigned int ipc_marequest_index_t; + +ipc_marequest_index_t ipc_marequest_size; +ipc_marequest_index_t ipc_marequest_mask; + +#define IMAR_HASH(space, name) \ + ((((ipc_marequest_index_t)((vm_offset_t)space) >> 4) + \ + MACH_PORT_INDEX(name) + MACH_PORT_NGEN(name)) & \ + ipc_marequest_mask) + +typedef struct ipc_marequest_bucket { + decl_simple_lock_data(, imarb_lock_data) + ipc_marequest_t imarb_head; +} *ipc_marequest_bucket_t; + +#define IMARB_NULL ((ipc_marequest_bucket_t) 0) + +#define imarb_lock_init(imarb) simple_lock_init(&(imarb)->imarb_lock_data) +#define imarb_lock(imarb) simple_lock(&(imarb)->imarb_lock_data) +#define imarb_unlock(imarb) simple_unlock(&(imarb)->imarb_lock_data) + +ipc_marequest_bucket_t ipc_marequest_table; + + + +/* + * Routine: ipc_marequest_init + * Purpose: + * Initialize the msg-accepted request module. + */ + +void +ipc_marequest_init(void) +{ + ipc_marequest_index_t i; + + /* initialize ipc_marequest_size */ + + ipc_marequest_size = IPC_MAREQUEST_SIZE; + + /* make sure it is a power of two */ + + ipc_marequest_mask = ipc_marequest_size - 1; + if ((ipc_marequest_size & ipc_marequest_mask) != 0) { + unsigned int bit; + + /* round up to closest power of two */ + + for (bit = 1;; bit <<= 1) { + ipc_marequest_mask |= bit; + ipc_marequest_size = ipc_marequest_mask + 1; + + if ((ipc_marequest_size & ipc_marequest_mask) == 0) + break; + } + } + + /* allocate ipc_marequest_table */ + + ipc_marequest_table = (ipc_marequest_bucket_t) + kalloc((vm_size_t) (ipc_marequest_size * + sizeof(struct ipc_marequest_bucket))); + assert(ipc_marequest_table != IMARB_NULL); + + /* and initialize it */ + + for (i = 0; i < ipc_marequest_size; i++) { + ipc_marequest_bucket_t bucket; + + bucket = &ipc_marequest_table[i]; + imarb_lock_init(bucket); + bucket->imarb_head = IMAR_NULL; + } + + kmem_cache_init(&ipc_marequest_cache, "ipc_marequest", + sizeof(struct ipc_marequest), 0, NULL, 0); +} + +/* + * Routine: ipc_marequest_create + * Purpose: + * Create a msg-accepted request, because + * a sender is forcing a message with MACH_SEND_NOTIFY. + * + * The "notify" argument should name a receive right + * that is used to create the send-once notify port. + * Conditions: + * Nothing locked; refs held for space and port. + * Returns: + * MACH_MSG_SUCCESS Msg-accepted request created. + * MACH_SEND_INVALID_NOTIFY The space is dead. + * MACH_SEND_INVALID_NOTIFY The notify port is bad. + * MACH_SEND_NOTIFY_IN_PROGRESS + * This space has already forced a message to this port. + * MACH_SEND_NO_NOTIFY Can't allocate a msg-accepted request. + */ + +mach_msg_return_t +ipc_marequest_create( + ipc_space_t space, + ipc_port_t port, + mach_port_name_t notify, + ipc_marequest_t *marequestp) +{ + mach_port_name_t name; + ipc_entry_t entry; + ipc_port_t soright; + ipc_marequest_t marequest; + ipc_marequest_bucket_t bucket; + + marequest = imar_alloc(); + if (marequest == IMAR_NULL) + return MACH_SEND_NO_NOTIFY; + + /* + * Delay creating the send-once right until + * we know there will be no errors. Otherwise, + * we would have to worry about disposing of it + * when it turned out it wasn't needed. + */ + + is_write_lock(space); + if (!space->is_active) { + is_write_unlock(space); + imar_free(marequest); + return MACH_SEND_INVALID_NOTIFY; + } + + if (ipc_right_reverse(space, (ipc_object_t) port, &name, &entry)) { + ipc_entry_bits_t bits; + + /* port is locked and active */ + ip_unlock(port); + bits = entry->ie_bits; + + assert(port == (ipc_port_t) entry->ie_object); + assert(bits & MACH_PORT_TYPE_SEND_RECEIVE); + + if (bits & IE_BITS_MAREQUEST) { + is_write_unlock(space); + imar_free(marequest); + return MACH_SEND_NOTIFY_IN_PROGRESS; + } + + if ((soright = ipc_port_lookup_notify(space, notify)) + == IP_NULL) { + is_write_unlock(space); + imar_free(marequest); + return MACH_SEND_INVALID_NOTIFY; + } + + entry->ie_bits = bits | IE_BITS_MAREQUEST; + + is_reference(space); + marequest->imar_space = space; + marequest->imar_name = name; + marequest->imar_soright = soright; + + bucket = &ipc_marequest_table[IMAR_HASH(space, name)]; + imarb_lock(bucket); + + marequest->imar_next = bucket->imarb_head; + bucket->imarb_head = marequest; + + imarb_unlock(bucket); + } else { + if ((soright = ipc_port_lookup_notify(space, notify)) + == IP_NULL) { + is_write_unlock(space); + imar_free(marequest); + return MACH_SEND_INVALID_NOTIFY; + } + + is_reference(space); + marequest->imar_space = space; + marequest->imar_name = MACH_PORT_NULL; + marequest->imar_soright = soright; + } + + is_write_unlock(space); + *marequestp = marequest; + return MACH_MSG_SUCCESS; +} + +/* + * Routine: ipc_marequest_cancel + * Purpose: + * Cancel a msg-accepted request, because + * the space's entry is being destroyed. + * Conditions: + * The space is write-locked and active. + */ + +void +ipc_marequest_cancel( + ipc_space_t space, + mach_port_name_t name) +{ + ipc_marequest_bucket_t bucket; + ipc_marequest_t marequest, *last; + + assert(space->is_active); + + bucket = &ipc_marequest_table[IMAR_HASH(space, name)]; + imarb_lock(bucket); + + for (last = &bucket->imarb_head; + (marequest = *last) != IMAR_NULL; + last = &marequest->imar_next) + if ((marequest->imar_space == space) && + (marequest->imar_name == name)) + break; + + assert(marequest != IMAR_NULL); + *last = marequest->imar_next; + imarb_unlock(bucket); + + marequest->imar_name = MACH_PORT_NAME_NULL; +} + +/* + * Routine: ipc_marequest_rename + * Purpose: + * Rename a msg-accepted request, because the entry + * in the space is being renamed. + * Conditions: + * The space is write-locked and active. + */ + +void +ipc_marequest_rename( + ipc_space_t space, + mach_port_name_t old, + mach_port_name_t new) +{ + ipc_marequest_bucket_t bucket; + ipc_marequest_t marequest, *last; + + assert(space->is_active); + + bucket = &ipc_marequest_table[IMAR_HASH(space, old)]; + imarb_lock(bucket); + + for (last = &bucket->imarb_head; + (marequest = *last) != IMAR_NULL; + last = &marequest->imar_next) + if ((marequest->imar_space == space) && + (marequest->imar_name == old)) + break; + + assert(marequest != IMAR_NULL); + *last = marequest->imar_next; + imarb_unlock(bucket); + + marequest->imar_name = new; + + bucket = &ipc_marequest_table[IMAR_HASH(space, new)]; + imarb_lock(bucket); + + marequest->imar_next = bucket->imarb_head; + bucket->imarb_head = marequest; + + imarb_unlock(bucket); +} + +/* + * Routine: ipc_marequest_destroy + * Purpose: + * Destroy a msg-accepted request, because + * the kernel message is being received/destroyed. + * Conditions: + * Nothing locked. + */ + +void +ipc_marequest_destroy(ipc_marequest_t marequest) +{ + ipc_space_t space = marequest->imar_space; + mach_port_name_t name; + ipc_port_t soright; + + is_write_lock(space); + + name = marequest->imar_name; + soright = marequest->imar_soright; + + if (name != MACH_PORT_NULL) { + ipc_marequest_bucket_t bucket; + ipc_marequest_t this, *last; + + bucket = &ipc_marequest_table[IMAR_HASH(space, name)]; + imarb_lock(bucket); + + for (last = &bucket->imarb_head; + (this = *last) != IMAR_NULL; + last = &this->imar_next) + if ((this->imar_space == space) && + (this->imar_name == name)) + break; + + assert(this == marequest); + *last = this->imar_next; + imarb_unlock(bucket); + + if (space->is_active) { + ipc_entry_t entry; + + entry = ipc_entry_lookup(space, name); + assert(entry != IE_NULL); + assert(entry->ie_bits & IE_BITS_MAREQUEST); + assert(entry->ie_bits & MACH_PORT_TYPE_SEND_RECEIVE); + + entry->ie_bits &= ~IE_BITS_MAREQUEST; + + } else + name = MACH_PORT_NAME_NULL; + } + + is_write_unlock(space); + is_release(space); + + imar_free(marequest); + + assert(soright != IP_NULL); + ipc_notify_msg_accepted(soright, name); +} + +#if MACH_IPC_DEBUG + + +/* + * Routine: ipc_marequest_info + * Purpose: + * Return information about the marequest hash table. + * Fills the buffer with as much information as possible + * and returns the desired size of the buffer. + * Conditions: + * Nothing locked. The caller should provide + * possibly-pageable memory. + */ + +unsigned int +ipc_marequest_info( + unsigned int *maxp, + hash_info_bucket_t *info, + unsigned int count) +{ + ipc_marequest_index_t i; + + if (ipc_marequest_size < count) + count = ipc_marequest_size; + + for (i = 0; i < count; i++) { + ipc_marequest_bucket_t bucket = &ipc_marequest_table[i]; + unsigned int bucket_count = 0; + ipc_marequest_t marequest; + + imarb_lock(bucket); + for (marequest = bucket->imarb_head; + marequest != IMAR_NULL; + marequest = marequest->imar_next) + bucket_count++; + imarb_unlock(bucket); + + /* don't touch pageable memory while holding locks */ + info[i].hib_count = bucket_count; + } + + *maxp = (unsigned int)-1; + return ipc_marequest_size; +} + +#endif /* MACH_IPC_DEBUG */ diff --git a/ipc/ipc_marequest.h b/ipc/ipc_marequest.h new file mode 100644 index 0000000..a55d4e2 --- /dev/null +++ b/ipc/ipc_marequest.h @@ -0,0 +1,99 @@ +/* + * 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. + */ +/* + * File: ipc/ipc_marequest.h + * Author: Rich Draves + * Date: 1989 + * + * Definitions for msg-accepted requests. + */ + +#ifndef _IPC_IPC_MAREQUEST_H_ +#define _IPC_IPC_MAREQUEST_H_ + +#include +#include +#include +#include + +/* + * A msg-accepted request is made when MACH_SEND_NOTIFY is used + * to force a message to a send right. The IE_BITS_MAREQUEST bit + * in an entry indicates the entry is blocked because MACH_SEND_NOTIFY + * has already been used to force a message. The kmsg holds + * a pointer to the marequest; it is destroyed when the kmsg + * is received/destroyed. (If the send right is destroyed, + * this just changes imar_name. If the space is destroyed, + * the marequest is left unchanged.) + * + * Locking considerations: The imar_space field is read-only and + * points to the space which locks the imar_name field. imar_soright + * is read-only. Normally it is a non-null send-once right for + * the msg-accepted notification, but in compat mode it is null + * and the notification goes to the space's notify port. Normally + * imar_name is non-null, but if the send right is destroyed then + * it is changed to be null. imar_next is locked by a bucket lock; + * imar_name is read-only when the request is in a bucket. (So lookups + * in the bucket can safely check imar_space and imar_name.) + * imar_space and imar_soright both hold references. + */ + +typedef struct ipc_marequest { + struct ipc_space *imar_space; + mach_port_name_t imar_name; + struct ipc_port *imar_soright; + struct ipc_marequest *imar_next; +} *ipc_marequest_t; + +#define IMAR_NULL ((ipc_marequest_t) 0) + +#define IPC_MAREQUEST_SIZE 16 + +extern void +ipc_marequest_init(void); + +#if MACH_IPC_DEBUG + +extern unsigned int +ipc_marequest_info(unsigned int *, hash_info_bucket_t *, unsigned int); + +#endif /* MACH_IPC_DEBUG */ + +extern mach_msg_return_t +ipc_marequest_create(ipc_space_t space, ipc_port_t port, + mach_port_name_t notify, ipc_marequest_t *marequestp); + +extern void +ipc_marequest_cancel(ipc_space_t space, mach_port_name_t name); + +extern void +ipc_marequest_rename(ipc_space_t space, + mach_port_name_t old, mach_port_name_t new); + +extern void +ipc_marequest_destroy(ipc_marequest_t marequest); + +#endif /* _IPC_IPC_MAREQUEST_H_ */ diff --git a/ipc/ipc_mqueue.c b/ipc/ipc_mqueue.c new file mode 100644 index 0000000..44e1eb9 --- /dev/null +++ b/ipc/ipc_mqueue.c @@ -0,0 +1,695 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University. + * Copyright (c) 1993,1994 The University of Utah and + * the Computer Systems Laboratory (CSL). + * 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, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF + * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM 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. + */ +/* + * File: ipc/ipc_mqueue.c + * Author: Rich Draves + * Date: 1989 + * + * Functions to manipulate IPC message queues. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +/* + * Routine: ipc_mqueue_init + * Purpose: + * Initialize a newly-allocated message queue. + */ + +void +ipc_mqueue_init( + ipc_mqueue_t mqueue) +{ + imq_lock_init(mqueue); + ipc_kmsg_queue_init(&mqueue->imq_messages); + ipc_thread_queue_init(&mqueue->imq_threads); +} + +/* + * Routine: ipc_mqueue_move + * Purpose: + * Move messages from one queue (source) to another (dest). + * Only moves messages sent to the specified port. + * Conditions: + * Both queues must be locked. + * (This is sufficient to manipulate port->ip_seqno.) + */ + +void +ipc_mqueue_move( + ipc_mqueue_t dest, + ipc_mqueue_t source, + const ipc_port_t port) +{ + ipc_kmsg_queue_t oldq, newq; + ipc_thread_queue_t blockedq; + ipc_kmsg_t kmsg, next; + ipc_thread_t th; + + oldq = &source->imq_messages; + newq = &dest->imq_messages; + blockedq = &dest->imq_threads; + + for (kmsg = ipc_kmsg_queue_first(oldq); + kmsg != IKM_NULL; kmsg = next) { + next = ipc_kmsg_queue_next(oldq, kmsg); + + /* only move messages sent to port */ + + if (kmsg->ikm_header.msgh_remote_port != (mach_port_t) port) + continue; + + ipc_kmsg_rmqueue(oldq, kmsg); + + /* before adding kmsg to newq, check for a blocked receiver */ + + while ((th = ipc_thread_dequeue(blockedq)) != ITH_NULL) { + assert(ipc_kmsg_queue_empty(newq)); + + thread_go(th); + + /* check if the receiver can handle the message */ + + if (kmsg->ikm_header.msgh_size <= th->ith_msize) { + th->ith_state = MACH_MSG_SUCCESS; + th->ith_kmsg = kmsg; + th->ith_seqno = port->ip_seqno++; + + goto next_kmsg; + } + + th->ith_state = MACH_RCV_TOO_LARGE; + th->ith_msize = kmsg->ikm_header.msgh_size; + } + + /* didn't find a receiver to handle the message */ + + ipc_kmsg_enqueue(newq, kmsg); + next_kmsg:; + } +} + +/* + * Routine: ipc_mqueue_changed + * Purpose: + * Wake up receivers waiting in a message queue. + * Conditions: + * The message queue is locked. + */ + +void +ipc_mqueue_changed( + ipc_mqueue_t mqueue, + mach_msg_return_t mr) +{ + ipc_thread_t th; + + while ((th = ipc_thread_dequeue(&mqueue->imq_threads)) != ITH_NULL) { + th->ith_state = mr; + thread_go(th); + } +} + +/* + * Routine: ipc_mqueue_send + * Purpose: + * Send a message to a port. The message holds a reference + * for the destination port in the msgh_remote_port field. + * + * If unsuccessful, the caller still has possession of + * the message and must do something with it. If successful, + * the message is queued, given to a receiver, destroyed, + * or handled directly by the kernel via mach_msg. + * Conditions: + * Nothing locked. + * Returns: + * MACH_MSG_SUCCESS The message was accepted. + * MACH_SEND_TIMED_OUT Caller still has message. + * MACH_SEND_INTERRUPTED Caller still has message. + */ + +mach_msg_return_t +ipc_mqueue_send( + ipc_kmsg_t kmsg, + mach_msg_option_t option, + mach_msg_timeout_t time_out) +{ + ipc_port_t port; + + port = (ipc_port_t) kmsg->ikm_header.msgh_remote_port; + assert(IP_VALID(port)); + + ip_lock(port); + + if (port->ip_receiver == ipc_space_kernel) { + ipc_kmsg_t reply; + + /* + * We can check ip_receiver == ipc_space_kernel + * before checking that the port is active because + * ipc_port_dealloc_kernel clears ip_receiver + * before destroying a kernel port. + */ + + assert(ip_active(port)); + ip_unlock(port); + + reply = ipc_kobject_server(kmsg); + if (reply != IKM_NULL) + ipc_mqueue_send_always(reply); + + return MACH_MSG_SUCCESS; + } + + for (;;) { + ipc_thread_t self; + + /* + * Can't deliver to a dead port. + * However, we can pretend it got sent + * and was then immediately destroyed. + */ + + if (!ip_active(port)) { + /* + * We can't let ipc_kmsg_destroy deallocate + * the port right, because we might end up + * in an infinite loop trying to deliver + * a send-once notification. + */ + + ip_release(port); + ip_check_unlock(port); + kmsg->ikm_header.msgh_remote_port = MACH_PORT_NULL; + ipc_kmsg_destroy(kmsg); + return MACH_MSG_SUCCESS; + } + + /* + * Don't block if: + * 1) We're under the queue limit. + * 2) Caller used the MACH_SEND_ALWAYS internal option. + * 3) Message is sent to a send-once right. + */ + + if ((port->ip_msgcount < port->ip_qlimit) || + (option & MACH_SEND_ALWAYS) || + (MACH_MSGH_BITS_REMOTE(kmsg->ikm_header.msgh_bits) == + MACH_MSG_TYPE_PORT_SEND_ONCE)) + break; + + /* must block waiting for queue to clear */ + + self = current_thread(); + + if (option & MACH_SEND_TIMEOUT) { + if (time_out == 0) { + ip_unlock(port); + return MACH_SEND_TIMED_OUT; + } + + thread_will_wait_with_timeout(self, time_out); + } else + thread_will_wait(self); + + ipc_thread_enqueue(&port->ip_blocked, self); + self->ith_state = MACH_SEND_IN_PROGRESS; + + ip_unlock(port); + counter(c_ipc_mqueue_send_block++); + thread_block(thread_no_continuation); + ip_lock(port); + + /* why did we wake up? */ + + if (self->ith_state == MACH_MSG_SUCCESS) + continue; + assert(self->ith_state == MACH_SEND_IN_PROGRESS); + + /* take ourselves off blocked queue */ + + ipc_thread_rmqueue(&port->ip_blocked, self); + + /* + * Thread wakeup-reason field tells us why + * the wait was interrupted. + */ + + switch (self->ith_wait_result) { + case THREAD_INTERRUPTED: + /* send was interrupted - give up */ + + ip_unlock(port); + return MACH_SEND_INTERRUPTED; + + case THREAD_TIMED_OUT: + /* timeout expired */ + + assert(option & MACH_SEND_TIMEOUT); + time_out = 0; + break; + + case THREAD_RESTART: + default: +#if MACH_ASSERT + assert(!"ipc_mqueue_send"); +#else + panic("ipc_mqueue_send"); +#endif + } + } + + if (kmsg->ikm_header.msgh_bits & MACH_MSGH_BITS_CIRCULAR) { + ip_unlock(port); + + /* don't allow the creation of a circular loop */ + + ipc_kmsg_destroy(kmsg); + return MACH_MSG_SUCCESS; + } + + { + ipc_mqueue_t mqueue; + ipc_pset_t pset; + ipc_thread_t receiver; + ipc_thread_queue_t receivers; + + port->ip_msgcount++; + assert(port->ip_msgcount > 0); + + pset = port->ip_pset; + if (pset == IPS_NULL) + mqueue = &port->ip_messages; + else + mqueue = &pset->ips_messages; + + imq_lock(mqueue); + receivers = &mqueue->imq_threads; + + /* + * Can unlock the port now that the msg queue is locked + * and we know the port is active. While the msg queue + * is locked, we have control of the kmsg, so the ref in + * it for the port is still good. If the msg queue is in + * a set (dead or alive), then we're OK because the port + * is still a member of the set and the set won't go away + * until the port is taken out, which tries to lock the + * set's msg queue to remove the port's msgs. + */ + + ip_unlock(port); + + /* check for a receiver for the message */ + + for (;;) { + receiver = ipc_thread_queue_first(receivers); + if (receiver == ITH_NULL) { + /* no receivers; queue kmsg */ + + ipc_kmsg_enqueue_macro(&mqueue->imq_messages, kmsg); + imq_unlock(mqueue); + break; + } + + ipc_thread_rmqueue_first_macro(receivers, receiver); + assert(ipc_kmsg_queue_empty(&mqueue->imq_messages)); + + if (kmsg->ikm_header.msgh_size <= receiver->ith_msize) { + /* got a successful receiver */ + + receiver->ith_state = MACH_MSG_SUCCESS; + receiver->ith_kmsg = kmsg; + receiver->ith_seqno = port->ip_seqno++; + imq_unlock(mqueue); + + thread_go(receiver); + break; + } + + receiver->ith_state = MACH_RCV_TOO_LARGE; + receiver->ith_msize = kmsg->ikm_header.msgh_size; + thread_go(receiver); + } + } + + current_task()->messages_sent++; + + return MACH_MSG_SUCCESS; +} + +/* + * Routine: ipc_mqueue_copyin + * Purpose: + * Convert a name in a space to a message queue. + * Conditions: + * Nothing locked. If successful, the message queue + * is returned locked and caller gets a ref for the object. + * This ref ensures the continued existence of the queue. + * Returns: + * MACH_MSG_SUCCESS Found a message queue. + * MACH_RCV_INVALID_NAME The space is dead. + * MACH_RCV_INVALID_NAME The name doesn't denote a right. + * MACH_RCV_INVALID_NAME + * The denoted right is not receive or port set. + * MACH_RCV_IN_SET Receive right is a member of a set. + */ + +mach_msg_return_t +ipc_mqueue_copyin( + ipc_space_t space, + mach_port_name_t name, + ipc_mqueue_t *mqueuep, + ipc_object_t *objectp) +{ + ipc_entry_t entry; + ipc_entry_bits_t bits; + ipc_object_t object; + ipc_mqueue_t mqueue; + + is_read_lock(space); + if (!space->is_active) { + is_read_unlock(space); + return MACH_RCV_INVALID_NAME; + } + + entry = ipc_entry_lookup(space, name); + if (entry == IE_NULL) { + is_read_unlock(space); + return MACH_RCV_INVALID_NAME; + } + + bits = entry->ie_bits; + object = entry->ie_object; + + if (bits & MACH_PORT_TYPE_RECEIVE) { + ipc_port_t port; + ipc_pset_t pset; + + port = (ipc_port_t) object; + assert(port != IP_NULL); + + ip_lock(port); + assert(ip_active(port)); + assert(port->ip_receiver_name == name); + assert(port->ip_receiver == space); + is_read_unlock(space); + + pset = port->ip_pset; + if (pset != IPS_NULL) { + ips_lock(pset); + if (ips_active(pset)) { + ips_unlock(pset); + ip_unlock(port); + return MACH_RCV_IN_SET; + } + + ipc_pset_remove(pset, port); + ips_check_unlock(pset); + assert(port->ip_pset == IPS_NULL); + } + + mqueue = &port->ip_messages; + } else if (bits & MACH_PORT_TYPE_PORT_SET) { + ipc_pset_t pset; + + pset = (ipc_pset_t) object; + assert(pset != IPS_NULL); + + ips_lock(pset); + assert(ips_active(pset)); + assert(pset->ips_local_name == name); + is_read_unlock(space); + + mqueue = &pset->ips_messages; + } else { + is_read_unlock(space); + return MACH_RCV_INVALID_NAME; + } + + /* + * At this point, the object is locked and active, + * the space is unlocked, and mqueue is initialized. + */ + + io_reference(object); + imq_lock(mqueue); + io_unlock(object); + + *objectp = object; + *mqueuep = mqueue; + return MACH_MSG_SUCCESS; +} + +/* + * Routine: ipc_mqueue_receive + * Purpose: + * Receive a message from a message queue. + * + * If continuation is non-zero, then we might discard + * our kernel stack when we block. We will continue + * after unblocking by executing continuation. + * + * If resume is true, then we are resuming a receive + * operation after a blocked receive discarded our stack. + * Conditions: + * The message queue is locked; it will be returned unlocked. + * + * Our caller must hold a reference for the port or port set + * to which this queue belongs, to keep the queue + * from being deallocated. Furthermore, the port or set + * must have been active when the queue was locked. + * + * The kmsg is returned with clean header fields + * and with the circular bit turned off. + * Returns: + * MACH_MSG_SUCCESS Message returned in kmsgp. + * MACH_RCV_TOO_LARGE Message size returned in kmsgp. + * MACH_RCV_TIMED_OUT No message obtained. + * MACH_RCV_INTERRUPTED No message obtained. + * MACH_RCV_PORT_DIED Port/set died; no message. + * MACH_RCV_PORT_CHANGED Port moved into set; no msg. + * + */ + +mach_msg_return_t +ipc_mqueue_receive( + ipc_mqueue_t mqueue, + mach_msg_option_t option, + mach_msg_size_t max_size, + mach_msg_timeout_t time_out, + boolean_t resume, + continuation_t continuation, + ipc_kmsg_t *kmsgp, + mach_port_seqno_t *seqnop) +{ + ipc_port_t port; + ipc_kmsg_t kmsg; + mach_port_seqno_t seqno; + + { + ipc_kmsg_queue_t kmsgs = &mqueue->imq_messages; + ipc_thread_t self = current_thread(); + + if (resume) + goto after_thread_block; + + for (;;) { + kmsg = ipc_kmsg_queue_first(kmsgs); + if (kmsg != IKM_NULL) { + /* check space requirements */ + + if (msg_usize(&kmsg->ikm_header) > max_size) { + * (mach_msg_size_t *) kmsgp = + kmsg->ikm_header.msgh_size; + imq_unlock(mqueue); + return MACH_RCV_TOO_LARGE; + } + + ipc_kmsg_rmqueue_first_macro(kmsgs, kmsg); + port = (ipc_port_t) kmsg->ikm_header.msgh_remote_port; + seqno = port->ip_seqno++; + break; + } + + /* must block waiting for a message */ + + if (option & MACH_RCV_TIMEOUT) { + if (time_out == 0) { + imq_unlock(mqueue); + return MACH_RCV_TIMED_OUT; + } + + thread_will_wait_with_timeout(self, time_out); + } else + thread_will_wait(self); + + ipc_thread_enqueue_macro(&mqueue->imq_threads, self); + self->ith_state = MACH_RCV_IN_PROGRESS; + self->ith_msize = max_size; + + imq_unlock(mqueue); + if (continuation != (void (*)(void)) 0) { + counter(c_ipc_mqueue_receive_block_user++); + } else { + counter(c_ipc_mqueue_receive_block_kernel++); + } + thread_block(continuation); + after_thread_block: + imq_lock(mqueue); + + /* why did we wake up? */ + + if (self->ith_state == MACH_MSG_SUCCESS) { + /* pick up the message that was handed to us */ + + kmsg = self->ith_kmsg; + seqno = self->ith_seqno; + port = (ipc_port_t) kmsg->ikm_header.msgh_remote_port; + break; + } + + switch (self->ith_state) { + case MACH_RCV_TOO_LARGE: + /* pick up size of the too-large message */ + + * (mach_msg_size_t *) kmsgp = self->ith_msize; + /* fall-through */ + + case MACH_RCV_PORT_DIED: + case MACH_RCV_PORT_CHANGED: + /* something bad happened to the port/set */ + + imq_unlock(mqueue); + return self->ith_state; + + case MACH_RCV_IN_PROGRESS: + /* + * Awakened for other than IPC completion. + * Remove ourselves from the waiting queue, + * then check the wakeup cause. + */ + + ipc_thread_rmqueue(&mqueue->imq_threads, self); + + switch (self->ith_wait_result) { + case THREAD_INTERRUPTED: + /* receive was interrupted - give up */ + + imq_unlock(mqueue); + return MACH_RCV_INTERRUPTED; + + case THREAD_TIMED_OUT: + /* timeout expired */ + + assert(option & MACH_RCV_TIMEOUT); + time_out = 0; + break; + + case THREAD_RESTART: + default: +#if MACH_ASSERT + assert(!"ipc_mqueue_receive"); +#else + panic("ipc_mqueue_receive"); +#endif + } + break; + + default: +#if MACH_ASSERT + assert(!"ipc_mqueue_receive: strange ith_state"); +#else + panic("ipc_mqueue_receive: strange ith_state"); +#endif + } + } + + /* we have a kmsg; unlock the msg queue */ + + imq_unlock(mqueue); + assert(msg_usize(&kmsg->ikm_header) <= max_size); + } + + { + ipc_marequest_t marequest; + + marequest = kmsg->ikm_marequest; + if (marequest != IMAR_NULL) { + ipc_marequest_destroy(marequest); + kmsg->ikm_marequest = IMAR_NULL; + } + assert((kmsg->ikm_header.msgh_bits & MACH_MSGH_BITS_CIRCULAR) == 0); + + assert(port == (ipc_port_t) kmsg->ikm_header.msgh_remote_port); + ip_lock(port); + + if (ip_active(port)) { + ipc_thread_queue_t senders; + ipc_thread_t sender; + + assert(port->ip_msgcount > 0); + port->ip_msgcount--; + + senders = &port->ip_blocked; + sender = ipc_thread_queue_first(senders); + + if ((sender != ITH_NULL) && + (port->ip_msgcount < port->ip_qlimit)) { + ipc_thread_rmqueue(senders, sender); + sender->ith_state = MACH_MSG_SUCCESS; + thread_go(sender); + } + } + + ip_unlock(port); + } + + current_task()->messages_received++; + + *kmsgp = kmsg; + *seqnop = seqno; + return MACH_MSG_SUCCESS; +} diff --git a/ipc/ipc_mqueue.h b/ipc/ipc_mqueue.h new file mode 100644 index 0000000..dfac745 --- /dev/null +++ b/ipc/ipc_mqueue.h @@ -0,0 +1,112 @@ +/* + * 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. + */ +/* + * File: ipc/ipc_mqueue.h + * Author: Rich Draves + * Date: 1989 + * + * Definitions for message queues. + */ + +#ifndef _IPC_IPC_MQUEUE_H_ +#define _IPC_IPC_MQUEUE_H_ + +#include +#include +#include +#include +#include +#include +#include + +typedef struct ipc_mqueue { + decl_simple_lock_data(, imq_lock_data) + struct ipc_kmsg_queue imq_messages; + struct ipc_thread_queue imq_threads; +} *ipc_mqueue_t; + +#define IMQ_NULL ((ipc_mqueue_t) 0) + +#define imq_lock_init(mq) simple_lock_init(&(mq)->imq_lock_data) +#define imq_lock(mq) simple_lock(&(mq)->imq_lock_data) +#define imq_lock_try(mq) simple_lock_try(&(mq)->imq_lock_data) +#define imq_unlock(mq) simple_unlock(&(mq)->imq_lock_data) + +extern void +ipc_mqueue_init(ipc_mqueue_t); + +extern void +ipc_mqueue_move(ipc_mqueue_t, ipc_mqueue_t, ipc_port_t); + +extern void +ipc_mqueue_changed(ipc_mqueue_t, mach_msg_return_t); + +extern mach_msg_return_t +ipc_mqueue_send(ipc_kmsg_t, mach_msg_option_t, mach_msg_timeout_t); + +extern mach_msg_return_t +ipc_mqueue_copyin(ipc_space_t, mach_port_name_t, ipc_mqueue_t *, ipc_object_t *); + +#define IMQ_NULL_CONTINUE ((void (*)()) 0) + +extern mach_msg_return_t +ipc_mqueue_receive(ipc_mqueue_t, mach_msg_option_t, + mach_msg_size_t, mach_msg_timeout_t, + boolean_t, continuation_t, + ipc_kmsg_t *, mach_port_seqno_t *); + +/* + * extern void + * ipc_mqueue_send_always(ipc_kmsg_t); + * + * Unfortunately, to avoid warnings/lint about unused variables + * when assertions are turned off, we need two versions of this. + */ + +#include + +#if MACH_ASSERT + +#define ipc_mqueue_send_always(kmsg) \ +MACRO_BEGIN \ + mach_msg_return_t mr; \ + \ + mr = ipc_mqueue_send((kmsg), MACH_SEND_ALWAYS, \ + MACH_MSG_TIMEOUT_NONE); \ + assert(mr == MACH_MSG_SUCCESS); \ +MACRO_END + +#else /* MACH_ASSERT */ + +#define ipc_mqueue_send_always(kmsg) \ +MACRO_BEGIN \ + (void) ipc_mqueue_send((kmsg), MACH_SEND_ALWAYS, \ + MACH_MSG_TIMEOUT_NONE); \ +MACRO_END + +#endif /* MACH_ASSERT */ + +#endif /* _IPC_IPC_MQUEUE_H_ */ diff --git a/ipc/ipc_notify.c b/ipc/ipc_notify.c new file mode 100644 index 0000000..d0b71cf --- /dev/null +++ b/ipc/ipc_notify.c @@ -0,0 +1,449 @@ +/* + * 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. + */ +/* + * File: ipc/ipc_notify.c + * Author: Rich Draves + * Date: 1989 + * + * Notification-sending functions. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +mach_port_deleted_notification_t ipc_notify_port_deleted_template; +mach_msg_accepted_notification_t ipc_notify_msg_accepted_template; +mach_port_destroyed_notification_t ipc_notify_port_destroyed_template; +mach_no_senders_notification_t ipc_notify_no_senders_template; +mach_send_once_notification_t ipc_notify_send_once_template; +mach_dead_name_notification_t ipc_notify_dead_name_template; + +#define NOTIFY_MSGH_SEQNO 0 + +/* + * Routine: ipc_notify_init_port_deleted + * Purpose: + * Initialize a template for port-deleted notifications. + */ + +static void +ipc_notify_init_port_deleted(mach_port_deleted_notification_t *n) +{ + mach_msg_header_t *m = &n->not_header; + mach_msg_type_t *t = &n->not_type; + + m->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, 0); + m->msgh_size = sizeof *n; + m->msgh_seqno = NOTIFY_MSGH_SEQNO; + m->msgh_local_port = MACH_PORT_NULL; + m->msgh_remote_port = MACH_PORT_NULL; + m->msgh_id = MACH_NOTIFY_PORT_DELETED; + + t->msgt_name = MACH_MSG_TYPE_PORT_NAME; + t->msgt_size = PORT_NAME_T_SIZE_IN_BITS; + t->msgt_number = 1; + t->msgt_inline = TRUE; + t->msgt_longform = FALSE; + t->msgt_deallocate = FALSE; + t->msgt_unused = 0; + + n->not_port = MACH_PORT_NULL; +} + +/* + * Routine: ipc_notify_init_msg_accepted + * Purpose: + * Initialize a template for msg-accepted notifications. + */ + +static void +ipc_notify_init_msg_accepted(mach_msg_accepted_notification_t *n) +{ + mach_msg_header_t *m = &n->not_header; + mach_msg_type_t *t = &n->not_type; + + m->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, 0); + m->msgh_size = sizeof *n; + m->msgh_seqno = NOTIFY_MSGH_SEQNO; + m->msgh_local_port = MACH_PORT_NULL; + m->msgh_remote_port = MACH_PORT_NULL; + m->msgh_id = MACH_NOTIFY_MSG_ACCEPTED; + + t->msgt_name = MACH_MSG_TYPE_PORT_NAME; + t->msgt_size = PORT_NAME_T_SIZE_IN_BITS; + t->msgt_number = 1; + t->msgt_inline = TRUE; + t->msgt_longform = FALSE; + t->msgt_deallocate = FALSE; + t->msgt_unused = 0; + + n->not_port = MACH_PORT_NULL; +} + +/* + * Routine: ipc_notify_init_port_destroyed + * Purpose: + * Initialize a template for port-destroyed notifications. + */ + +static void +ipc_notify_init_port_destroyed(mach_port_destroyed_notification_t *n) +{ + mach_msg_header_t *m = &n->not_header; + mach_msg_type_t *t = &n->not_type; + + m->msgh_bits = MACH_MSGH_BITS_COMPLEX | + MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, 0); + m->msgh_size = sizeof *n; + m->msgh_seqno = NOTIFY_MSGH_SEQNO; + m->msgh_local_port = MACH_PORT_NULL; + m->msgh_remote_port = MACH_PORT_NULL; + m->msgh_id = MACH_NOTIFY_PORT_DESTROYED; + + t->msgt_name = MACH_MSG_TYPE_PORT_RECEIVE; + t->msgt_size = PORT_T_SIZE_IN_BITS; + t->msgt_number = 1; + t->msgt_inline = TRUE; + t->msgt_longform = FALSE; + t->msgt_deallocate = FALSE; + t->msgt_unused = 0; + + n->not_port = MACH_PORT_NULL; +} + +/* + * Routine: ipc_notify_init_no_senders + * Purpose: + * Initialize a template for no-senders notifications. + */ + +static void +ipc_notify_init_no_senders( + mach_no_senders_notification_t *n) +{ + mach_msg_header_t *m = &n->not_header; + mach_msg_type_t *t = &n->not_type; + + m->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, 0); + m->msgh_size = sizeof *n; + m->msgh_seqno = NOTIFY_MSGH_SEQNO; + m->msgh_local_port = MACH_PORT_NULL; + m->msgh_remote_port = MACH_PORT_NULL; + m->msgh_id = MACH_NOTIFY_NO_SENDERS; + + t->msgt_name = MACH_MSG_TYPE_INTEGER_32; + t->msgt_size = 32; + t->msgt_number = 1; + t->msgt_inline = TRUE; + t->msgt_longform = FALSE; + t->msgt_deallocate = FALSE; + t->msgt_unused = 0; + + n->not_count = 0; +} + +/* + * Routine: ipc_notify_init_send_once + * Purpose: + * Initialize a template for send-once notifications. + */ + +static void +ipc_notify_init_send_once( + mach_send_once_notification_t *n) +{ + mach_msg_header_t *m = &n->not_header; + + m->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, 0); + m->msgh_size = sizeof *n; + m->msgh_seqno = NOTIFY_MSGH_SEQNO; + m->msgh_local_port = MACH_PORT_NULL; + m->msgh_remote_port = MACH_PORT_NULL; + m->msgh_id = MACH_NOTIFY_SEND_ONCE; +} + +/* + * Routine: ipc_notify_init_dead_name + * Purpose: + * Initialize a template for dead-name notifications. + */ + +static void +ipc_notify_init_dead_name( + mach_dead_name_notification_t *n) +{ + mach_msg_header_t *m = &n->not_header; + mach_msg_type_t *t = &n->not_type; + + m->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, 0); + m->msgh_size = sizeof *n; + m->msgh_seqno = NOTIFY_MSGH_SEQNO; + m->msgh_local_port = MACH_PORT_NULL; + m->msgh_remote_port = MACH_PORT_NULL; + m->msgh_id = MACH_NOTIFY_DEAD_NAME; + + t->msgt_name = MACH_MSG_TYPE_PORT_NAME; + t->msgt_size = PORT_NAME_T_SIZE_IN_BITS; + t->msgt_number = 1; + t->msgt_inline = TRUE; + t->msgt_longform = FALSE; + t->msgt_deallocate = FALSE; + t->msgt_unused = 0; + + n->not_port = MACH_PORT_NULL; +} + +/* + * Routine: ipc_notify_init + * Purpose: + * Initialize the notification subsystem. + */ + +void +ipc_notify_init(void) +{ + ipc_notify_init_port_deleted(&ipc_notify_port_deleted_template); + ipc_notify_init_msg_accepted(&ipc_notify_msg_accepted_template); + ipc_notify_init_port_destroyed(&ipc_notify_port_destroyed_template); + ipc_notify_init_no_senders(&ipc_notify_no_senders_template); + ipc_notify_init_send_once(&ipc_notify_send_once_template); + ipc_notify_init_dead_name(&ipc_notify_dead_name_template); +} + +/* + * Routine: ipc_notify_port_deleted + * Purpose: + * Send a port-deleted notification. + * Conditions: + * Nothing locked. + * Consumes a ref/soright for port. + */ + +void +ipc_notify_port_deleted( + ipc_port_t port, + mach_port_name_t name) +{ + ipc_kmsg_t kmsg; + mach_port_deleted_notification_t *n; + + kmsg = ikm_alloc(sizeof *n); + if (kmsg == IKM_NULL) { + printf("dropped port-deleted (0x%p, 0x%x)\n", port, name); + ipc_port_release_sonce(port); + return; + } + + ikm_init(kmsg, sizeof *n); + n = (mach_port_deleted_notification_t *) &kmsg->ikm_header; + *n = ipc_notify_port_deleted_template; + + n->not_header.msgh_remote_port = (mach_port_t) port; + n->not_port = name; + + ipc_mqueue_send_always(kmsg); +} + +/* + * Routine: ipc_notify_msg_accepted + * Purpose: + * Send a msg-accepted notification. + * Conditions: + * Nothing locked. + * Consumes a ref/soright for port. + */ + +void +ipc_notify_msg_accepted( + ipc_port_t port, + mach_port_name_t name) +{ + ipc_kmsg_t kmsg; + mach_msg_accepted_notification_t *n; + + kmsg = ikm_alloc(sizeof *n); + if (kmsg == IKM_NULL) { + printf("dropped msg-accepted (0x%p, 0x%x)\n", port, name); + ipc_port_release_sonce(port); + return; + } + + ikm_init(kmsg, sizeof *n); + n = (mach_msg_accepted_notification_t *) &kmsg->ikm_header; + *n = ipc_notify_msg_accepted_template; + + n->not_header.msgh_remote_port = (mach_port_t) port; + n->not_port = name; + + ipc_mqueue_send_always(kmsg); +} + +/* + * Routine: ipc_notify_port_destroyed + * Purpose: + * Send a port-destroyed notification. + * Conditions: + * Nothing locked. + * Consumes a ref/soright for port. + * Consumes a ref for right, which should be a receive right + * prepped for placement into a message. (In-transit, + * or in-limbo if a circularity was detected.) + */ + +void +ipc_notify_port_destroyed( + ipc_port_t port, + ipc_port_t right) +{ + ipc_kmsg_t kmsg; + mach_port_destroyed_notification_t *n; + + kmsg = ikm_alloc(sizeof *n); + if (kmsg == IKM_NULL) { + printf("dropped port-destroyed (0x%p, 0x%p)\n", + port, right); + ipc_port_release_sonce(port); + ipc_port_release_receive(right); + return; + } + + ikm_init(kmsg, sizeof *n); + n = (mach_port_destroyed_notification_t *) &kmsg->ikm_header; + *n = ipc_notify_port_destroyed_template; + + n->not_header.msgh_remote_port = (mach_port_t) port; + n->not_port = (mach_port_t) right; + + ipc_mqueue_send_always(kmsg); +} + +/* + * Routine: ipc_notify_no_senders + * Purpose: + * Send a no-senders notification. + * Conditions: + * Nothing locked. + * Consumes a ref/soright for port. + */ + +void +ipc_notify_no_senders( + ipc_port_t port, + mach_port_mscount_t mscount) +{ + ipc_kmsg_t kmsg; + mach_no_senders_notification_t *n; + + kmsg = ikm_alloc(sizeof *n); + if (kmsg == IKM_NULL) { + printf("dropped no-senders (0x%p, %u)\n", port, mscount); + ipc_port_release_sonce(port); + return; + } + + ikm_init(kmsg, sizeof *n); + n = (mach_no_senders_notification_t *) &kmsg->ikm_header; + *n = ipc_notify_no_senders_template; + + n->not_header.msgh_remote_port = (mach_port_t) port; + n->not_count = mscount; + + ipc_mqueue_send_always(kmsg); +} + +/* + * Routine: ipc_notify_send_once + * Purpose: + * Send a send-once notification. + * Conditions: + * Nothing locked. + * Consumes a ref/soright for port. + */ + +void +ipc_notify_send_once(ipc_port_t port) +{ + ipc_kmsg_t kmsg; + mach_send_once_notification_t *n; + + kmsg = ikm_alloc(sizeof *n); + if (kmsg == IKM_NULL) { + printf("dropped send-once (0x%p)\n", port); + ipc_port_release_sonce(port); + return; + } + + ikm_init(kmsg, sizeof *n); + n = (mach_send_once_notification_t *) &kmsg->ikm_header; + *n = ipc_notify_send_once_template; + + n->not_header.msgh_remote_port = (mach_port_t) port; + + ipc_mqueue_send_always(kmsg); +} + +/* + * Routine: ipc_notify_dead_name + * Purpose: + * Send a dead-name notification. + * Conditions: + * Nothing locked. + * Consumes a ref/soright for port. + */ + +void +ipc_notify_dead_name( + ipc_port_t port, + mach_port_name_t name) +{ + ipc_kmsg_t kmsg; + mach_dead_name_notification_t *n; + + kmsg = ikm_alloc(sizeof *n); + if (kmsg == IKM_NULL) { + printf("dropped dead-name (0x%p, 0x%x)\n", port, name); + ipc_port_release_sonce(port); + return; + } + + ikm_init(kmsg, sizeof *n); + n = (mach_dead_name_notification_t *) &kmsg->ikm_header; + *n = ipc_notify_dead_name_template; + + n->not_header.msgh_remote_port = (mach_port_t) port; + n->not_port = name; + + ipc_mqueue_send_always(kmsg); +} diff --git a/ipc/ipc_notify.h b/ipc/ipc_notify.h new file mode 100644 index 0000000..8940f38 --- /dev/null +++ b/ipc/ipc_notify.h @@ -0,0 +1,58 @@ +/* + * 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. + */ +/* + * File: ipc/ipc_notify.h + * Author: Rich Draves + * Date: 1989 + * + * Declarations of notification-sending functions. + */ + +#ifndef _IPC_IPC_NOTIFY_H_ +#define _IPC_IPC_NOTIFY_H_ + +extern void +ipc_notify_init(void); + +extern void +ipc_notify_port_deleted(ipc_port_t, mach_port_name_t); + +extern void +ipc_notify_msg_accepted(ipc_port_t, mach_port_name_t); + +extern void +ipc_notify_port_destroyed(ipc_port_t, ipc_port_t); + +extern void +ipc_notify_no_senders(ipc_port_t, mach_port_mscount_t); + +extern void +ipc_notify_send_once(ipc_port_t); + +extern void +ipc_notify_dead_name(ipc_port_t, mach_port_name_t); + +#endif /* _IPC_IPC_NOTIFY_H_ */ diff --git a/ipc/ipc_object.c b/ipc/ipc_object.c new file mode 100644 index 0000000..1074fb2 --- /dev/null +++ b/ipc/ipc_object.c @@ -0,0 +1,969 @@ +/* + * 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. + */ +/* + * File: ipc/ipc_object.c + * Author: Rich Draves + * Date: 1989 + * + * Functions to manipulate IPC objects. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if MACH_KDB +#include +#endif /* MACH_KDB */ + + +struct kmem_cache ipc_object_caches[IOT_NUMBER]; + + + +/* + * Routine: ipc_object_reference + * Purpose: + * Take a reference to an object. + */ + +void +ipc_object_reference( + ipc_object_t object) +{ + io_lock(object); + assert(object->io_references > 0); + io_reference(object); + io_unlock(object); +} + +/* + * Routine: ipc_object_release + * Purpose: + * Release a reference to an object. + */ + +void +ipc_object_release( + ipc_object_t object) +{ + io_lock(object); + assert(object->io_references > 0); + io_release(object); + io_check_unlock(object); +} + +/* + * Routine: ipc_object_translate + * Purpose: + * Look up an object in a space. + * Conditions: + * Nothing locked before. If successful, the object + * is returned locked. The caller doesn't get a ref. + * Returns: + * KERN_SUCCESS Objected returned locked. + * KERN_INVALID_TASK The space is dead. + * KERN_INVALID_NAME The name doesn't denote a right. + * KERN_INVALID_RIGHT Name doesn't denote the correct right. + */ + +kern_return_t +ipc_object_translate( + ipc_space_t space, + mach_port_name_t name, + mach_port_right_t right, + ipc_object_t *objectp) +{ + ipc_entry_t entry; + ipc_object_t object; + kern_return_t kr; + + kr = ipc_right_lookup_read(space, name, &entry); + if (kr != KERN_SUCCESS) + return kr; + /* space is read-locked and active */ + + if ((entry->ie_bits & MACH_PORT_TYPE(right)) == (mach_port_right_t) 0) { + is_read_unlock(space); + return KERN_INVALID_RIGHT; + } + + object = entry->ie_object; + assert(object != IO_NULL); + + io_lock(object); + is_read_unlock(space); + + *objectp = object; + return KERN_SUCCESS; +} + +/* + * Routine: ipc_object_alloc_dead + * Purpose: + * Allocate a dead-name entry. + * Conditions: + * Nothing locked. + * Returns: + * KERN_SUCCESS The dead name is allocated. + * KERN_INVALID_TASK The space is dead. + * KERN_NO_SPACE No room for an entry in the space. + * KERN_RESOURCE_SHORTAGE Couldn't allocate memory. + */ + +kern_return_t +ipc_object_alloc_dead( + ipc_space_t space, + mach_port_name_t *namep) +{ + ipc_entry_t entry; + kern_return_t kr; + + is_write_lock(space); + kr = ipc_entry_alloc(space, namep, &entry); + if (kr != KERN_SUCCESS) { + is_write_unlock(space); + return kr; + } + + /* null object, MACH_PORT_TYPE_DEAD_NAME, 1 uref */ + + assert(entry->ie_object == IO_NULL); + entry->ie_bits |= MACH_PORT_TYPE_DEAD_NAME | 1; + + is_write_unlock(space); + return KERN_SUCCESS; +} + +/* + * Routine: ipc_object_alloc_dead_name + * Purpose: + * Allocate a dead-name entry, with a specific name. + * Conditions: + * Nothing locked. + * Returns: + * KERN_SUCCESS The dead name is allocated. + * KERN_INVALID_TASK The space is dead. + * KERN_NAME_EXISTS The name already denotes a right. + * KERN_RESOURCE_SHORTAGE Couldn't allocate memory. + */ + +kern_return_t +ipc_object_alloc_dead_name( + ipc_space_t space, + mach_port_name_t name) +{ + ipc_entry_t entry; + kern_return_t kr; + + is_write_lock(space); + kr = ipc_entry_alloc_name(space, name, &entry); + if (kr != KERN_SUCCESS) { + is_write_unlock(space); + return kr; + } + + if (ipc_right_inuse(space, name, entry)) + return KERN_NAME_EXISTS; + + /* null object, MACH_PORT_TYPE_DEAD_NAME, 1 uref */ + + assert(entry->ie_object == IO_NULL); + entry->ie_bits |= MACH_PORT_TYPE_DEAD_NAME | 1; + + is_write_unlock(space); + return KERN_SUCCESS; +} + +/* + * Routine: ipc_object_alloc + * Purpose: + * Allocate an object. + * Conditions: + * Nothing locked. If successful, the object is returned locked. + * The caller doesn't get a reference for the object. + * Returns: + * KERN_SUCCESS The object is allocated. + * KERN_INVALID_TASK The space is dead. + * KERN_NO_SPACE No room for an entry in the space. + * KERN_RESOURCE_SHORTAGE Couldn't allocate memory. + */ + +kern_return_t +ipc_object_alloc( + ipc_space_t space, + ipc_object_type_t otype, + mach_port_type_t type, + mach_port_urefs_t urefs, + mach_port_name_t *namep, + ipc_object_t *objectp) +{ + ipc_object_t object; + ipc_entry_t entry; + kern_return_t kr; + + assert(otype < IOT_NUMBER); + assert((type & MACH_PORT_TYPE_ALL_RIGHTS) == type); + assert(type != MACH_PORT_TYPE_NONE); + assert(urefs <= MACH_PORT_UREFS_MAX); + + object = io_alloc(otype); + if (object == IO_NULL) + return KERN_RESOURCE_SHORTAGE; + + if (otype == IOT_PORT) { + ipc_port_t port = (ipc_port_t)object; + + memset(port, 0, sizeof(*port)); + } else if (otype == IOT_PORT_SET) { + ipc_pset_t pset = (ipc_pset_t)object; + + memset(pset, 0, sizeof(*pset)); + } + is_write_lock(space); + kr = ipc_entry_alloc(space, namep, &entry); + if (kr != KERN_SUCCESS) { + is_write_unlock(space); + io_free(otype, object); + return kr; + } + + entry->ie_bits |= type | urefs; + entry->ie_object = object; + + io_lock_init(object); + io_lock(object); + is_write_unlock(space); + + object->io_references = 1; /* for entry, not caller */ + object->io_bits = io_makebits(TRUE, otype, 0); + + *objectp = object; + return KERN_SUCCESS; +} + +/* + * Routine: ipc_object_alloc_name + * Purpose: + * Allocate an object, with a specific name. + * Conditions: + * Nothing locked. If successful, the object is returned locked. + * The caller doesn't get a reference for the object. + * Returns: + * KERN_SUCCESS The object is allocated. + * KERN_INVALID_TASK The space is dead. + * KERN_NAME_EXISTS The name already denotes a right. + * KERN_RESOURCE_SHORTAGE Couldn't allocate memory. + */ + +kern_return_t +ipc_object_alloc_name( + ipc_space_t space, + ipc_object_type_t otype, + mach_port_type_t type, + mach_port_urefs_t urefs, + mach_port_name_t name, + ipc_object_t *objectp) +{ + ipc_object_t object; + ipc_entry_t entry; + kern_return_t kr; + + assert(otype < IOT_NUMBER); + assert((type & MACH_PORT_TYPE_ALL_RIGHTS) == type); + assert(type != MACH_PORT_TYPE_NONE); + assert(urefs <= MACH_PORT_UREFS_MAX); + + object = io_alloc(otype); + if (object == IO_NULL) + return KERN_RESOURCE_SHORTAGE; + + if (otype == IOT_PORT) { + ipc_port_t port = (ipc_port_t)object; + + memset(port, 0, sizeof(*port)); + } else if (otype == IOT_PORT_SET) { + ipc_pset_t pset = (ipc_pset_t)object; + + memset(pset, 0, sizeof(*pset)); + } + + is_write_lock(space); + kr = ipc_entry_alloc_name(space, name, &entry); + if (kr != KERN_SUCCESS) { + is_write_unlock(space); + io_free(otype, object); + return kr; + } + + if (ipc_right_inuse(space, name, entry)) { + io_free(otype, object); + return KERN_NAME_EXISTS; + } + + entry->ie_bits |= type | urefs; + entry->ie_object = object; + + io_lock_init(object); + io_lock(object); + is_write_unlock(space); + + object->io_references = 1; /* for entry, not caller */ + object->io_bits = io_makebits(TRUE, otype, 0); + + *objectp = object; + return KERN_SUCCESS; +} + +/* + * Routine: ipc_object_copyin_type + * Purpose: + * Convert a send type name to a received type name. + */ + +mach_msg_type_name_t +ipc_object_copyin_type( + mach_msg_type_name_t msgt_name) +{ + switch (msgt_name) { + case 0: + return 0; + + case MACH_MSG_TYPE_MOVE_RECEIVE: + return MACH_MSG_TYPE_PORT_RECEIVE; + + case MACH_MSG_TYPE_MOVE_SEND_ONCE: + case MACH_MSG_TYPE_MAKE_SEND_ONCE: + return MACH_MSG_TYPE_PORT_SEND_ONCE; + + case MACH_MSG_TYPE_MOVE_SEND: + case MACH_MSG_TYPE_MAKE_SEND: + case MACH_MSG_TYPE_COPY_SEND: + return MACH_MSG_TYPE_PORT_SEND; + + default: +#if MACH_ASSERT + assert(!"ipc_object_copyin_type: strange rights"); +#else + panic("ipc_object_copyin_type: strange rights"); +#endif + return 0; /* in case assert/panic returns */ + } +} + +/* + * Routine: ipc_object_copyin + * Purpose: + * Copyin a capability from a space. + * If successful, the caller gets a ref + * for the resulting object, unless it is IO_DEAD. + * Conditions: + * Nothing locked. + * Returns: + * KERN_SUCCESS Acquired an object, possibly IO_DEAD. + * KERN_INVALID_TASK The space is dead. + * KERN_INVALID_NAME Name doesn't exist in space. + * KERN_INVALID_RIGHT Name doesn't denote correct right. + */ + +kern_return_t +ipc_object_copyin( + ipc_space_t space, + mach_port_name_t name, + mach_msg_type_name_t msgt_name, + ipc_object_t *objectp) +{ + ipc_entry_t entry; + ipc_port_t soright; + kern_return_t kr; + + /* + * Could first try a read lock when doing + * MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND, + * and MACH_MSG_TYPE_MAKE_SEND_ONCE. + */ + + kr = ipc_right_lookup_write(space, name, &entry); + if (kr != KERN_SUCCESS) + return kr; + /* space is write-locked and active */ + + kr = ipc_right_copyin(space, name, entry, + msgt_name, TRUE, + objectp, &soright); + if (IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_NONE) + ipc_entry_dealloc(space, name, entry); + is_write_unlock(space); + + if ((kr == KERN_SUCCESS) && (soright != IP_NULL)) + ipc_notify_port_deleted(soright, name); + + return kr; +} + +/* + * Routine: ipc_object_copyin_from_kernel + * Purpose: + * Copyin a naked capability from the kernel. + * + * MACH_MSG_TYPE_MOVE_RECEIVE + * The receiver must be ipc_space_kernel. + * Consumes the naked receive right. + * MACH_MSG_TYPE_COPY_SEND + * A naked send right must be supplied. + * The port gains a reference, and a send right + * if the port is still active. + * MACH_MSG_TYPE_MAKE_SEND + * The receiver must be ipc_space_kernel. + * The port gains a reference and a send right. + * MACH_MSG_TYPE_MOVE_SEND + * Consumes a naked send right. + * MACH_MSG_TYPE_MAKE_SEND_ONCE + * The receiver must be ipc_space_kernel. + * The port gains a reference and a send-once right. + * MACH_MSG_TYPE_MOVE_SEND_ONCE + * Consumes a naked send-once right. + * Conditions: + * Nothing locked. + */ + +void +ipc_object_copyin_from_kernel( + ipc_object_t object, + mach_msg_type_name_t msgt_name) +{ + assert(IO_VALID(object)); + + switch (msgt_name) { + case MACH_MSG_TYPE_MOVE_RECEIVE: { + ipc_port_t port = (ipc_port_t) object; + + ip_lock(port); + assert(ip_active(port)); + assert(port->ip_receiver_name != MACH_PORT_NULL); + assert(port->ip_receiver == ipc_space_kernel); + + /* relevant part of ipc_port_clear_receiver */ + ipc_port_set_mscount(port, 0); + + port->ip_receiver_name = MACH_PORT_NULL; + port->ip_destination = IP_NULL; + ipc_port_flag_protected_payload_clear(port); + ip_unlock(port); + break; + } + + case MACH_MSG_TYPE_COPY_SEND: { + ipc_port_t port = (ipc_port_t) object; + + ip_lock(port); + if (ip_active(port)) { + assert(port->ip_srights > 0); + port->ip_srights++; + } + ip_reference(port); + ip_unlock(port); + break; + } + + case MACH_MSG_TYPE_MAKE_SEND: { + ipc_port_t port = (ipc_port_t) object; + + ip_lock(port); + assert(ip_active(port)); + assert(port->ip_receiver_name != MACH_PORT_NULL); + assert(port->ip_receiver == ipc_space_kernel); + + ip_reference(port); + port->ip_mscount++; + port->ip_srights++; + ip_unlock(port); + break; + } + + case MACH_MSG_TYPE_MOVE_SEND: + /* move naked send right into the message */ + break; + + case MACH_MSG_TYPE_MAKE_SEND_ONCE: { + ipc_port_t port = (ipc_port_t) object; + + ip_lock(port); + assert(ip_active(port)); + assert(port->ip_receiver_name != MACH_PORT_NULL); + assert(port->ip_receiver == ipc_space_kernel); + + ip_reference(port); + port->ip_sorights++; + ip_unlock(port); + break; + } + + case MACH_MSG_TYPE_MOVE_SEND_ONCE: + /* move naked send-once right into the message */ + break; + + default: +#if MACH_ASSERT + assert(!"ipc_object_copyin_from_kernel: strange rights"); +#else + panic("ipc_object_copyin_from_kernel: strange rights"); +#endif + } +} + +/* + * Routine: ipc_object_destroy + * Purpose: + * Destroys a naked capability. + * Consumes a ref for the object. + * + * A receive right should be in limbo or in transit. + * Conditions: + * Nothing locked. + */ + +void +ipc_object_destroy( + ipc_object_t object, + mach_msg_type_name_t msgt_name) +{ + assert(IO_VALID(object)); + assert(io_otype(object) == IOT_PORT); + + switch (msgt_name) { + case MACH_MSG_TYPE_PORT_SEND: + ipc_port_release_send((ipc_port_t) object); + break; + + case MACH_MSG_TYPE_PORT_SEND_ONCE: + ipc_notify_send_once((ipc_port_t) object); + break; + + case MACH_MSG_TYPE_PORT_RECEIVE: + ipc_port_release_receive((ipc_port_t) object); + break; + + default: + panic("ipc_object_destroy: strange rights"); + } +} + +/* + * Routine: ipc_object_copyout + * Purpose: + * Copyout a capability, placing it into a space. + * If successful, consumes a ref for the object. + * Conditions: + * Nothing locked. + * Returns: + * KERN_SUCCESS Copied out object, consumed ref. + * KERN_INVALID_TASK The space is dead. + * KERN_INVALID_CAPABILITY The object is dead. + * KERN_NO_SPACE No room in space for another right. + * KERN_RESOURCE_SHORTAGE No memory available. + * KERN_UREFS_OVERFLOW Urefs limit exceeded + * and overflow wasn't specified. + */ + +kern_return_t +ipc_object_copyout( + ipc_space_t space, + ipc_object_t object, + mach_msg_type_name_t msgt_name, + boolean_t overflow, + mach_port_name_t *namep) +{ + mach_port_name_t name; + ipc_entry_t entry; + kern_return_t kr; + + assert(IO_VALID(object)); + assert(io_otype(object) == IOT_PORT); + + is_write_lock(space); + + for (;;) { + if (!space->is_active) { + is_write_unlock(space); + return KERN_INVALID_TASK; + } + + if ((msgt_name != MACH_MSG_TYPE_PORT_SEND_ONCE) && + ipc_right_reverse(space, object, &name, &entry)) { + /* object is locked and active */ + + assert(entry->ie_bits & MACH_PORT_TYPE_SEND_RECEIVE); + break; + } + + kr = ipc_entry_alloc(space, &name, &entry); + if (kr != KERN_SUCCESS) { + is_write_unlock(space); + return kr; + } + + assert(IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_NONE); + assert(entry->ie_object == IO_NULL); + + io_lock(object); + if (!io_active(object)) { + io_unlock(object); + ipc_entry_dealloc(space, name, entry); + is_write_unlock(space); + return KERN_INVALID_CAPABILITY; + } + + entry->ie_object = object; + break; + } + + /* space is write-locked and active, object is locked and active */ + + kr = ipc_right_copyout(space, name, entry, + msgt_name, overflow, object); + /* object is unlocked */ + is_write_unlock(space); + + if (kr == KERN_SUCCESS) + *namep = name; + return kr; +} + +/* + * Routine: ipc_object_copyout_name + * Purpose: + * Copyout a capability, placing it into a space. + * The specified name is used for the capability. + * If successful, consumes a ref for the object. + * Conditions: + * Nothing locked. + * Returns: + * KERN_SUCCESS Copied out object, consumed ref. + * KERN_INVALID_TASK The space is dead. + * KERN_INVALID_CAPABILITY The object is dead. + * KERN_RESOURCE_SHORTAGE No memory available. + * KERN_UREFS_OVERFLOW Urefs limit exceeded + * and overflow wasn't specified. + * KERN_RIGHT_EXISTS Space has rights under another name. + * KERN_NAME_EXISTS Name is already used. + */ + +kern_return_t +ipc_object_copyout_name( + ipc_space_t space, + ipc_object_t object, + mach_msg_type_name_t msgt_name, + boolean_t overflow, + mach_port_name_t name) +{ + mach_port_name_t oname; + ipc_entry_t oentry; + ipc_entry_t entry; + kern_return_t kr; + + assert(IO_VALID(object)); + assert(io_otype(object) == IOT_PORT); + + is_write_lock(space); + kr = ipc_entry_alloc_name(space, name, &entry); + if (kr != KERN_SUCCESS) { + is_write_unlock(space); + return kr; + } + + if ((msgt_name != MACH_MSG_TYPE_PORT_SEND_ONCE) && + ipc_right_reverse(space, object, &oname, &oentry)) { + /* object is locked and active */ + + if (name != oname) { + io_unlock(object); + + if (IE_BITS_TYPE(entry->ie_bits) + == MACH_PORT_TYPE_NONE) + ipc_entry_dealloc(space, name, entry); + + is_write_unlock(space); + return KERN_RIGHT_EXISTS; + } + + assert(entry == oentry); + assert(entry->ie_bits & MACH_PORT_TYPE_SEND_RECEIVE); + } else { + if (ipc_right_inuse(space, name, entry)) + return KERN_NAME_EXISTS; + + assert(IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_NONE); + assert(entry->ie_object == IO_NULL); + + io_lock(object); + if (!io_active(object)) { + io_unlock(object); + ipc_entry_dealloc(space, name, entry); + is_write_unlock(space); + return KERN_INVALID_CAPABILITY; + } + + entry->ie_object = object; + } + + /* space is write-locked and active, object is locked and active */ + + kr = ipc_right_copyout(space, name, entry, + msgt_name, overflow, object); + /* object is unlocked */ + is_write_unlock(space); + return kr; +} + +/* + * Routine: ipc_object_copyout_dest + * Purpose: + * Translates/consumes the destination right of a message. + * This is unlike normal copyout because the right is consumed + * in a funny way instead of being given to the receiving space. + * The receiver gets his name for the port, if he has receive + * rights, otherwise MACH_PORT_NULL. + * Conditions: + * The object is locked and active. Nothing else locked. + * The object is unlocked and loses a reference. + */ + +void +ipc_object_copyout_dest( + ipc_space_t space, + ipc_object_t object, + mach_msg_type_name_t msgt_name, + mach_port_name_t *namep) +{ + mach_port_name_t name; + + assert(IO_VALID(object)); + assert(io_active(object)); + + io_release(object); + + /* + * If the space is the receiver/owner of the object, + * then we quietly consume the right and return + * the space's name for the object. Otherwise + * we destroy the right and return MACH_PORT_NULL. + */ + + switch (msgt_name) { + case MACH_MSG_TYPE_PORT_SEND: { + ipc_port_t port = (ipc_port_t) object; + ipc_port_t nsrequest = IP_NULL; + mach_port_mscount_t mscount = 0; /* '=0' to shut up lint */ + + assert(port->ip_srights > 0); + if (--port->ip_srights == 0) { + nsrequest = port->ip_nsrequest; + if (nsrequest != IP_NULL) { + port->ip_nsrequest = IP_NULL; + mscount = port->ip_mscount; + } + } + + if (port->ip_receiver == space) + name = port->ip_receiver_name; + else + name = MACH_PORT_NAME_NULL; + + ip_unlock(port); + + if (nsrequest != IP_NULL) + ipc_notify_no_senders(nsrequest, mscount); + + break; + } + + case MACH_MSG_TYPE_PORT_SEND_ONCE: { + ipc_port_t port = (ipc_port_t) object; + + assert(port->ip_sorights > 0); + + if (port->ip_receiver == space) { + /* quietly consume the send-once right */ + + port->ip_sorights--; + name = port->ip_receiver_name; + ip_unlock(port); + } else { + /* + * A very bizarre case. The message + * was received, but before this copyout + * happened the space lost receive rights. + * We can't quietly consume the soright + * out from underneath some other task, + * so generate a send-once notification. + */ + + ip_reference(port); /* restore ref */ + ip_unlock(port); + + ipc_notify_send_once(port); + name = MACH_PORT_NAME_NULL; + } + + break; + } + + default: +#if MACH_ASSERT + assert(!"ipc_object_copyout_dest: strange rights"); +#else + panic("ipc_object_copyout_dest: strange rights"); +#endif + + } + + *namep = name; +} + +/* + * Routine: ipc_object_rename + * Purpose: + * Rename an entry in a space. + * Conditions: + * Nothing locked. + * Returns: + * KERN_SUCCESS Renamed the entry. + * KERN_INVALID_TASK The space was dead. + * KERN_INVALID_NAME oname didn't denote an entry. + * KERN_NAME_EXISTS nname already denoted an entry. + * KERN_RESOURCE_SHORTAGE Couldn't allocate new entry. + */ + +kern_return_t +ipc_object_rename( + ipc_space_t space, + mach_port_name_t oname, + mach_port_name_t nname) +{ + ipc_entry_t oentry, nentry; + kern_return_t kr; + + is_write_lock(space); + kr = ipc_entry_alloc_name(space, nname, &nentry); + if (kr != KERN_SUCCESS) { + is_write_unlock(space); + return kr; + } + + if (ipc_right_inuse(space, nname, nentry)) { + /* space is unlocked */ + return KERN_NAME_EXISTS; + } + + /* don't let ipc_entry_lookup see the uninitialized new entry */ + + if ((oname == nname) || + ((oentry = ipc_entry_lookup(space, oname)) == IE_NULL)) { + ipc_entry_dealloc(space, nname, nentry); + is_write_unlock(space); + return KERN_INVALID_NAME; + } + + kr = ipc_right_rename(space, oname, oentry, nname, nentry); + /* space is unlocked */ + return kr; +} + +#if MACH_KDB +#define printf kdbprintf + +/* + * Routine: ipc_object_print + * Purpose: + * Pretty-print an object for kdb. + */ + +char *ikot_print_array[IKOT_MAX_TYPE] = { + "(NONE) ", + "(THREAD) ", + "(TASK) ", + "(HOST) ", + "(HOST_PRIV) ", + "(PROCESSOR) ", + "(PSET) ", + "(PSET_NAME) ", + "(PAGER) ", + "(PAGER_REQUEST) ", + "(DEVICE) ", /* 10 */ + "(XMM_OBJECT) ", + "(XMM_PAGER) ", + "(XMM_KERNEL) ", + "(XMM_REPLY) ", + "(PAGER_TERMINATING)", + "(PAGING_NAME) ", + "(HOST_SECURITY) ", + "(LEDGER) ", + "(MASTER_DEVICE) ", + "(ACTIVATION) ", /* 20 */ + "(SUBSYSTEM) ", + "(IO_DONE_QUEUE) ", + "(SEMAPHORE) ", + "(LOCK_SET) ", + "(CLOCK) ", + "(CLOCK_CTRL) ", + "(PAGER_PROXY) ", /* 27 */ + /* << new entries here */ + "(UNKNOWN) " /* magic catchall */ +}; /* Please keep in sync with kern/ipc_kobject.h */ + +void +ipc_object_print( + const ipc_object_t object) +{ + int kotype; + + iprintf("%s", io_active(object) ? "active" : "dead"); + printf(", refs=%d", object->io_references); + printf(", otype=%d", io_otype(object)); + kotype = io_kotype(object); + if (kotype >= 0 && kotype < IKOT_MAX_TYPE) + printf(", kotype=%d %s\n", io_kotype(object), + ikot_print_array[kotype]); + else + printf(", kotype=0x%x %s\n", io_kotype(object), + ikot_print_array[IKOT_UNKNOWN]); +} + +#endif /* MACH_KDB */ diff --git a/ipc/ipc_object.h b/ipc/ipc_object.h new file mode 100644 index 0000000..209fae1 --- /dev/null +++ b/ipc/ipc_object.h @@ -0,0 +1,169 @@ +/* + * 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. + */ +/* + * File: ipc/ipc_object.h + * Author: Rich Draves + * Date: 1989 + * + * Definitions for IPC objects, for which tasks have capabilities. + */ + +#ifndef _IPC_IPC_OBJECT_H_ +#define _IPC_IPC_OBJECT_H_ + +#include +#include +#include +#include +#include +#include + +typedef unsigned int ipc_object_refs_t; +typedef unsigned int ipc_object_bits_t; +typedef unsigned int ipc_object_type_t; + +typedef struct ipc_object { + decl_simple_lock_data(,io_lock_data) + ipc_object_refs_t io_references; + ipc_object_bits_t io_bits; +} *ipc_object_t; + +#define IO_NULL ((ipc_object_t) 0) +#define IO_DEAD ((ipc_object_t) -1) + +#define IO_VALID(io) (((io) != IO_NULL) && ((io) != IO_DEAD)) + +#define IO_BITS_KOTYPE 0x0000ffff /* used by the object */ +#define IO_BITS_OTYPE 0x3fff0000 /* determines a cache */ +/* The following masks are used to store attributes of ipc ports. */ +#define IO_BITS_PROTECTED_PAYLOAD 0x40000000 /* pp set? */ +#define IO_BITS_ACTIVE 0x80000000U /* is object alive? */ + +#define io_active(io) ((int)(io)->io_bits < 0) /* hack */ + +#define io_otype(io) (((io)->io_bits & IO_BITS_OTYPE) >> 16) +#define io_kotype(io) ((io)->io_bits & IO_BITS_KOTYPE) + +#define io_makebits(active, otype, kotype) \ + (((active) ? IO_BITS_ACTIVE : 0) | ((otype) << 16) | (kotype)) + +/* + * Object types: ports, port sets, kernel-loaded ports + */ +#define IOT_PORT 0 +#define IOT_PORT_SET 1 +#define IOT_NUMBER 2 /* number of types used */ + +extern struct kmem_cache ipc_object_caches[IOT_NUMBER]; + +#define io_alloc(otype) \ + ((ipc_object_t) kmem_cache_alloc(&ipc_object_caches[(otype)])) + +#define io_free(otype, io) \ + kmem_cache_free(&ipc_object_caches[(otype)], (vm_offset_t) (io)) + +#define io_lock_init(io) simple_lock_init(&(io)->io_lock_data) +#define io_lock(io) simple_lock(&(io)->io_lock_data) +#define io_lock_try(io) simple_lock_try(&(io)->io_lock_data) +#define io_unlock(io) simple_unlock(&(io)->io_lock_data) + +#define io_check_unlock(io) \ +MACRO_BEGIN \ + ipc_object_refs_t _refs = (io)->io_references; \ + \ + io_unlock(io); \ + if (_refs == 0) \ + io_free(io_otype(io), io); \ +MACRO_END + +#define io_reference(io) \ +MACRO_BEGIN \ + (io)->io_references++; \ +MACRO_END + +#define io_release(io) \ +MACRO_BEGIN \ + (io)->io_references--; \ +MACRO_END + +extern void +ipc_object_reference(ipc_object_t); + +extern void +ipc_object_release(ipc_object_t); + +extern kern_return_t +ipc_object_translate(ipc_space_t, mach_port_name_t, + mach_port_right_t, ipc_object_t *); + +extern kern_return_t +ipc_object_alloc_dead(ipc_space_t, mach_port_name_t *); + +extern kern_return_t +ipc_object_alloc_dead_name(ipc_space_t, mach_port_name_t); + +extern kern_return_t +ipc_object_alloc(ipc_space_t, ipc_object_type_t, + mach_port_type_t, mach_port_urefs_t, + mach_port_name_t *, ipc_object_t *); + +extern kern_return_t +ipc_object_alloc_name(ipc_space_t, ipc_object_type_t, + mach_port_type_t, mach_port_urefs_t, + mach_port_name_t, ipc_object_t *); + +extern mach_msg_type_name_t +ipc_object_copyin_type(mach_msg_type_name_t); + +extern kern_return_t +ipc_object_copyin(ipc_space_t, mach_port_name_t, + mach_msg_type_name_t, ipc_object_t *); + +extern void +ipc_object_copyin_from_kernel(ipc_object_t, mach_msg_type_name_t); + +extern void +ipc_object_destroy(ipc_object_t, mach_msg_type_name_t); + +extern kern_return_t +ipc_object_copyout(ipc_space_t, ipc_object_t, + mach_msg_type_name_t, boolean_t, mach_port_name_t *); + +extern kern_return_t +ipc_object_copyout_name(ipc_space_t, ipc_object_t, + mach_msg_type_name_t, boolean_t, mach_port_name_t); + +extern void +ipc_object_copyout_dest(ipc_space_t, ipc_object_t, + mach_msg_type_name_t, mach_port_name_t *); + +extern kern_return_t +ipc_object_rename(ipc_space_t, mach_port_name_t, mach_port_name_t); + +extern void +ipc_object_print(ipc_object_t); + +#endif /* _IPC_IPC_OBJECT_H_ */ diff --git a/ipc/ipc_port.c b/ipc/ipc_port.c new file mode 100644 index 0000000..e959f67 --- /dev/null +++ b/ipc/ipc_port.c @@ -0,0 +1,1290 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University. + * Copyright (c) 1993,1994 The University of Utah and + * the Computer Systems Laboratory (CSL). + * 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, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF + * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM 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. + */ +/* + * File: ipc/ipc_port.c + * Author: Rich Draves + * Date: 1989 + * + * Functions to manipulate IPC ports. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if MACH_KDB +#include +#include +#endif /* MACH_KDB */ + + +def_simple_lock_data(, ipc_port_multiple_lock_data) + +def_simple_lock_data(, ipc_port_timestamp_lock_data) +ipc_port_timestamp_t ipc_port_timestamp_data; + +/* + * Routine: ipc_port_timestamp + * Purpose: + * Retrieve a timestamp value. + */ + +ipc_port_timestamp_t +ipc_port_timestamp(void) +{ + ipc_port_timestamp_t timestamp; + + ipc_port_timestamp_lock(); + timestamp = ipc_port_timestamp_data++; + ipc_port_timestamp_unlock(); + + return timestamp; +} + +/* + * Routine: ipc_port_dnrequest + * Purpose: + * Try to allocate a dead-name request slot. + * If successful, returns the request index. + * Otherwise returns zero. + * Conditions: + * The port is locked and active. + * Returns: + * KERN_SUCCESS A request index was found. + * KERN_NO_SPACE No index allocated. + */ + +kern_return_t +ipc_port_dnrequest( + ipc_port_t port, + mach_port_name_t name, + ipc_port_t soright, + ipc_port_request_index_t *indexp) +{ + ipc_port_request_t ipr, table; + ipc_port_request_index_t index; + + assert(ip_active(port)); + assert(name != MACH_PORT_NULL); + assert(soright != IP_NULL); + + table = port->ip_dnrequests; + if (table == IPR_NULL) + return KERN_NO_SPACE; + + index = table->ipr_next; + if (index == 0) + return KERN_NO_SPACE; + + ipr = &table[index]; + assert(ipr->ipr_name == MACH_PORT_NULL); + + table->ipr_next = ipr->ipr_next; + ipr->ipr_name = name; + ipr->ipr_soright = soright; + + *indexp = index; + return KERN_SUCCESS; +} + +/* + * Routine: ipc_port_dngrow + * Purpose: + * Grow a port's table of dead-name requests. + * Conditions: + * The port must be locked and active. + * Nothing else locked; will allocate memory. + * Upon return the port is unlocked. + * Returns: + * KERN_SUCCESS Grew the table. + * KERN_SUCCESS Somebody else grew the table. + * KERN_SUCCESS The port died. + * KERN_RESOURCE_SHORTAGE Couldn't allocate new table. + */ + +kern_return_t +ipc_port_dngrow(ipc_port_t port) +{ + ipc_table_size_t its; + ipc_port_request_t otable, ntable; + + assert(ip_active(port)); + + otable = port->ip_dnrequests; + if (otable == IPR_NULL) + its = &ipc_table_dnrequests[0]; + else + its = otable->ipr_size + 1; + + ip_reference(port); + ip_unlock(port); + + if ((its->its_size == 0) || + ((ntable = it_dnrequests_alloc(its)) == IPR_NULL)) { + ipc_port_release(port); + return KERN_RESOURCE_SHORTAGE; + } + + ip_lock(port); + ip_release(port); + + /* + * Check that port is still active and that nobody else + * has slipped in and grown the table on us. Note that + * just checking port->ip_dnrequests == otable isn't + * sufficient; must check ipr_size. + */ + + if (ip_active(port) && + (port->ip_dnrequests == otable) && + ((otable == IPR_NULL) || (otable->ipr_size+1 == its))) { + ipc_table_size_t oits = 0; /* '=0' to shut up lint */ + ipc_table_elems_t osize, nsize; + ipc_port_request_index_t free, i; + + /* copy old table to new table */ + + if (otable != IPR_NULL) { + oits = otable->ipr_size; + osize = oits->its_size; + free = otable->ipr_next; + + memcpy((ntable + 1), (otable + 1), + (osize - 1) * sizeof(struct ipc_port_request)); + } else { + osize = 1; + free = 0; + } + + nsize = its->its_size; + assert(nsize > osize); + + /* add new elements to the new table's free list */ + + for (i = osize; i < nsize; i++) { + ipc_port_request_t ipr = &ntable[i]; + + ipr->ipr_name = MACH_PORT_NULL; + ipr->ipr_next = free; + free = i; + } + + ntable->ipr_next = free; + ntable->ipr_size = its; + port->ip_dnrequests = ntable; + ip_unlock(port); + + if (otable != IPR_NULL) + it_dnrequests_free(oits, otable); + } else { + ip_check_unlock(port); + it_dnrequests_free(its, ntable); + } + + return KERN_SUCCESS; +} + +/* + * Routine: ipc_port_dncancel + * Purpose: + * Cancel a dead-name request and return the send-once right. + * Conditions: + * The port must locked and active. + */ + +ipc_port_t +ipc_port_dncancel( + ipc_port_t port, + mach_port_name_t name, + ipc_port_request_index_t index) +{ + ipc_port_request_t ipr, table; + ipc_port_t dnrequest; + + assert(ip_active(port)); + assert(name != MACH_PORT_NULL); + assert(index != 0); + + table = port->ip_dnrequests; + assert(table != IPR_NULL); + + ipr = &table[index]; + dnrequest = ipr->ipr_soright; + assert(ipr->ipr_name == name); + + /* return ipr to the free list inside the table */ + + ipr->ipr_name = MACH_PORT_NULL; + ipr->ipr_next = table->ipr_next; + table->ipr_next = index; + + return dnrequest; +} + +/* + * Routine: ipc_port_pdrequest + * Purpose: + * Make a port-deleted request, returning the + * previously registered send-once right. + * Just cancels the previous request if notify is IP_NULL. + * Conditions: + * The port is locked and active. It is unlocked. + * Consumes a ref for notify (if non-null), and + * returns previous with a ref (if non-null). + */ + +void +ipc_port_pdrequest( + ipc_port_t port, + const ipc_port_t notify, + ipc_port_t *previousp) +{ + ipc_port_t previous; + + assert(ip_active(port)); + + previous = port->ip_pdrequest; + port->ip_pdrequest = notify; + ip_unlock(port); + + *previousp = previous; +} + +/* + * Routine: ipc_port_nsrequest + * Purpose: + * Make a no-senders request, returning the + * previously registered send-once right. + * Just cancels the previous request if notify is IP_NULL. + * Conditions: + * The port is locked and active. It is unlocked. + * Consumes a ref for notify (if non-null), and + * returns previous with a ref (if non-null). + */ + +void +ipc_port_nsrequest( + ipc_port_t port, + mach_port_mscount_t sync, + ipc_port_t notify, + ipc_port_t *previousp) +{ + ipc_port_t previous; + mach_port_mscount_t mscount; + + assert(ip_active(port)); + + previous = port->ip_nsrequest; + mscount = port->ip_mscount; + + if ((port->ip_srights == 0) && + (sync <= mscount) && + (notify != IP_NULL)) { + port->ip_nsrequest = IP_NULL; + ip_unlock(port); + ipc_notify_no_senders(notify, mscount); + } else { + port->ip_nsrequest = notify; + ip_unlock(port); + } + + *previousp = previous; +} + +/* + * Routine: ipc_port_set_qlimit + * Purpose: + * Changes a port's queue limit; the maximum number + * of messages which may be queued to the port. + * Conditions: + * The port is locked and active. + */ + +void +ipc_port_set_qlimit( + ipc_port_t port, + mach_port_msgcount_t qlimit) +{ + assert(ip_active(port)); + + /* wake up senders allowed by the new qlimit */ + + if (qlimit > port->ip_qlimit) { + mach_port_msgcount_t i, wakeup; + + /* caution: wakeup, qlimit are unsigned */ + + wakeup = qlimit - port->ip_qlimit; + + for (i = 0; i < wakeup; i++) { + ipc_thread_t th; + + th = ipc_thread_dequeue(&port->ip_blocked); + if (th == ITH_NULL) + break; + + th->ith_state = MACH_MSG_SUCCESS; + thread_go(th); + } + } + + port->ip_qlimit = qlimit; +} + +/* + * Routine: ipc_port_lock_mqueue + * Purpose: + * Locks and returns the message queue that the port is using. + * The message queue may be in the port or in its port set. + * Conditions: + * The port is locked and active. + * Port set, message queue locks may be taken. + */ + +ipc_mqueue_t +ipc_port_lock_mqueue(ipc_port_t port) +{ + if (port->ip_pset != IPS_NULL) { + ipc_pset_t pset = port->ip_pset; + + ips_lock(pset); + if (ips_active(pset)) { + imq_lock(&pset->ips_messages); + ips_unlock(pset); + return &pset->ips_messages; + } + + ipc_pset_remove(pset, port); + ips_check_unlock(pset); + } + + imq_lock(&port->ip_messages); + return &port->ip_messages; +} + +/* + * Routine: ipc_port_set_seqno + * Purpose: + * Changes a port's sequence number. + * Conditions: + * The port is locked and active. + * Port set, message queue locks may be taken. + */ + +void +ipc_port_set_seqno( + ipc_port_t port, + mach_port_seqno_t seqno) +{ + ipc_mqueue_t mqueue; + + mqueue = ipc_port_lock_mqueue(port); + port->ip_seqno = seqno; + imq_unlock(mqueue); +} + +/* + * Routine: ipc_port_set_protected_payload + * Purpose: + * Changes a port's protected payload. + * Conditions: + * The port is locked and active. + */ + +void +ipc_port_set_protected_payload(ipc_port_t port, rpc_uintptr_t payload) +{ + ipc_mqueue_t mqueue; + + mqueue = ipc_port_lock_mqueue(port); + port->ip_protected_payload = payload; + ipc_port_flag_protected_payload_set(port); + imq_unlock(mqueue); +} + +/* + * Routine: ipc_port_clear_protected_payload + * Purpose: + * Clear a port's protected payload. + * Conditions: + * The port is locked and active. + */ + +void +ipc_port_clear_protected_payload(ipc_port_t port) +{ + ipc_mqueue_t mqueue; + + mqueue = ipc_port_lock_mqueue(port); + ipc_port_flag_protected_payload_clear(port); + imq_unlock(mqueue); +} + + +/* + * Routine: ipc_port_clear_receiver + * Purpose: + * Prepares a receive right for transmission/destruction. + * Conditions: + * The port is locked and active. + */ + +void +ipc_port_clear_receiver( + ipc_port_t port) +{ + ipc_pset_t pset; + + assert(ip_active(port)); + + pset = port->ip_pset; + if (pset != IPS_NULL) { + /* No threads receiving from port, but must remove from set. */ + + ips_lock(pset); + ipc_pset_remove(pset, port); + ips_check_unlock(pset); + } else { + /* Else, wake up all receivers, indicating why. */ + + imq_lock(&port->ip_messages); + ipc_mqueue_changed(&port->ip_messages, MACH_RCV_PORT_DIED); + imq_unlock(&port->ip_messages); + } + + ipc_port_set_mscount(port, 0); + imq_lock(&port->ip_messages); + port->ip_seqno = 0; + imq_unlock(&port->ip_messages); +} + +/* + * Routine: ipc_port_init + * Purpose: + * Initializes a newly-allocated port. + * Doesn't touch the ip_object fields. + */ + +void +ipc_port_init( + ipc_port_t port, + ipc_space_t space, + mach_port_name_t name) +{ + /* port->ip_kobject doesn't have to be initialized */ + + ipc_target_init(&port->ip_target, name); + + port->ip_receiver = space; + + port->ip_mscount = 0; + port->ip_srights = 0; + port->ip_sorights = 0; + + port->ip_nsrequest = IP_NULL; + port->ip_pdrequest = IP_NULL; + port->ip_dnrequests = IPR_NULL; + + port->ip_pset = IPS_NULL; + port->ip_cur_target = &port->ip_target; + port->ip_seqno = 0; + port->ip_msgcount = 0; + port->ip_qlimit = MACH_PORT_QLIMIT_DEFAULT; + ipc_port_flag_protected_payload_clear(port); + port->ip_protected_payload = 0; + + ipc_mqueue_init(&port->ip_messages); + ipc_thread_queue_init(&port->ip_blocked); +} + +/* + * Routine: ipc_port_alloc + * Purpose: + * Allocate a port. + * Conditions: + * Nothing locked. If successful, the port is returned + * locked. (The caller doesn't have a reference.) + * Returns: + * KERN_SUCCESS The port is allocated. + * KERN_INVALID_TASK The space is dead. + * KERN_NO_SPACE No room for an entry in the space. + * KERN_RESOURCE_SHORTAGE Couldn't allocate memory. + */ + +kern_return_t +ipc_port_alloc( + ipc_space_t space, + mach_port_name_t *namep, + ipc_port_t *portp) +{ + ipc_port_t port; + mach_port_name_t name; + kern_return_t kr; + + kr = ipc_object_alloc(space, IOT_PORT, + MACH_PORT_TYPE_RECEIVE, 0, + &name, (ipc_object_t *) &port); + if (kr != KERN_SUCCESS) + return kr; + + /* port is locked */ + + ipc_port_init(port, space, name); + + *namep = name; + *portp = port; + + return KERN_SUCCESS; +} + +/* + * Routine: ipc_port_alloc_name + * Purpose: + * Allocate a port, with a specific name. + * Conditions: + * Nothing locked. If successful, the port is returned + * locked. (The caller doesn't have a reference.) + * Returns: + * KERN_SUCCESS The port is allocated. + * KERN_INVALID_TASK The space is dead. + * KERN_NAME_EXISTS The name already denotes a right. + * KERN_RESOURCE_SHORTAGE Couldn't allocate memory. + */ + +kern_return_t +ipc_port_alloc_name( + ipc_space_t space, + mach_port_name_t name, + ipc_port_t *portp) +{ + ipc_port_t port; + kern_return_t kr; + + kr = ipc_object_alloc_name(space, IOT_PORT, + MACH_PORT_TYPE_RECEIVE, 0, + name, (ipc_object_t *) &port); + if (kr != KERN_SUCCESS) + return kr; + /* port is locked */ + + ipc_port_init(port, space, name); + + *portp = port; + return KERN_SUCCESS; +} + +/* + * Routine: ipc_port_destroy + * Purpose: + * Destroys a port. Cleans up queued messages. + * + * If the port has a backup, it doesn't get destroyed, + * but is sent in a port-destroyed notification to the backup. + * Conditions: + * The port is locked and alive; nothing else locked. + * The caller has a reference, which is consumed. + * Afterwards, the port is unlocked and dead. + */ + +void +ipc_port_destroy( + ipc_port_t port) +{ + ipc_port_t pdrequest, nsrequest; + ipc_mqueue_t mqueue; + ipc_kmsg_queue_t kmqueue; + ipc_kmsg_t kmsg; + ipc_thread_t sender; + ipc_port_request_t dnrequests; + + assert(ip_active(port)); + /* port->ip_receiver_name is garbage */ + /* port->ip_receiver/port->ip_destination is garbage */ + assert(port->ip_pset == IPS_NULL); + assert(port->ip_mscount == 0); + assert(port->ip_seqno == 0); + + /* first check for a backup port */ + + pdrequest = port->ip_pdrequest; + if (pdrequest != IP_NULL) { + /* we assume the ref for pdrequest */ + port->ip_pdrequest = IP_NULL; + + /* make port be in limbo */ + port->ip_receiver_name = MACH_PORT_NULL; + port->ip_destination = IP_NULL; + ipc_port_flag_protected_payload_clear(port); + ip_unlock(port); + + if (!ipc_port_check_circularity(port, pdrequest)) { + /* consumes our refs for port and pdrequest */ + ipc_notify_port_destroyed(pdrequest, port); + return; + } else { + /* consume pdrequest and destroy port */ + ipc_port_release_sonce(pdrequest); + } + + ip_lock(port); + assert(ip_active(port)); + assert(port->ip_pset == IPS_NULL); + assert(port->ip_mscount == 0); + assert(port->ip_seqno == 0); + assert(port->ip_pdrequest == IP_NULL); + assert(port->ip_receiver_name == MACH_PORT_NULL); + assert(port->ip_destination == IP_NULL); + + /* fall through and destroy the port */ + } + + /* + * rouse all blocked senders + * + * This must be done with the port locked, because + * ipc_mqueue_send can play with the ip_blocked queue + * of a dead port. + */ + + while ((sender = ipc_thread_dequeue(&port->ip_blocked)) != ITH_NULL) { + sender->ith_state = MACH_MSG_SUCCESS; + thread_go(sender); + } + + /* once port is dead, we don't need to keep it locked */ + + port->ip_object.io_bits &= ~IO_BITS_ACTIVE; + port->ip_timestamp = ipc_port_timestamp(); + ip_unlock(port); + + /* throw away no-senders request */ + + nsrequest = port->ip_nsrequest; + if (nsrequest != IP_NULL) + ipc_notify_send_once(nsrequest); /* consumes ref */ + + /* destroy any queued messages */ + + mqueue = &port->ip_messages; + imq_lock(mqueue); + assert(ipc_thread_queue_empty(&mqueue->imq_threads)); + kmqueue = &mqueue->imq_messages; + + while ((kmsg = ipc_kmsg_dequeue(kmqueue)) != IKM_NULL) { + imq_unlock(mqueue); + + assert(kmsg->ikm_header.msgh_remote_port == + (mach_port_t) port); + + ipc_port_release(port); + kmsg->ikm_header.msgh_remote_port = MACH_PORT_NULL; + ipc_kmsg_destroy(kmsg); + + imq_lock(mqueue); + } + + imq_unlock(mqueue); + + /* generate dead-name notifications */ + + dnrequests = port->ip_dnrequests; + if (dnrequests != IPR_NULL) { + ipc_table_size_t its = dnrequests->ipr_size; + ipc_table_elems_t size = its->its_size; + ipc_port_request_index_t index; + + for (index = 1; index < size; index++) { + ipc_port_request_t ipr = &dnrequests[index]; + mach_port_name_t name = ipr->ipr_name; + ipc_port_t soright; + + if (name == MACH_PORT_NULL) + continue; + + soright = ipr->ipr_soright; + assert(soright != IP_NULL); + + ipc_notify_dead_name(soright, name); + } + + it_dnrequests_free(its, dnrequests); + } + + if (ip_kotype(port) != IKOT_NONE) + ipc_kobject_destroy(port); + + /* Common destruction for the IPC target. */ + ipc_target_terminate(&port->ip_target); + + ipc_port_release(port); /* consume caller's ref */ +} + +/* + * Routine: ipc_port_check_circularity + * Purpose: + * Check if queueing "port" in a message for "dest" + * would create a circular group of ports and messages. + * + * If no circularity (FALSE returned), then "port" + * is changed from "in limbo" to "in transit". + * + * That is, we want to set port->ip_destination == dest, + * but guaranteeing that this doesn't create a circle + * port->ip_destination->ip_destination->... == port + * Conditions: + * No ports locked. References held for "port" and "dest". + */ + +boolean_t +ipc_port_check_circularity( + ipc_port_t port, + ipc_port_t dest) +{ + ipc_port_t base; + + assert(port != IP_NULL); + assert(dest != IP_NULL); + + if (port == dest) + return TRUE; + base = dest; + + /* + * First try a quick check that can run in parallel. + * No circularity if dest is not in transit. + */ + + ip_lock(port); + if (ip_lock_try(dest)) { + if (!ip_active(dest) || + (dest->ip_receiver_name != MACH_PORT_NULL) || + (dest->ip_destination == IP_NULL)) + goto not_circular; + + /* dest is in transit; further checking necessary */ + + ip_unlock(dest); + } + ip_unlock(port); + + ipc_port_multiple_lock(); /* massive serialization */ + + /* + * Search for the end of the chain (a port not in transit), + * acquiring locks along the way. + */ + + for (;;) { + ip_lock(base); + + if (!ip_active(base) || + (base->ip_receiver_name != MACH_PORT_NULL) || + (base->ip_destination == IP_NULL)) + break; + + base = base->ip_destination; + } + + /* all ports in chain from dest to base, inclusive, are locked */ + + if (port == base) { + /* circularity detected! */ + + ipc_port_multiple_unlock(); + + /* port (== base) is in limbo */ + + assert(ip_active(port)); + assert(port->ip_receiver_name == MACH_PORT_NULL); + assert(port->ip_destination == IP_NULL); + + while (dest != IP_NULL) { + ipc_port_t next; + + /* dest is in transit or in limbo */ + + assert(ip_active(dest)); + assert(dest->ip_receiver_name == MACH_PORT_NULL); + + next = dest->ip_destination; + ip_unlock(dest); + dest = next; + } + + return TRUE; + } + + /* + * The guarantee: lock port while the entire chain is locked. + * Once port is locked, we can take a reference to dest, + * add port to the chain, and unlock everything. + */ + + ip_lock(port); + ipc_port_multiple_unlock(); + + not_circular: + + /* port is in limbo */ + + assert(ip_active(port)); + assert(port->ip_receiver_name == MACH_PORT_NULL); + assert(port->ip_destination == IP_NULL); + + ip_reference(dest); + port->ip_destination = dest; + + /* now unlock chain */ + + while (port != base) { + ipc_port_t next; + + /* port is in transit */ + + assert(ip_active(port)); + assert(port->ip_receiver_name == MACH_PORT_NULL); + assert(port->ip_destination != IP_NULL); + + next = port->ip_destination; + ip_unlock(port); + port = next; + } + + /* base is not in transit */ + + assert(!ip_active(base) || + (base->ip_receiver_name != MACH_PORT_NULL) || + (base->ip_destination == IP_NULL)); + ip_unlock(base); + + return FALSE; +} + +/* + * Routine: ipc_port_lookup_notify + * Purpose: + * Make a send-once notify port from a receive right. + * Returns IP_NULL if name doesn't denote a receive right. + * Conditions: + * The space must be locked (read or write) and active. + */ + +ipc_port_t +ipc_port_lookup_notify( + ipc_space_t space, + mach_port_name_t name) +{ + ipc_port_t port; + ipc_entry_t entry; + + assert(space->is_active); + + entry = ipc_entry_lookup(space, name); + if (entry == IE_NULL) + return IP_NULL; + + if ((entry->ie_bits & MACH_PORT_TYPE_RECEIVE) == 0) + return IP_NULL; + + port = (ipc_port_t) entry->ie_object; + assert(port != IP_NULL); + + ip_lock(port); + assert(ip_active(port)); + assert(port->ip_receiver_name == name); + assert(port->ip_receiver == space); + + ip_reference(port); + port->ip_sorights++; + ip_unlock(port); + + return port; +} + +/* + * Routine: ipc_port_make_send + * Purpose: + * Make a naked send right from a receive right. + * Conditions: + * The port is not locked but it is active. + */ + +ipc_port_t +ipc_port_make_send( + ipc_port_t port) +{ + assert(IP_VALID(port)); + + ip_lock(port); + assert(ip_active(port)); + port->ip_mscount++; + port->ip_srights++; + ip_reference(port); + ip_unlock(port); + + return port; +} + +/* + * Routine: ipc_port_copy_send + * Purpose: + * Make a naked send right from another naked send right. + * IP_NULL -> IP_NULL + * IP_DEAD -> IP_DEAD + * dead port -> IP_DEAD + * live port -> port + ref + * Conditions: + * Nothing locked except possibly a space. + */ + +ipc_port_t +ipc_port_copy_send( + ipc_port_t port) +{ + ipc_port_t sright; + + if (!IP_VALID(port)) + return port; + + ip_lock(port); + if (ip_active(port)) { + assert(port->ip_srights > 0); + + ip_reference(port); + port->ip_srights++; + sright = port; + } else + sright = IP_DEAD; + ip_unlock(port); + + return sright; +} + +/* + * Routine: ipc_port_copyout_send + * Purpose: + * Copyout a naked send right (possibly null/dead), + * or if that fails, destroy the right. + * Conditions: + * Nothing locked. + */ + +mach_port_name_t +ipc_port_copyout_send( + ipc_port_t sright, + ipc_space_t space) +{ + mach_port_name_t name; + + if (IP_VALID(sright)) { + kern_return_t kr; + + kr = ipc_object_copyout(space, (ipc_object_t) sright, + MACH_MSG_TYPE_PORT_SEND, TRUE, &name); + if (kr != KERN_SUCCESS) { + ipc_port_release_send(sright); + + if (kr == KERN_INVALID_CAPABILITY) + name = MACH_PORT_NAME_DEAD; + else + name = MACH_PORT_NAME_NULL; + } + } else + name = invalid_port_to_name((mach_port_t)sright); + + return name; +} + +/* + * Routine: ipc_port_release_send + * Purpose: + * Release a (valid) naked send right. + * Consumes a ref for the port. + * Conditions: + * Nothing locked. + */ + +void +ipc_port_release_send( + ipc_port_t port) +{ + ipc_port_t nsrequest = IP_NULL; + mach_port_mscount_t mscount; + + assert(IP_VALID(port)); + + ip_lock(port); + ip_release(port); + + if (!ip_active(port)) { + ip_check_unlock(port); + return; + } + + assert(port->ip_srights > 0); + + if (--port->ip_srights == 0) { + nsrequest = port->ip_nsrequest; + if (nsrequest != IP_NULL) { + port->ip_nsrequest = IP_NULL; + mscount = port->ip_mscount; + } + } + + ip_unlock(port); + + if (nsrequest != IP_NULL) + ipc_notify_no_senders(nsrequest, mscount); +} + +/* + * Routine: ipc_port_make_sonce + * Purpose: + * Make a naked send-once right from a receive right. + * Conditions: + * The port is not locked but it is active. + */ + +ipc_port_t +ipc_port_make_sonce( + ipc_port_t port) +{ + assert(IP_VALID(port)); + + ip_lock(port); + assert(ip_active(port)); + port->ip_sorights++; + ip_reference(port); + ip_unlock(port); + + return port; +} + +/* + * Routine: ipc_port_release_sonce + * Purpose: + * Release a naked send-once right. + * Consumes a ref for the port. + * + * In normal situations, this is never used. + * Send-once rights are only consumed when + * a message (possibly a send-once notification) + * is sent to them. + * Conditions: + * Nothing locked except possibly a space. + */ + +void +ipc_port_release_sonce( + ipc_port_t port) +{ + assert(IP_VALID(port)); + + ip_lock(port); + + assert(port->ip_sorights > 0); + + port->ip_sorights--; + + ip_release(port); + + if (!ip_active(port)) { + ip_check_unlock(port); + return; + } + + ip_unlock(port); +} + +/* + * Routine: ipc_port_release_receive + * Purpose: + * Release a naked (in limbo or in transit) receive right. + * Consumes a ref for the port; destroys the port. + * Conditions: + * Nothing locked. + */ + +void +ipc_port_release_receive( + ipc_port_t port) +{ + ipc_port_t dest; + + assert(IP_VALID(port)); + + ip_lock(port); + assert(ip_active(port)); + assert(port->ip_receiver_name == MACH_PORT_NULL); + dest = port->ip_destination; + + ipc_port_destroy(port); /* consumes ref, unlocks */ + + if (dest != IP_NULL) + ipc_port_release(dest); +} + +/* + * Routine: ipc_port_alloc_special + * Purpose: + * Allocate a port in a special space. + * The new port is returned with one ref. + * If unsuccessful, IP_NULL is returned. + * Conditions: + * Nothing locked. + */ + +ipc_port_t +ipc_port_alloc_special(ipc_space_t space) +{ + ipc_port_t port; + + port = ip_alloc(); + if (port == IP_NULL) + return IP_NULL; + + ip_lock_init(port); + port->ip_references = 1; + port->ip_object.io_bits = io_makebits(TRUE, IOT_PORT, 0); + + /* + * The actual values of ip_receiver_name aren't important, + * as long as they are valid (not null/dead). + * + * Mach4: we set it to the internal port structure address + * so we can always just pass on ip_receiver_name during + * an rpc regardless of whether the destination is user or + * kernel (i.e. no special-casing code for the kernel along + * the fast rpc path). + */ + + ipc_port_init(port, space, (mach_port_name_t)port); + + return port; +} + +/* + * Routine: ipc_port_dealloc_special + * Purpose: + * Deallocate a port in a special space. + * Consumes one ref for the port. + * Conditions: + * Nothing locked. + */ + +void +ipc_port_dealloc_special( + ipc_port_t port, + ipc_space_t space) +{ + ip_lock(port); + assert(ip_active(port)); + assert(port->ip_receiver_name != MACH_PORT_NULL); + assert(port->ip_receiver == space); + + /* + * We clear ip_receiver_name and ip_receiver to simplify + * the ipc_space_kernel check in ipc_mqueue_send. + */ + + port->ip_receiver_name = MACH_PORT_NULL; + port->ip_receiver = IS_NULL; + + /* + * For ipc_space_kernel, all ipc_port_clear_receiver does + * is clean things up for the assertions in ipc_port_destroy. + * For ipc_space_reply, there might be a waiting receiver. + */ + + ipc_port_clear_receiver(port); + ipc_port_destroy(port); +} + +#if MACH_KDB +#define printf kdbprintf + +/* + * Routine: ipc_port_print + * Purpose: + * Pretty-print a port for kdb. + */ + +void +ipc_port_print(const ipc_port_t port) +{ + printf("port 0x%x\n", port); + + indent += 2; + + iprintf("flags "); + printf("has_protected_payload=%d", + ipc_port_flag_protected_payload(port)); + printf("\n"); + + ipc_object_print(&port->ip_object); + iprintf("receiver=0x%x", port->ip_receiver); + printf(", receiver_name=0x%x\n", port->ip_receiver_name); + + iprintf("mscount=%d", port->ip_mscount); + printf(", srights=%d", port->ip_srights); + printf(", sorights=%d\n", port->ip_sorights); + + iprintf("nsrequest=0x%x", port->ip_nsrequest); + printf(", pdrequest=0x%x", port->ip_pdrequest); + printf(", dnrequests=0x%x\n", port->ip_dnrequests); + + iprintf("pset=0x%x", port->ip_pset); + printf(", seqno=%d", port->ip_seqno); + printf(", msgcount=%d", port->ip_msgcount); + printf(", qlimit=%d\n", port->ip_qlimit); + + iprintf("kmsgs=0x%x", port->ip_messages.imq_messages.ikmq_base); + printf(", rcvrs=0x%x", port->ip_messages.imq_threads.ithq_base); + printf(", sndrs=0x%x", port->ip_blocked.ithq_base); + printf(", kobj=0x%x\n", port->ip_kobject); + + iprintf("protected_payload=%p\n", (void *) (vm_offset_t) port->ip_protected_payload); + + indent -= 2; +} + +#endif /* MACH_KDB */ diff --git a/ipc/ipc_port.h b/ipc/ipc_port.h new file mode 100644 index 0000000..192d880 --- /dev/null +++ b/ipc/ipc_port.h @@ -0,0 +1,354 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University. + * Copyright (c) 1993,1994 The University of Utah and + * the Computer Systems Laboratory (CSL). + * 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, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF + * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM 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. + */ +/* + */ +/* + * File: ipc/ipc_port.h + * Author: Rich Draves + * Date: 1989 + * + * Definitions for ports. + */ + +#ifndef _IPC_IPC_PORT_H_ +#define _IPC_IPC_PORT_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ipc_target.h" + +/* + * A receive right (port) can be in four states: + * 1) dead (not active, ip_timestamp has death time) + * 2) in a space (ip_receiver_name != 0, ip_receiver points + * to the space but doesn't hold a ref for it) + * 3) in transit (ip_receiver_name == 0, ip_destination points + * to the destination port and holds a ref for it) + * 4) in limbo (ip_receiver_name == 0, ip_destination == IP_NULL) + * + * If the port is active, and ip_receiver points to some space, + * then ip_receiver_name != 0, and that space holds receive rights. + * If the port is not active, then ip_timestamp contains a timestamp + * taken when the port was destroyed. + */ + +typedef unsigned int ipc_port_timestamp_t; + +struct ipc_port { + struct ipc_target ip_target; + + /* This points to the ip_target above if this port isn't on a port set; + otherwise it points to the port set's ips_target. */ + struct ipc_target *ip_cur_target; + + union { + struct ipc_space *receiver; + struct ipc_port *destination; + ipc_port_timestamp_t timestamp; + } data; + + ipc_kobject_t ip_kobject; + + mach_port_mscount_t ip_mscount; + mach_port_rights_t ip_srights; + mach_port_rights_t ip_sorights; + + struct ipc_port *ip_nsrequest; + struct ipc_port *ip_pdrequest; + struct ipc_port_request *ip_dnrequests; + + struct ipc_pset *ip_pset; + mach_port_seqno_t ip_seqno; /* locked by message queue */ + mach_port_msgcount_t ip_msgcount; + mach_port_msgcount_t ip_qlimit; + struct ipc_thread_queue ip_blocked; + rpc_uintptr_t ip_protected_payload; +}; + +#define ip_object ip_target.ipt_object +#define ip_receiver_name ip_target.ipt_name +#define ip_messages ip_target.ipt_messages +#define ip_references ip_object.io_references +#define ip_bits ip_object.io_bits +#define ip_receiver data.receiver +#define ip_destination data.destination +#define ip_timestamp data.timestamp + +#define IP_NULL ((ipc_port_t) IO_NULL) +#define IP_DEAD ((ipc_port_t) IO_DEAD) + +#define IP_VALID(port) IO_VALID(&(port)->ip_object) + +#define ip_active(port) io_active(&(port)->ip_object) +#define ip_lock_init(port) io_lock_init(&(port)->ip_object) +#define ip_lock(port) io_lock(&(port)->ip_object) +#define ip_lock_try(port) io_lock_try(&(port)->ip_object) +#define ip_unlock(port) io_unlock(&(port)->ip_object) +#define ip_check_unlock(port) io_check_unlock(&(port)->ip_object) +#define ip_reference(port) io_reference(&(port)->ip_object) +#define ip_release(port) io_release(&(port)->ip_object) + +#define ip_alloc() ((ipc_port_t) io_alloc(IOT_PORT)) +#define ip_free(port) io_free(IOT_PORT, &(port)->ip_object) + +#define ip_kotype(port) io_kotype(&(port)->ip_object) + +typedef ipc_table_index_t ipc_port_request_index_t; + +typedef struct ipc_port_request { + union { + struct ipc_port *port; + ipc_port_request_index_t index; + } notify; + + union { + mach_port_name_t name; + struct ipc_table_size *size; + } name; +} *ipc_port_request_t; + +#define ipr_next notify.index +#define ipr_size name.size + +#define ipr_soright notify.port +#define ipr_name name.name + +#define IPR_NULL ((ipc_port_request_t) 0) + +/* + * Taking the ipc_port_multiple lock grants the privilege + * to lock multiple ports at once. No ports must locked + * when it is taken. + */ + +decl_simple_lock_data(extern, ipc_port_multiple_lock_data) + +#define ipc_port_multiple_lock_init() \ + simple_lock_init(&ipc_port_multiple_lock_data) + +#define ipc_port_multiple_lock() \ + simple_lock(&ipc_port_multiple_lock_data) + +#define ipc_port_multiple_unlock() \ + simple_unlock(&ipc_port_multiple_lock_data) + +/* + * The port timestamp facility provides timestamps + * for port destruction. It is used to serialize + * mach_port_names with port death. + */ + +decl_simple_lock_data(extern, ipc_port_timestamp_lock_data) +extern ipc_port_timestamp_t ipc_port_timestamp_data; + +#define ipc_port_timestamp_lock_init() \ + simple_lock_init(&ipc_port_timestamp_lock_data) + +#define ipc_port_timestamp_lock() \ + simple_lock(&ipc_port_timestamp_lock_data) + +#define ipc_port_timestamp_unlock() \ + simple_unlock(&ipc_port_timestamp_lock_data) + +extern ipc_port_timestamp_t +ipc_port_timestamp(void); + +/* + * Compares two timestamps, and returns TRUE if one + * happened before two. Note that this formulation + * works when the timestamp wraps around at 2^32, + * as long as one and two aren't too far apart. + */ + +#define IP_TIMESTAMP_ORDER(one, two) ((int) ((one) - (two)) < 0) + +#define ipc_port_translate_receive(space, name, portp) \ + ipc_object_translate((space), (name), \ + MACH_PORT_RIGHT_RECEIVE, \ + (ipc_object_t *) (portp)) + +#define ipc_port_translate_send(space, name, portp) \ + ipc_object_translate((space), (name), \ + MACH_PORT_RIGHT_SEND, \ + (ipc_object_t *) (portp)) + +extern kern_return_t +ipc_port_dnrequest(ipc_port_t, mach_port_name_t, ipc_port_t, + ipc_port_request_index_t *); + +extern kern_return_t +ipc_port_dngrow(ipc_port_t); + +extern ipc_port_t +ipc_port_dncancel(ipc_port_t, mach_port_name_t, ipc_port_request_index_t); + +#define ipc_port_dnrename(port, index, oname, nname) \ +MACRO_BEGIN \ + ipc_port_request_t ipr, table; \ + \ + assert(ip_active(port)); \ + \ + table = port->ip_dnrequests; \ + assert(table != IPR_NULL); \ + \ + ipr = &table[index]; \ + assert(ipr->ipr_name == oname); \ + \ + ipr->ipr_name = nname; \ +MACRO_END + +/* Make a port-deleted request */ +extern void ipc_port_pdrequest( + ipc_port_t port, + ipc_port_t notify, + ipc_port_t *previousp); + +/* Make a no-senders request */ +extern void ipc_port_nsrequest( + ipc_port_t port, + mach_port_mscount_t sync, + ipc_port_t notify, + ipc_port_t *previousp); + +/* Change a port's queue limit */ +extern void ipc_port_set_qlimit( + ipc_port_t port, + mach_port_msgcount_t qlimit); + +#define ipc_port_set_mscount(port, mscount) \ +MACRO_BEGIN \ + assert(ip_active(port)); \ + \ + (port)->ip_mscount = (mscount); \ +MACRO_END + +extern struct ipc_mqueue * +ipc_port_lock_mqueue(ipc_port_t); + +extern void +ipc_port_set_seqno(ipc_port_t, mach_port_seqno_t); + +extern void +ipc_port_set_protected_payload(ipc_port_t, rpc_uintptr_t); + +extern void +ipc_port_clear_protected_payload(ipc_port_t); + +extern void +ipc_port_clear_receiver(ipc_port_t); + +extern void +ipc_port_init(ipc_port_t, ipc_space_t, mach_port_name_t); + +extern kern_return_t +ipc_port_alloc(ipc_space_t, mach_port_name_t *, ipc_port_t *); + +extern kern_return_t +ipc_port_alloc_name(ipc_space_t, mach_port_name_t, ipc_port_t *); + +extern void +ipc_port_destroy(ipc_port_t); + +extern boolean_t +ipc_port_check_circularity(ipc_port_t, ipc_port_t); + +extern ipc_port_t +ipc_port_lookup_notify(ipc_space_t, mach_port_name_t); + +extern ipc_port_t +ipc_port_make_send(ipc_port_t); + +extern ipc_port_t +ipc_port_copy_send(ipc_port_t); + +extern mach_port_name_t +ipc_port_copyout_send(ipc_port_t, ipc_space_t); + +extern void +ipc_port_release_send(ipc_port_t); + +extern ipc_port_t +ipc_port_make_sonce(ipc_port_t); + +extern void +ipc_port_release_sonce(ipc_port_t); + +extern void +ipc_port_release_receive(ipc_port_t); + +extern ipc_port_t +ipc_port_alloc_special(ipc_space_t); + +extern void +ipc_port_dealloc_special(ipc_port_t, ipc_space_t); + +#define ipc_port_alloc_kernel() \ + ipc_port_alloc_special(ipc_space_kernel) +#define ipc_port_dealloc_kernel(port) \ + ipc_port_dealloc_special((port), ipc_space_kernel) + +#define ipc_port_alloc_reply() \ + ipc_port_alloc_special(ipc_space_reply) +#define ipc_port_dealloc_reply(port) \ + ipc_port_dealloc_special((port), ipc_space_reply) + +#define ipc_port_reference(port) \ + ipc_object_reference(&(port)->ip_object) + +#define ipc_port_release(port) \ + ipc_object_release(&(port)->ip_object) + +static inline boolean_t +ipc_port_flag_protected_payload(const struct ipc_port *port) +{ + return !! (port->ip_target.ipt_object.io_bits + & IO_BITS_PROTECTED_PAYLOAD); +} + +static inline void +ipc_port_flag_protected_payload_set(struct ipc_port *port) +{ + port->ip_target.ipt_object.io_bits |= IO_BITS_PROTECTED_PAYLOAD; +} + +static inline void +ipc_port_flag_protected_payload_clear(struct ipc_port *port) +{ + port->ip_target.ipt_object.io_bits &= ~IO_BITS_PROTECTED_PAYLOAD; +} + +#endif /* _IPC_IPC_PORT_H_ */ diff --git a/ipc/ipc_print.h b/ipc/ipc_print.h new file mode 100644 index 0000000..5e8e4f3 --- /dev/null +++ b/ipc/ipc_print.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2013 Free Software Foundation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _IPC_PRINT_H_ +#define _IPC_PRINT_H_ + +#if MACH_KDB + +#include +#include +#include +#include + +extern void ipc_port_print(const ipc_port_t); + +extern void ipc_pset_print(const ipc_pset_t); + +extern void ipc_kmsg_print(const ipc_kmsg_t); + +extern void ipc_msg_print(mach_msg_header_t*); + +#endif /* MACH_KDB */ + +#endif /* IPC_PRINT_H */ diff --git a/ipc/ipc_pset.c b/ipc/ipc_pset.c new file mode 100644 index 0000000..30c12a2 --- /dev/null +++ b/ipc/ipc_pset.c @@ -0,0 +1,350 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989Carnegie Mellon University. + * Copyright (c) 1993,1994 The University of Utah and + * the Computer Systems Laboratory (CSL). + * 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, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF + * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM 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. + */ +/* + */ +/* + * File: ipc/ipc_pset.c + * Author: Rich Draves + * Date: 1989 + * + * Functions to manipulate IPC port sets. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if MACH_KDB +#include +#include +#endif /* MACH_KDB */ + + +/* + * Routine: ipc_pset_alloc + * Purpose: + * Allocate a port set. + * Conditions: + * Nothing locked. If successful, the port set is returned + * locked. (The caller doesn't have a reference.) + * Returns: + * KERN_SUCCESS The port set is allocated. + * KERN_INVALID_TASK The space is dead. + * KERN_NO_SPACE No room for an entry in the space. + * KERN_RESOURCE_SHORTAGE Couldn't allocate memory. + */ + +kern_return_t +ipc_pset_alloc( + ipc_space_t space, + mach_port_name_t *namep, + ipc_pset_t *psetp) +{ + ipc_pset_t pset; + mach_port_name_t name; + kern_return_t kr; + + kr = ipc_object_alloc(space, IOT_PORT_SET, + MACH_PORT_TYPE_PORT_SET, 0, + &name, (ipc_object_t *) &pset); + if (kr != KERN_SUCCESS) + return kr; + /* pset is locked */ + + ipc_target_init(&pset->ips_target, name); + + *namep = name; + *psetp = pset; + return KERN_SUCCESS; +} + +/* + * Routine: ipc_pset_alloc_name + * Purpose: + * Allocate a port set, with a specific name. + * Conditions: + * Nothing locked. If successful, the port set is returned + * locked. (The caller doesn't have a reference.) + * Returns: + * KERN_SUCCESS The port set is allocated. + * KERN_INVALID_TASK The space is dead. + * KERN_NAME_EXISTS The name already denotes a right. + * KERN_RESOURCE_SHORTAGE Couldn't allocate memory. + */ + +kern_return_t +ipc_pset_alloc_name( + ipc_space_t space, + mach_port_name_t name, + ipc_pset_t *psetp) +{ + ipc_pset_t pset; + kern_return_t kr; + + kr = ipc_object_alloc_name(space, IOT_PORT_SET, + MACH_PORT_TYPE_PORT_SET, 0, + name, (ipc_object_t *) &pset); + if (kr != KERN_SUCCESS) + return kr; + /* pset is locked */ + + ipc_target_init(&pset->ips_target, name); + + *psetp = pset; + return KERN_SUCCESS; +} + +/* + * Routine: ipc_pset_add + * Purpose: + * Puts a port into a port set. + * The port set gains a reference. + * Conditions: + * Both port and port set are locked and active. + * The port isn't already in a set. + * The owner of the port set is also receiver for the port. + */ + +void +ipc_pset_add( + ipc_pset_t pset, + ipc_port_t port) +{ + assert(ips_active(pset)); + assert(ip_active(port)); + assert(port->ip_pset == IPS_NULL); + + port->ip_pset = pset; + port->ip_cur_target = &pset->ips_target; + ips_reference(pset); + + imq_lock(&port->ip_messages); + imq_lock(&pset->ips_messages); + + /* move messages from port's queue to the port set's queue */ + + ipc_mqueue_move(&pset->ips_messages, &port->ip_messages, port); + imq_unlock(&pset->ips_messages); + assert(ipc_kmsg_queue_empty(&port->ip_messages.imq_messages)); + + /* wake up threads waiting to receive from the port */ + + ipc_mqueue_changed(&port->ip_messages, MACH_RCV_PORT_CHANGED); + assert(ipc_thread_queue_empty(&port->ip_messages.imq_threads)); + imq_unlock(&port->ip_messages); +} + +/* + * Routine: ipc_pset_remove + * Purpose: + * Removes a port from a port set. + * The port set loses a reference. + * Conditions: + * Both port and port set are locked. + * The port must be active. + */ + +void +ipc_pset_remove( + ipc_pset_t pset, + ipc_port_t port) +{ + assert(ip_active(port)); + assert(port->ip_pset == pset); + + port->ip_pset = IPS_NULL; + port->ip_cur_target = &port->ip_target; + ips_release(pset); + + imq_lock(&port->ip_messages); + imq_lock(&pset->ips_messages); + + /* move messages from port set's queue to the port's queue */ + + ipc_mqueue_move(&port->ip_messages, &pset->ips_messages, port); + + imq_unlock(&pset->ips_messages); + imq_unlock(&port->ip_messages); +} + +/* + * Routine: ipc_pset_move + * Purpose: + * If nset is IPS_NULL, removes port + * from the port set it is in. Otherwise, adds + * port to nset, removing it from any set + * it might already be in. + * Conditions: + * The space is read-locked. + * Returns: + * KERN_SUCCESS Moved the port. + * KERN_NOT_IN_SET nset is null and port isn't in a set. + */ + +kern_return_t +ipc_pset_move( + ipc_space_t space, + ipc_port_t port, + ipc_pset_t nset) +{ + ipc_pset_t oset; + + /* + * While we've got the space locked, it holds refs for + * the port and nset (because of the entries). Also, + * they must be alive. While we've got port locked, it + * holds a ref for oset, which might not be alive. + */ + + ip_lock(port); + assert(ip_active(port)); + + oset = port->ip_pset; + + if (oset == nset) { + /* the port is already in the new set: a noop */ + + is_read_unlock(space); + } else if (oset == IPS_NULL) { + /* just add port to the new set */ + + ips_lock(nset); + assert(ips_active(nset)); + is_read_unlock(space); + + ipc_pset_add(nset, port); + + ips_unlock(nset); + } else if (nset == IPS_NULL) { + /* just remove port from the old set */ + + is_read_unlock(space); + ips_lock(oset); + + ipc_pset_remove(oset, port); + + if (ips_active(oset)) + ips_unlock(oset); + else { + ips_check_unlock(oset); + oset = IPS_NULL; /* trigger KERN_NOT_IN_SET */ + } + } else { + /* atomically move port from oset to nset */ + + if (oset < nset) { + ips_lock(oset); + ips_lock(nset); + } else { + ips_lock(nset); + ips_lock(oset); + } + + is_read_unlock(space); + assert(ips_active(nset)); + + ipc_pset_remove(oset, port); + ipc_pset_add(nset, port); + + ips_unlock(nset); + ips_check_unlock(oset); /* KERN_NOT_IN_SET not a possibility */ + } + + ip_unlock(port); + + return (((nset == IPS_NULL) && (oset == IPS_NULL)) ? + KERN_NOT_IN_SET : KERN_SUCCESS); +} + +/* + * Routine: ipc_pset_destroy + * Purpose: + * Destroys a port_set. + * + * Doesn't remove members from the port set; + * that happens lazily. As members are removed, + * their messages are removed from the queue. + * Conditions: + * The port_set is locked and alive. + * The caller has a reference, which is consumed. + * Afterwards, the port_set is unlocked and dead. + */ + +void +ipc_pset_destroy( + ipc_pset_t pset) +{ + assert(ips_active(pset)); + + pset->ips_object.io_bits &= ~IO_BITS_ACTIVE; + + imq_lock(&pset->ips_messages); + ipc_mqueue_changed(&pset->ips_messages, MACH_RCV_PORT_DIED); + imq_unlock(&pset->ips_messages); + + /* Common destruction for the IPC target. */ + ipc_target_terminate(&pset->ips_target); + + ips_release(pset); /* consume the ref our caller gave us */ + ips_check_unlock(pset); +} + + +#if MACH_KDB +#define printf kdbprintf + +/* + * Routine: ipc_pset_print + * Purpose: + * Pretty-print a port set for kdb. + */ + +void +ipc_pset_print( + const ipc_pset_t pset) +{ + printf("pset 0x%x\n", pset); + + indent += 2; + + ipc_object_print(&pset->ips_object); + iprintf("local_name = 0x%x\n", pset->ips_local_name); + iprintf("kmsgs = 0x%x", pset->ips_messages.imq_messages.ikmq_base); + printf(",rcvrs = 0x%x\n", pset->ips_messages.imq_threads.ithq_base); + + indent -= 2; +} + +#endif /* MACH_KDB */ diff --git a/ipc/ipc_pset.h b/ipc/ipc_pset.h new file mode 100644 index 0000000..3f94be5 --- /dev/null +++ b/ipc/ipc_pset.h @@ -0,0 +1,92 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University. + * Copyright (c) 1993,1994 The University of Utah and + * the Computer Systems Laboratory (CSL). + * 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, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF + * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM 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. + */ +/* + */ +/* + * File: ipc/ipc_pset.h + * Author: Rich Draves + * Date: 1989 + * + * Definitions for port sets. + */ + +#ifndef _IPC_IPC_PSET_H_ +#define _IPC_IPC_PSET_H_ + +#include +#include +#include +#include +#include "ipc_target.h" + +typedef struct ipc_pset { + struct ipc_target ips_target; + +} *ipc_pset_t; + +#define ips_object ips_target.ipt_object +#define ips_local_name ips_target.ipt_name +#define ips_messages ips_target.ipt_messages +#define ips_references ips_object.io_references + +#define IPS_NULL ((ipc_pset_t) IO_NULL) + +#define ips_active(pset) io_active(&(pset)->ips_object) +#define ips_lock(pset) io_lock(&(pset)->ips_object) +#define ips_lock_try(pset) io_lock_try(&(pset)->ips_object) +#define ips_unlock(pset) io_unlock(&(pset)->ips_object) +#define ips_check_unlock(pset) io_check_unlock(&(pset)->ips_object) +#define ips_reference(pset) io_reference(&(pset)->ips_object) +#define ips_release(pset) io_release(&(pset)->ips_object) + +extern kern_return_t +ipc_pset_alloc(ipc_space_t, mach_port_name_t *, ipc_pset_t *); + +extern kern_return_t +ipc_pset_alloc_name(ipc_space_t, mach_port_name_t, ipc_pset_t *); + +extern void +ipc_pset_add(ipc_pset_t, ipc_port_t); + +extern void +ipc_pset_remove(ipc_pset_t, ipc_port_t); + +extern kern_return_t +ipc_pset_move(ipc_space_t, ipc_port_t, ipc_pset_t); + +extern void +ipc_pset_destroy(ipc_pset_t); + +#define ipc_pset_reference(pset) \ + ipc_object_reference(&(pset)->ips_object) + +#define ipc_pset_release(pset) \ + ipc_object_release(&(pset)->ips_object) + +#endif /* _IPC_IPC_PSET_H_ */ diff --git a/ipc/ipc_right.c b/ipc/ipc_right.c new file mode 100644 index 0000000..79f70c3 --- /dev/null +++ b/ipc/ipc_right.c @@ -0,0 +1,2115 @@ +/* + * 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. + */ +/* + */ +/* + * File: ipc/ipc_right.c + * Author: Rich Draves + * Date: 1989 + * + * Functions to manipulate IPC capabilities. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +/* + * Routine: ipc_right_lookup_write + * Purpose: + * Finds an entry in a space, given the name. + * Conditions: + * Nothing locked. If successful, the space is write-locked. + * Returns: + * KERN_SUCCESS Found an entry. + * KERN_INVALID_TASK The space is dead. + * KERN_INVALID_NAME Name doesn't exist in space. + */ + +kern_return_t +ipc_right_lookup_write( + ipc_space_t space, + mach_port_name_t name, + ipc_entry_t *entryp) +{ + ipc_entry_t entry; + + assert(space != IS_NULL); + + is_write_lock(space); + + if (!space->is_active) { + is_write_unlock(space); + return KERN_INVALID_TASK; + } + + if ((entry = ipc_entry_lookup(space, name)) == IE_NULL) { + is_write_unlock(space); + return KERN_INVALID_NAME; + } + + *entryp = entry; + return KERN_SUCCESS; +} + +/* + * Routine: ipc_right_reverse + * Purpose: + * Translate (space, object) -> (name, entry). + * Only finds send/receive rights. + * Returns TRUE if an entry is found; if so, + * the object is locked and active. + * Conditions: + * The space must be locked (read or write) and active. + * Nothing else locked. + */ + +boolean_t +ipc_right_reverse( + ipc_space_t space, + ipc_object_t object, + mach_port_name_t *namep, + ipc_entry_t *entryp) +{ + ipc_port_t port; + mach_port_name_t name; + ipc_entry_t entry; + + /* would switch on io_otype to handle multiple types of object */ + + assert(space->is_active); + assert(io_otype(object) == IOT_PORT); + + port = (ipc_port_t) object; + + ip_lock(port); + if (!ip_active(port)) { + ip_unlock(port); + + return FALSE; + } + + if (port->ip_receiver == space) { + name = port->ip_receiver_name; + assert(name != MACH_PORT_NULL); + + entry = ipc_entry_lookup(space, name); + + assert(entry != IE_NULL); + assert(entry->ie_bits & MACH_PORT_TYPE_RECEIVE); + assert(port == (ipc_port_t) entry->ie_object); + + *namep = name; + *entryp = entry; + return TRUE; + } + + if ((*entryp = ipc_reverse_lookup(space, (ipc_object_t) port))) { + *namep = (*entryp)->ie_name; + assert((entry = *entryp) != IE_NULL); + assert(IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_SEND); + assert(port == (ipc_port_t) entry->ie_object); + + return TRUE; + } + + ip_unlock(port); + return FALSE; +} + +/* + * Routine: ipc_right_dnrequest + * Purpose: + * Make a dead-name request, returning the previously + * registered send-once right. If notify is IP_NULL, + * just cancels the previously registered request. + * + * This interacts with the IE_BITS_COMPAT, because they + * both use ie_request. If this is a compat entry, then + * previous always gets IP_NULL. If notify is IP_NULL, + * then the entry remains a compat entry. Otherwise + * the real dead-name request is registered and the entry + * is no longer a compat entry. + * Conditions: + * Nothing locked. May allocate memory. + * Only consumes/returns refs if successful. + * Returns: + * KERN_SUCCESS Made/canceled dead-name request. + * KERN_INVALID_TASK The space is dead. + * KERN_INVALID_NAME Name doesn't exist in space. + * KERN_INVALID_RIGHT Name doesn't denote port/dead rights. + * KERN_INVALID_ARGUMENT Name denotes dead name, but + * immediate is FALSE or notify is IP_NULL. + * KERN_UREFS_OVERFLOW Name denotes dead name, but + * generating immediate notif. would overflow urefs. + * KERN_RESOURCE_SHORTAGE Couldn't allocate memory. + */ + +kern_return_t +ipc_right_dnrequest( + ipc_space_t space, + mach_port_name_t name, + boolean_t immediate, + ipc_port_t notify, + ipc_port_t *previousp) +{ + ipc_port_t previous; + + for (;;) { + ipc_entry_t entry; + ipc_entry_bits_t bits; + kern_return_t kr; + + kr = ipc_right_lookup_write(space, name, &entry); + if (kr != KERN_SUCCESS) + return kr; + /* space is write-locked and active */ + + bits = entry->ie_bits; + if (bits & MACH_PORT_TYPE_PORT_RIGHTS) { + ipc_port_t port; + ipc_port_request_index_t request; + + port = (ipc_port_t) entry->ie_object; + assert(port != IP_NULL); + + if (!ipc_right_check(space, port, name, entry)) { + /* port is locked and active */ + + if (notify == IP_NULL) { + previous = ipc_right_dncancel_macro( + space, port, name, entry); + + ip_unlock(port); + is_write_unlock(space); + break; + } + + /* + * If a registered soright exists, + * want to atomically switch with it. + * If ipc_port_dncancel finds us a + * soright, then the following + * ipc_port_dnrequest will reuse + * that slot, so we are guaranteed + * not to unlock and retry. + */ + + previous = ipc_right_dncancel_macro(space, + port, name, entry); + + kr = ipc_port_dnrequest(port, name, notify, + &request); + if (kr != KERN_SUCCESS) { + assert(previous == IP_NULL); + is_write_unlock(space); + + kr = ipc_port_dngrow(port); + /* port is unlocked */ + if (kr != KERN_SUCCESS) + return kr; + + continue; + } + + assert(request != 0); + ip_unlock(port); + + entry->ie_request = request; + is_write_unlock(space); + break; + } + + bits = entry->ie_bits; + assert(bits & MACH_PORT_TYPE_DEAD_NAME); + } + + if ((bits & MACH_PORT_TYPE_DEAD_NAME) && + immediate && (notify != IP_NULL)) { + mach_port_urefs_t urefs = IE_BITS_UREFS(bits); + + assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME); + assert(urefs > 0); + + if (MACH_PORT_UREFS_OVERFLOW(urefs, 1)) { + is_write_unlock(space); + return KERN_UREFS_OVERFLOW; + } + + entry->ie_bits = bits + 1; /* increment urefs */ + is_write_unlock(space); + + ipc_notify_dead_name(notify, name); + previous = IP_NULL; + break; + } + + is_write_unlock(space); + if (bits & MACH_PORT_TYPE_PORT_OR_DEAD) + return KERN_INVALID_ARGUMENT; + else + return KERN_INVALID_RIGHT; + } + + *previousp = previous; + return KERN_SUCCESS; +} + +/* + * Routine: ipc_right_dncancel + * Purpose: + * Cancel a dead-name request and return the send-once right. + * Afterwards, entry->ie_request == 0. + * Conditions: + * The space must be write-locked; the port must be locked. + * The port must be active; the space doesn't have to be. + */ + +ipc_port_t +ipc_right_dncancel( + ipc_space_t space, + ipc_port_t port, + mach_port_name_t name, + ipc_entry_t entry) +{ + ipc_port_t dnrequest; + + assert(ip_active(port)); + assert(port == (ipc_port_t) entry->ie_object); + + dnrequest = ipc_port_dncancel(port, name, entry->ie_request); + entry->ie_request = 0; + + return dnrequest; +} + +/* + * Routine: ipc_right_inuse + * Purpose: + * Check if an entry is being used. + * Returns TRUE if it is. + * Conditions: + * The space is write-locked and active. + * It is unlocked if the entry is inuse. + */ + +boolean_t +ipc_right_inuse( + ipc_space_t space, + mach_port_name_t name, + ipc_entry_t entry) +{ + ipc_entry_bits_t bits = entry->ie_bits; + + if (IE_BITS_TYPE(bits) != MACH_PORT_TYPE_NONE) { + is_write_unlock(space); + return TRUE; + } + + return FALSE; +} + +/* + * Routine: ipc_right_check + * Purpose: + * Check if the port has died. If it has, + * clean up the entry and return TRUE. + * Conditions: + * The space is write-locked; the port is not locked. + * If returns FALSE, the port is also locked and active. + * Otherwise, entry is converted to a dead name, freeing + * a reference to port. + */ + +boolean_t +ipc_right_check( + ipc_space_t space, + ipc_port_t port, + mach_port_name_t name, + ipc_entry_t entry) +{ + ipc_entry_bits_t bits; + + assert(space->is_active); + assert(port == (ipc_port_t) entry->ie_object); + + ip_lock(port); + if (ip_active(port)) + return FALSE; + ip_unlock(port); + + /* this was either a pure send right or a send-once right */ + + bits = entry->ie_bits; + assert((bits & MACH_PORT_TYPE_RECEIVE) == 0); + assert(IE_BITS_UREFS(bits) > 0); + + if (bits & MACH_PORT_TYPE_SEND) { + assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND); + + /* clean up msg-accepted request */ + + if (bits & IE_BITS_MAREQUEST) { + bits &= ~IE_BITS_MAREQUEST; + + ipc_marequest_cancel(space, name); + } + + ipc_reverse_remove(space, (ipc_object_t) port); + } else { + assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE); + assert(IE_BITS_UREFS(bits) == 1); + assert((bits & IE_BITS_MAREQUEST) == 0); + } + + ipc_port_release(port); + + /* convert entry to dead name */ + + bits = (bits &~ IE_BITS_TYPE_MASK) | MACH_PORT_TYPE_DEAD_NAME; + + if (entry->ie_request != 0) { + assert(IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX); + + entry->ie_request = 0; + bits++; /* increment urefs */ + } + + entry->ie_bits = bits; + entry->ie_object = IO_NULL; + + return TRUE; +} + +/* + * Routine: ipc_right_clean + * Purpose: + * Cleans up an entry in a dead space. + * The entry isn't deallocated or removed + * from the reverse mappings. + * Conditions: + * The space is dead and unlocked. + */ + +void +ipc_right_clean( + ipc_space_t space, + mach_port_name_t name, + ipc_entry_t entry) +{ + ipc_entry_bits_t bits = entry->ie_bits; + mach_port_type_t type = IE_BITS_TYPE(bits); + + assert(!space->is_active); + + /* + * We can't clean up IE_BITS_MAREQUEST when the space is dead. + * This is because ipc_marequest_destroy can't turn off + * the bit if the space is dead. Hence, it might be on + * even though the marequest has been destroyed. It's OK + * not to cancel the marequest, because ipc_marequest_destroy + * cancels for us if the space is dead. + * + * IE_BITS_COMPAT/ipc_right_dncancel doesn't have this + * problem, because we check that the port is active. If + * we didn't cancel IE_BITS_COMPAT, ipc_port_destroy + * would still work, but dead space refs would accumulate + * in ip_dnrequests. They would use up slots in + * ip_dnrequests and keep the spaces from being freed. + */ + + switch (type) { + case MACH_PORT_TYPE_DEAD_NAME: + assert(entry->ie_request == 0); + assert(entry->ie_object == IO_NULL); + assert((bits & IE_BITS_MAREQUEST) == 0); + break; + + case MACH_PORT_TYPE_PORT_SET: { + ipc_pset_t pset = (ipc_pset_t) entry->ie_object; + + assert(entry->ie_request == 0); + assert((bits & IE_BITS_MAREQUEST) == 0); + assert(pset != IPS_NULL); + + ips_lock(pset); + assert(ips_active(pset)); + + ipc_pset_destroy(pset); /* consumes ref, unlocks */ + break; + } + + case MACH_PORT_TYPE_SEND: + case MACH_PORT_TYPE_RECEIVE: + case MACH_PORT_TYPE_SEND_RECEIVE: + case MACH_PORT_TYPE_SEND_ONCE: { + ipc_port_t port = (ipc_port_t) entry->ie_object; + ipc_port_t dnrequest; + ipc_port_t nsrequest = IP_NULL; + mach_port_mscount_t mscount = 0; /* '=0' to shut up lint */ + + assert(port != IP_NULL); + ip_lock(port); + + if (!ip_active(port)) { + ip_release(port); + ip_check_unlock(port); + break; + } + + dnrequest = ipc_right_dncancel_macro(space, port, name, entry); + + if (type & MACH_PORT_TYPE_SEND) { + assert(port->ip_srights > 0); + if (--port->ip_srights == 0) { + nsrequest = port->ip_nsrequest; + if (nsrequest != IP_NULL) { + port->ip_nsrequest = IP_NULL; + mscount = port->ip_mscount; + } + } + } + + if (type & MACH_PORT_TYPE_RECEIVE) { + assert(port->ip_receiver_name == name); + assert(port->ip_receiver == space); + + ipc_port_clear_receiver(port); + ipc_port_destroy(port); /* consumes our ref, unlocks */ + } else if (type & MACH_PORT_TYPE_SEND_ONCE) { + assert(port->ip_sorights > 0); + ip_unlock(port); + + ipc_notify_send_once(port); /* consumes our ref */ + } else { + assert(port->ip_receiver != space); + + ip_release(port); + ip_unlock(port); /* port is active */ + } + + if (nsrequest != IP_NULL) + ipc_notify_no_senders(nsrequest, mscount); + + if (dnrequest != IP_NULL) + ipc_notify_port_deleted(dnrequest, name); + break; + } + + default: +#if MACH_ASSERT + assert(!"ipc_right_clean: strange type"); +#else + panic("ipc_right_clean: strange type"); +#endif + } +} + +/* + * Routine: ipc_right_destroy + * Purpose: + * Destroys an entry in a space. + * Conditions: + * The space is write-locked, and is unlocked upon return. + * The space must be active. + * Returns: + * KERN_SUCCESS The entry was destroyed. + */ + +kern_return_t +ipc_right_destroy( + ipc_space_t space, + mach_port_name_t name, + ipc_entry_t entry) +{ + ipc_entry_bits_t bits = entry->ie_bits; + mach_port_type_t type = IE_BITS_TYPE(bits); + + assert(space->is_active); + + switch (type) { + case MACH_PORT_TYPE_DEAD_NAME: + assert(entry->ie_request == 0); + assert(entry->ie_object == IO_NULL); + assert((bits & IE_BITS_MAREQUEST) == 0); + + ipc_entry_dealloc(space, name, entry); + is_write_unlock(space); + break; + + case MACH_PORT_TYPE_PORT_SET: { + ipc_pset_t pset = (ipc_pset_t) entry->ie_object; + + assert(entry->ie_request == 0); + assert(pset != IPS_NULL); + + entry->ie_object = IO_NULL; + ipc_entry_dealloc(space, name, entry); + + ips_lock(pset); + assert(ips_active(pset)); + is_write_unlock(space); + + ipc_pset_destroy(pset); /* consumes ref, unlocks */ + break; + } + + case MACH_PORT_TYPE_SEND: + case MACH_PORT_TYPE_RECEIVE: + case MACH_PORT_TYPE_SEND_RECEIVE: + case MACH_PORT_TYPE_SEND_ONCE: { + ipc_port_t port = (ipc_port_t) entry->ie_object; + ipc_port_t nsrequest = IP_NULL; + mach_port_mscount_t mscount = 0; /* '=0' to shut up lint */ + ipc_port_t dnrequest; + + assert(port != IP_NULL); + + if (bits & IE_BITS_MAREQUEST) { + assert(type & MACH_PORT_TYPE_SEND_RECEIVE); + + ipc_marequest_cancel(space, name); + } + + if (type == MACH_PORT_TYPE_SEND) + ipc_reverse_remove(space, (ipc_object_t) port); + + ip_lock(port); + + if (!ip_active(port)) { + assert((type & MACH_PORT_TYPE_RECEIVE) == 0); + + ip_release(port); + ip_check_unlock(port); + + entry->ie_request = 0; + entry->ie_object = IO_NULL; + ipc_entry_dealloc(space, name, entry); + is_write_unlock(space); + + break; + } + + dnrequest = ipc_right_dncancel_macro(space, port, name, entry); + + entry->ie_object = IO_NULL; + ipc_entry_dealloc(space, name, entry); + is_write_unlock(space); + + if (type & MACH_PORT_TYPE_SEND) { + assert(port->ip_srights > 0); + if (--port->ip_srights == 0) { + nsrequest = port->ip_nsrequest; + if (nsrequest != IP_NULL) { + port->ip_nsrequest = IP_NULL; + mscount = port->ip_mscount; + } + } + } + + if (type & MACH_PORT_TYPE_RECEIVE) { + assert(ip_active(port)); + assert(port->ip_receiver == space); + + ipc_port_clear_receiver(port); + ipc_port_destroy(port); /* consumes our ref, unlocks */ + } else if (type & MACH_PORT_TYPE_SEND_ONCE) { + assert(port->ip_sorights > 0); + ip_unlock(port); + + ipc_notify_send_once(port); /* consumes our ref */ + } else { + assert(port->ip_receiver != space); + + ip_release(port); + ip_unlock(port); + } + + if (nsrequest != IP_NULL) + ipc_notify_no_senders(nsrequest, mscount); + + if (dnrequest != IP_NULL) + ipc_notify_port_deleted(dnrequest, name); + break; + } + + default: +#if MACH_ASSERT + assert(!"ipc_right_destroy: strange type"); +#else + panic("ipc_right_destroy: strange type"); +#endif + } + + return KERN_SUCCESS; +} + +/* + * Routine: ipc_right_dealloc + * Purpose: + * Releases a send/send-once/dead-name user ref. + * Like ipc_right_delta with a delta of -1, + * but looks at the entry to determine the right. + * Conditions: + * The space is write-locked, and is unlocked upon return. + * The space must be active. + * Returns: + * KERN_SUCCESS A user ref was released. + * KERN_INVALID_RIGHT Entry has wrong type. + */ + +kern_return_t +ipc_right_dealloc( + ipc_space_t space, + mach_port_name_t name, + ipc_entry_t entry) +{ + ipc_entry_bits_t bits = entry->ie_bits; + mach_port_type_t type = IE_BITS_TYPE(bits); + + assert(space->is_active); + + switch (type) { + case MACH_PORT_TYPE_DEAD_NAME: { + dead_name: + + assert(IE_BITS_UREFS(bits) > 0); + assert(entry->ie_request == 0); + assert(entry->ie_object == IO_NULL); + assert((bits & IE_BITS_MAREQUEST) == 0); + + if (IE_BITS_UREFS(bits) == 1) + ipc_entry_dealloc(space, name, entry); + else + entry->ie_bits = bits-1; /* decrement urefs */ + + is_write_unlock(space); + break; + } + + case MACH_PORT_TYPE_SEND_ONCE: { + ipc_port_t port, dnrequest; + + assert(IE_BITS_UREFS(bits) == 1); + assert((bits & IE_BITS_MAREQUEST) == 0); + + port = (ipc_port_t) entry->ie_object; + assert(port != IP_NULL); + + if (ipc_right_check(space, port, name, entry)) { + bits = entry->ie_bits; + assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME); + goto dead_name; + } + /* port is locked and active */ + + assert(port->ip_sorights > 0); + + dnrequest = ipc_right_dncancel_macro(space, port, name, entry); + ip_unlock(port); + + entry->ie_object = IO_NULL; + ipc_entry_dealloc(space, name, entry); + is_write_unlock(space); + + ipc_notify_send_once(port); + + if (dnrequest != IP_NULL) + ipc_notify_port_deleted(dnrequest, name); + break; + } + + case MACH_PORT_TYPE_SEND: { + ipc_port_t port; + ipc_port_t dnrequest = IP_NULL; + ipc_port_t nsrequest = IP_NULL; + mach_port_mscount_t mscount = 0; /* '=0' to shut up lint */ + + assert(IE_BITS_UREFS(bits) > 0); + + port = (ipc_port_t) entry->ie_object; + assert(port != IP_NULL); + + if (ipc_right_check(space, port, name, entry)) { + bits = entry->ie_bits; + assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME); + goto dead_name; + } + /* port is locked and active */ + + assert(port->ip_srights > 0); + + if (IE_BITS_UREFS(bits) == 1) { + if (--port->ip_srights == 0) { + nsrequest = port->ip_nsrequest; + if (nsrequest != IP_NULL) { + port->ip_nsrequest = IP_NULL; + mscount = port->ip_mscount; + } + } + + dnrequest = ipc_right_dncancel_macro(space, port, + name, entry); + + ipc_reverse_remove(space, (ipc_object_t) port); + + if (bits & IE_BITS_MAREQUEST) + ipc_marequest_cancel(space, name); + + ip_release(port); + entry->ie_object = IO_NULL; + ipc_entry_dealloc(space, name, entry); + } else + entry->ie_bits = bits-1; /* decrement urefs */ + + ip_unlock(port); /* even if dropped a ref, port is active */ + is_write_unlock(space); + + if (nsrequest != IP_NULL) + ipc_notify_no_senders(nsrequest, mscount); + + if (dnrequest != IP_NULL) + ipc_notify_port_deleted(dnrequest, name); + break; + } + + case MACH_PORT_TYPE_SEND_RECEIVE: { + ipc_port_t port; + ipc_port_t nsrequest = IP_NULL; + mach_port_mscount_t mscount = 0; /* '=0' to shut up lint */ + + assert(IE_BITS_UREFS(bits) > 0); + + port = (ipc_port_t) entry->ie_object; + assert(port != IP_NULL); + + ip_lock(port); + assert(ip_active(port)); + assert(port->ip_receiver_name == name); + assert(port->ip_receiver == space); + assert(port->ip_srights > 0); + + if (IE_BITS_UREFS(bits) == 1) { + if (--port->ip_srights == 0) { + nsrequest = port->ip_nsrequest; + if (nsrequest != IP_NULL) { + port->ip_nsrequest = IP_NULL; + mscount = port->ip_mscount; + } + } + + entry->ie_bits = bits &~ (IE_BITS_UREFS_MASK| + MACH_PORT_TYPE_SEND); + } else + entry->ie_bits = bits-1; /* decrement urefs */ + + ip_unlock(port); + is_write_unlock(space); + + if (nsrequest != IP_NULL) + ipc_notify_no_senders(nsrequest, mscount); + break; + } + + default: + is_write_unlock(space); + return KERN_INVALID_RIGHT; + } + + return KERN_SUCCESS; +} + +/* + * Routine: ipc_right_delta + * Purpose: + * Modifies the user-reference count for a right. + * May deallocate the right, if the count goes to zero. + * Conditions: + * The space is write-locked, and is unlocked upon return. + * The space must be active. + * Returns: + * KERN_SUCCESS Count was modified. + * KERN_INVALID_RIGHT Entry has wrong type. + * KERN_INVALID_VALUE Bad delta for the right. + * KERN_UREFS_OVERFLOW OK delta, except would overflow. + */ + +kern_return_t +ipc_right_delta( + ipc_space_t space, + mach_port_name_t name, + ipc_entry_t entry, + mach_port_right_t right, + mach_port_delta_t delta) +{ + ipc_entry_bits_t bits = entry->ie_bits; + + assert(space->is_active); + assert(right < MACH_PORT_RIGHT_NUMBER); + + /* Rights-specific restrictions and operations. */ + + switch (right) { + case MACH_PORT_RIGHT_PORT_SET: { + ipc_pset_t pset; + + if ((bits & MACH_PORT_TYPE_PORT_SET) == 0) + goto invalid_right; + + assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_PORT_SET); + assert(IE_BITS_UREFS(bits) == 0); + assert((bits & IE_BITS_MAREQUEST) == 0); + assert(entry->ie_request == 0); + + if (delta == 0) + goto success; + + if (delta != -1) + goto invalid_value; + + pset = (ipc_pset_t) entry->ie_object; + assert(pset != IPS_NULL); + + entry->ie_object = IO_NULL; + ipc_entry_dealloc(space, name, entry); + + ips_lock(pset); + assert(ips_active(pset)); + is_write_unlock(space); + + ipc_pset_destroy(pset); /* consumes ref, unlocks */ + break; + } + + case MACH_PORT_RIGHT_RECEIVE: { + ipc_port_t port; + ipc_port_t dnrequest = IP_NULL; + + if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) + goto invalid_right; + + if (delta == 0) + goto success; + + if (delta != -1) + goto invalid_value; + + if (bits & IE_BITS_MAREQUEST) { + bits &= ~IE_BITS_MAREQUEST; + + ipc_marequest_cancel(space, name); + } + + port = (ipc_port_t) entry->ie_object; + assert(port != IP_NULL); + + /* + * The port lock is needed for ipc_right_dncancel; + * otherwise, we wouldn't have to take the lock + * until just before dropping the space lock. + */ + + ip_lock(port); + assert(ip_active(port)); + assert(port->ip_receiver_name == name); + assert(port->ip_receiver == space); + + if (bits & MACH_PORT_TYPE_SEND) { + assert(IE_BITS_TYPE(bits) == + MACH_PORT_TYPE_SEND_RECEIVE); + assert(IE_BITS_UREFS(bits) > 0); + assert(IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX); + assert(port->ip_srights > 0); + + /* + * The remaining send right turns into a + * dead name. Notice we don't decrement + * ip_srights, generate a no-senders notif, + * or use ipc_right_dncancel, because the + * port is destroyed "first". + */ + + bits &= ~IE_BITS_TYPE_MASK; + bits |= MACH_PORT_TYPE_DEAD_NAME; + + if (entry->ie_request != 0) { + entry->ie_request = 0; + bits++; /* increment urefs */ + } + + entry->ie_bits = bits; + entry->ie_object = IO_NULL; + } else { + assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_RECEIVE); + assert(IE_BITS_UREFS(bits) == 0); + + dnrequest = ipc_right_dncancel_macro(space, port, + name, entry); + + entry->ie_object = IO_NULL; + ipc_entry_dealloc(space, name, entry); + } + is_write_unlock(space); + + ipc_port_clear_receiver(port); + ipc_port_destroy(port); /* consumes ref, unlocks */ + + if (dnrequest != IP_NULL) + ipc_notify_port_deleted(dnrequest, name); + break; + } + + case MACH_PORT_RIGHT_SEND_ONCE: { + ipc_port_t port, dnrequest; + + if ((bits & MACH_PORT_TYPE_SEND_ONCE) == 0) + goto invalid_right; + + assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE); + assert(IE_BITS_UREFS(bits) == 1); + assert((bits & IE_BITS_MAREQUEST) == 0); + + if ((delta > 0) || (delta < -1)) + goto invalid_value; + + port = (ipc_port_t) entry->ie_object; + assert(port != IP_NULL); + + if (ipc_right_check(space, port, name, entry)) { + assert(!(entry->ie_bits & MACH_PORT_TYPE_SEND_ONCE)); + goto invalid_right; + } + /* port is locked and active */ + + assert(port->ip_sorights > 0); + + if (delta == 0) { + ip_unlock(port); + goto success; + } + + dnrequest = ipc_right_dncancel_macro(space, port, name, entry); + ip_unlock(port); + + entry->ie_object = IO_NULL; + ipc_entry_dealloc(space, name, entry); + is_write_unlock(space); + + ipc_notify_send_once(port); + + if (dnrequest != IP_NULL) + ipc_notify_port_deleted(dnrequest, name); + break; + } + + case MACH_PORT_RIGHT_DEAD_NAME: { + mach_port_urefs_t urefs; + + if (bits & MACH_PORT_TYPE_SEND_RIGHTS) { + ipc_port_t port; + + port = (ipc_port_t) entry->ie_object; + assert(port != IP_NULL); + + if (!ipc_right_check(space, port, name, entry)) { + /* port is locked and active */ + ip_unlock(port); + goto invalid_right; + } + + bits = entry->ie_bits; + } else if ((bits & MACH_PORT_TYPE_DEAD_NAME) == 0) + goto invalid_right; + + assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME); + assert(IE_BITS_UREFS(bits) > 0); + assert((bits & IE_BITS_MAREQUEST) == 0); + assert(entry->ie_object == IO_NULL); + assert(entry->ie_request == 0); + + urefs = IE_BITS_UREFS(bits); + if (MACH_PORT_UREFS_UNDERFLOW(urefs, delta)) + goto invalid_value; + if (MACH_PORT_UREFS_OVERFLOW(urefs, delta)) + goto urefs_overflow; + + if ((urefs + delta) == 0) + ipc_entry_dealloc(space, name, entry); + else + entry->ie_bits = bits + delta; + + is_write_unlock(space); + break; + } + + case MACH_PORT_RIGHT_SEND: { + mach_port_urefs_t urefs; + ipc_port_t port; + ipc_port_t dnrequest = IP_NULL; + ipc_port_t nsrequest = IP_NULL; + mach_port_mscount_t mscount = 0; /* '=0' to shut up lint */ + + if ((bits & MACH_PORT_TYPE_SEND) == 0) + goto invalid_right; + + /* maximum urefs for send is MACH_PORT_UREFS_MAX-1 */ + + urefs = IE_BITS_UREFS(bits); + if (MACH_PORT_UREFS_UNDERFLOW(urefs, delta)) + goto invalid_value; + if (MACH_PORT_UREFS_OVERFLOW(urefs+1, delta)) + goto urefs_overflow; + + port = (ipc_port_t) entry->ie_object; + assert(port != IP_NULL); + + if (ipc_right_check(space, port, name, entry)) { + assert((entry->ie_bits & MACH_PORT_TYPE_SEND) == 0); + goto invalid_right; + } + /* port is locked and active */ + + assert(port->ip_srights > 0); + + if ((urefs + delta) == 0) { + if (--port->ip_srights == 0) { + nsrequest = port->ip_nsrequest; + if (nsrequest != IP_NULL) { + port->ip_nsrequest = IP_NULL; + mscount = port->ip_mscount; + } + } + + if (bits & MACH_PORT_TYPE_RECEIVE) { + assert(port->ip_receiver_name == name); + assert(port->ip_receiver == space); + assert(IE_BITS_TYPE(bits) == + MACH_PORT_TYPE_SEND_RECEIVE); + + entry->ie_bits = bits &~ (IE_BITS_UREFS_MASK| + MACH_PORT_TYPE_SEND); + } else { + assert(IE_BITS_TYPE(bits) == + MACH_PORT_TYPE_SEND); + + dnrequest = ipc_right_dncancel_macro( + space, port, name, entry); + + ipc_reverse_remove(space, (ipc_object_t) port); + + if (bits & IE_BITS_MAREQUEST) + ipc_marequest_cancel(space, name); + + ip_release(port); + entry->ie_object = IO_NULL; + ipc_entry_dealloc(space, name, entry); + } + } else + entry->ie_bits = bits + delta; + + ip_unlock(port); /* even if dropped a ref, port is active */ + is_write_unlock(space); + + if (nsrequest != IP_NULL) + ipc_notify_no_senders(nsrequest, mscount); + + if (dnrequest != IP_NULL) + ipc_notify_port_deleted(dnrequest, name); + break; + } + + default: +#if MACH_ASSERT + assert(!"ipc_right_delta: strange right"); +#else + panic("ipc_right_delta: strange right"); +#endif + } + + return KERN_SUCCESS; + + success: + is_write_unlock(space); + return KERN_SUCCESS; + + invalid_right: + is_write_unlock(space); + return KERN_INVALID_RIGHT; + + invalid_value: + is_write_unlock(space); + return KERN_INVALID_VALUE; + + urefs_overflow: + is_write_unlock(space); + return KERN_UREFS_OVERFLOW; +} + +/* + * Routine: ipc_right_info + * Purpose: + * Retrieves information about the right. + * Conditions: + * The space is write-locked, and is unlocked upon return + * if the call is unsuccessful. The space must be active. + * Returns: + * KERN_SUCCESS Retrieved info; space still locked. + */ + +kern_return_t +ipc_right_info( + ipc_space_t space, + mach_port_name_t name, + ipc_entry_t entry, + mach_port_type_t *typep, + mach_port_urefs_t *urefsp) +{ + ipc_entry_bits_t bits = entry->ie_bits; + ipc_port_request_index_t request; + mach_port_type_t type; + + if (bits & MACH_PORT_TYPE_SEND_RIGHTS) { + ipc_port_t port = (ipc_port_t) entry->ie_object; + + if (ipc_right_check(space, port, name, entry)) { + bits = entry->ie_bits; + assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME); + } else + ip_unlock(port); + } + + type = IE_BITS_TYPE(bits); + request = entry->ie_request; + + if (request != 0) + type |= MACH_PORT_TYPE_DNREQUEST; + if (bits & IE_BITS_MAREQUEST) + type |= MACH_PORT_TYPE_MAREQUEST; + + *typep = type; + *urefsp = IE_BITS_UREFS(bits); + return KERN_SUCCESS; +} + +/* + * Routine: ipc_right_copyin_check + * Purpose: + * Check if a subsequent ipc_right_copyin would succeed. + * Conditions: + * The space is locked (read or write) and active. + */ + +boolean_t +ipc_right_copyin_check( + ipc_space_t space, + mach_port_name_t name, + ipc_entry_t entry, + mach_msg_type_name_t msgt_name) +{ + ipc_entry_bits_t bits = entry->ie_bits; + + assert(space->is_active); + + switch (msgt_name) { + case MACH_MSG_TYPE_MAKE_SEND: + case MACH_MSG_TYPE_MAKE_SEND_ONCE: + case MACH_MSG_TYPE_MOVE_RECEIVE: + if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) + return FALSE; + + break; + + case MACH_MSG_TYPE_COPY_SEND: + case MACH_MSG_TYPE_MOVE_SEND: + case MACH_MSG_TYPE_MOVE_SEND_ONCE: { + ipc_port_t port; + boolean_t active; + + if (bits & MACH_PORT_TYPE_DEAD_NAME) + break; + + if ((bits & MACH_PORT_TYPE_SEND_RIGHTS) == 0) + return FALSE; + + port = (ipc_port_t) entry->ie_object; + assert(port != IP_NULL); + + ip_lock(port); + active = ip_active(port); + ip_unlock(port); + + if (!active) { + break; + } + + if (msgt_name == MACH_MSG_TYPE_MOVE_SEND_ONCE) { + if ((bits & MACH_PORT_TYPE_SEND_ONCE) == 0) + return FALSE; + } else { + if ((bits & MACH_PORT_TYPE_SEND) == 0) + return FALSE; + } + + break; + } + + default: +#if MACH_ASSERT + assert(!"ipc_right_copyin_check: strange rights"); +#else + panic("ipc_right_copyin_check: strange rights"); +#endif + } + + return TRUE; +} + +/* + * Routine: ipc_right_copyin + * Purpose: + * Copyin a capability from a space. + * If successful, the caller gets a ref + * for the resulting object, unless it is IO_DEAD, + * and possibly a send-once right which should + * be used in a port-deleted notification. + * + * If deadok is not TRUE, the copyin operation + * will fail instead of producing IO_DEAD. + * + * The entry is never deallocated (except + * when KERN_INVALID_NAME), so the caller + * should deallocate the entry if its type + * is MACH_PORT_TYPE_NONE. + * Conditions: + * The space is write-locked and active. + * Returns: + * KERN_SUCCESS Acquired an object, possibly IO_DEAD. + * KERN_INVALID_RIGHT Name doesn't denote correct right. + */ + +kern_return_t +ipc_right_copyin( + ipc_space_t space, + mach_port_name_t name, + ipc_entry_t entry, + mach_msg_type_name_t msgt_name, + boolean_t deadok, + ipc_object_t *objectp, + ipc_port_t *sorightp) +{ + ipc_entry_bits_t bits = entry->ie_bits; + + assert(space->is_active); + + switch (msgt_name) { + case MACH_MSG_TYPE_MAKE_SEND: { + ipc_port_t port; + + if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) + goto invalid_right; + + port = (ipc_port_t) entry->ie_object; + assert(port != IP_NULL); + + ip_lock(port); + assert(ip_active(port)); + assert(port->ip_receiver_name == name); + assert(port->ip_receiver == space); + + port->ip_mscount++; + port->ip_srights++; + ip_reference(port); + ip_unlock(port); + + *objectp = (ipc_object_t) port; + *sorightp = IP_NULL; + break; + } + + case MACH_MSG_TYPE_MAKE_SEND_ONCE: { + ipc_port_t port; + + if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) + goto invalid_right; + + port = (ipc_port_t) entry->ie_object; + assert(port != IP_NULL); + + ip_lock(port); + assert(ip_active(port)); + assert(port->ip_receiver_name == name); + assert(port->ip_receiver == space); + + port->ip_sorights++; + ip_reference(port); + ip_unlock(port); + + *objectp = (ipc_object_t) port; + *sorightp = IP_NULL; + break; + } + + case MACH_MSG_TYPE_MOVE_RECEIVE: { + ipc_port_t port; + ipc_port_t dnrequest = IP_NULL; + + if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) + goto invalid_right; + + port = (ipc_port_t) entry->ie_object; + assert(port != IP_NULL); + + ip_lock(port); + assert(ip_active(port)); + assert(port->ip_receiver_name == name); + assert(port->ip_receiver == space); + + if (bits & MACH_PORT_TYPE_SEND) { + assert(IE_BITS_TYPE(bits) == + MACH_PORT_TYPE_SEND_RECEIVE); + assert(IE_BITS_UREFS(bits) > 0); + assert(port->ip_srights > 0); + + entry->ie_name = name; + ipc_reverse_insert(space, (ipc_object_t) port, entry); + + ip_reference(port); + } else { + assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_RECEIVE); + assert(IE_BITS_UREFS(bits) == 0); + + dnrequest = ipc_right_dncancel_macro(space, port, + name, entry); + + if (bits & IE_BITS_MAREQUEST) + ipc_marequest_cancel(space, name); + + entry->ie_object = IO_NULL; + } + entry->ie_bits = bits &~ MACH_PORT_TYPE_RECEIVE; + + ipc_port_clear_receiver(port); + + port->ip_receiver_name = MACH_PORT_NULL; + port->ip_destination = IP_NULL; + + /* + * Clear the protected payload field to retain + * the behavior of mach_msg. + */ + ipc_port_flag_protected_payload_clear(port); + ip_unlock(port); + + *objectp = (ipc_object_t) port; + *sorightp = dnrequest; + break; + } + + case MACH_MSG_TYPE_COPY_SEND: { + ipc_port_t port; + + if (bits & MACH_PORT_TYPE_DEAD_NAME) + goto copy_dead; + + /* allow for dead send-once rights */ + + if ((bits & MACH_PORT_TYPE_SEND_RIGHTS) == 0) + goto invalid_right; + + assert(IE_BITS_UREFS(bits) > 0); + + port = (ipc_port_t) entry->ie_object; + assert(port != IP_NULL); + + if (ipc_right_check(space, port, name, entry)) { + bits = entry->ie_bits; + goto copy_dead; + } + /* port is locked and active */ + + if ((bits & MACH_PORT_TYPE_SEND) == 0) { + assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE); + assert(port->ip_sorights > 0); + + ip_unlock(port); + goto invalid_right; + } + + assert(port->ip_srights > 0); + + port->ip_srights++; + ip_reference(port); + ip_unlock(port); + + *objectp = (ipc_object_t) port; + *sorightp = IP_NULL; + break; + } + + case MACH_MSG_TYPE_MOVE_SEND: { + ipc_port_t port; + ipc_port_t dnrequest = IP_NULL; + + if (bits & MACH_PORT_TYPE_DEAD_NAME) + goto move_dead; + + /* allow for dead send-once rights */ + + if ((bits & MACH_PORT_TYPE_SEND_RIGHTS) == 0) + goto invalid_right; + + assert(IE_BITS_UREFS(bits) > 0); + + port = (ipc_port_t) entry->ie_object; + assert(port != IP_NULL); + + if (ipc_right_check(space, port, name, entry)) { + bits = entry->ie_bits; + goto move_dead; + } + /* port is locked and active */ + + if ((bits & MACH_PORT_TYPE_SEND) == 0) { + assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE); + assert(port->ip_sorights > 0); + + ip_unlock(port); + goto invalid_right; + } + + assert(port->ip_srights > 0); + + if (IE_BITS_UREFS(bits) == 1) { + if (bits & MACH_PORT_TYPE_RECEIVE) { + assert(port->ip_receiver_name == name); + assert(port->ip_receiver == space); + assert(IE_BITS_TYPE(bits) == + MACH_PORT_TYPE_SEND_RECEIVE); + + ip_reference(port); + } else { + assert(IE_BITS_TYPE(bits) == + MACH_PORT_TYPE_SEND); + + dnrequest = ipc_right_dncancel_macro( + space, port, name, entry); + + ipc_reverse_remove(space, (ipc_object_t) port); + + if (bits & IE_BITS_MAREQUEST) + ipc_marequest_cancel(space, name); + + entry->ie_object = IO_NULL; + } + entry->ie_bits = bits &~ + (IE_BITS_UREFS_MASK|MACH_PORT_TYPE_SEND); + } else { + port->ip_srights++; + ip_reference(port); + entry->ie_bits = bits-1; /* decrement urefs */ + } + + ip_unlock(port); + + *objectp = (ipc_object_t) port; + *sorightp = dnrequest; + break; + } + + case MACH_MSG_TYPE_MOVE_SEND_ONCE: { + ipc_port_t port; + ipc_port_t dnrequest; + + if (bits & MACH_PORT_TYPE_DEAD_NAME) + goto move_dead; + + /* allow for dead send rights */ + + if ((bits & MACH_PORT_TYPE_SEND_RIGHTS) == 0) + goto invalid_right; + + assert(IE_BITS_UREFS(bits) > 0); + + port = (ipc_port_t) entry->ie_object; + assert(port != IP_NULL); + + if (ipc_right_check(space, port, name, entry)) { + bits = entry->ie_bits; + goto move_dead; + } + /* port is locked and active */ + + if ((bits & MACH_PORT_TYPE_SEND_ONCE) == 0) { + assert(bits & MACH_PORT_TYPE_SEND); + assert(port->ip_srights > 0); + + ip_unlock(port); + goto invalid_right; + } + + assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE); + assert(IE_BITS_UREFS(bits) == 1); + assert((bits & IE_BITS_MAREQUEST) == 0); + assert(port->ip_sorights > 0); + + dnrequest = ipc_right_dncancel_macro(space, port, name, entry); + ip_unlock(port); + + entry->ie_object = IO_NULL; + entry->ie_bits = bits &~ MACH_PORT_TYPE_SEND_ONCE; + + *objectp = (ipc_object_t) port; + *sorightp = dnrequest; + break; + } + + default: +#if MACH_ASSERT + assert(!"ipc_right_copyin: strange rights"); +#else + panic("ipc_right_copyin: strange rights"); +#endif + } + + return KERN_SUCCESS; + + copy_dead: + assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME); + assert(IE_BITS_UREFS(bits) > 0); + assert((bits & IE_BITS_MAREQUEST) == 0); + assert(entry->ie_request == 0); + assert(entry->ie_object == 0); + + if (!deadok) + goto invalid_right; + + *objectp = IO_DEAD; + *sorightp = IP_NULL; + return KERN_SUCCESS; + + move_dead: + assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME); + assert(IE_BITS_UREFS(bits) > 0); + assert((bits & IE_BITS_MAREQUEST) == 0); + assert(entry->ie_request == 0); + assert(entry->ie_object == 0); + + if (!deadok) + goto invalid_right; + + if (IE_BITS_UREFS(bits) == 1) + entry->ie_bits = bits &~ MACH_PORT_TYPE_DEAD_NAME; + else + entry->ie_bits = bits-1; /* decrement urefs */ + + *objectp = IO_DEAD; + *sorightp = IP_NULL; + return KERN_SUCCESS; + + invalid_right: + return KERN_INVALID_RIGHT; +} + +/* + * Routine: ipc_right_copyin_undo + * Purpose: + * Undoes the effects of an ipc_right_copyin + * of a send/send-once right that is dead. + * (Object is either IO_DEAD or a dead port.) + * Conditions: + * The space is write-locked and active. + */ + +void +ipc_right_copyin_undo( + ipc_space_t space, + mach_port_name_t name, + ipc_entry_t entry, + mach_msg_type_name_t msgt_name, + ipc_object_t object, + ipc_port_t soright) +{ + ipc_entry_bits_t bits = entry->ie_bits; + + assert(space->is_active); + + assert((msgt_name == MACH_MSG_TYPE_MOVE_SEND) || + (msgt_name == MACH_MSG_TYPE_COPY_SEND) || + (msgt_name == MACH_MSG_TYPE_MOVE_SEND_ONCE)); + + if (soright != IP_NULL) { + assert((msgt_name == MACH_MSG_TYPE_MOVE_SEND) || + (msgt_name == MACH_MSG_TYPE_MOVE_SEND_ONCE)); + assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_NONE); + assert(entry->ie_object == IO_NULL); + assert(object != IO_DEAD); + + entry->ie_bits = ((bits &~ IE_BITS_RIGHT_MASK) | + MACH_PORT_TYPE_DEAD_NAME | 2); + } else if (IE_BITS_TYPE(bits) == MACH_PORT_TYPE_NONE) { + assert((msgt_name == MACH_MSG_TYPE_MOVE_SEND) || + (msgt_name == MACH_MSG_TYPE_MOVE_SEND_ONCE)); + assert(entry->ie_object == IO_NULL); + + entry->ie_bits = ((bits &~ IE_BITS_RIGHT_MASK) | + MACH_PORT_TYPE_DEAD_NAME | 1); + } else if (IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME) { + assert(entry->ie_object == IO_NULL); + assert(object == IO_DEAD); + assert(IE_BITS_UREFS(bits) > 0); + + if (msgt_name != MACH_MSG_TYPE_COPY_SEND) { + assert(IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX); + + entry->ie_bits = bits+1; /* increment urefs */ + } + } else { + assert((msgt_name == MACH_MSG_TYPE_MOVE_SEND) || + (msgt_name == MACH_MSG_TYPE_COPY_SEND)); + assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND); + assert(object != IO_DEAD); + assert(entry->ie_object == object); + assert(IE_BITS_UREFS(bits) > 0); + + if (msgt_name != MACH_MSG_TYPE_COPY_SEND) { + assert(IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX-1); + + entry->ie_bits = bits+1; /* increment urefs */ + } + + /* + * May as well convert the entry to a dead name. + * (Or if it is a compat entry, destroy it.) + */ + + (void) ipc_right_check(space, (ipc_port_t) object, + name, entry); + /* object is dead so it is not locked */ + } + + /* release the reference acquired by copyin */ + + if (object != IO_DEAD) + ipc_object_release(object); +} + +/* + * Routine: ipc_right_copyin_two + * Purpose: + * Like ipc_right_copyin with MACH_MSG_TYPE_MOVE_SEND + * and deadok == FALSE, except that this moves two + * send rights at once. + * Conditions: + * The space is write-locked and active. + * The object is returned with two refs/send rights. + * Returns: + * KERN_SUCCESS Acquired an object. + * KERN_INVALID_RIGHT Name doesn't denote correct right. + */ + +kern_return_t +ipc_right_copyin_two( + ipc_space_t space, + mach_port_name_t name, + ipc_entry_t entry, + ipc_object_t *objectp, + ipc_port_t *sorightp) +{ + ipc_entry_bits_t bits = entry->ie_bits; + mach_port_urefs_t urefs; + ipc_port_t port; + ipc_port_t dnrequest = IP_NULL; + + assert(space->is_active); + + if ((bits & MACH_PORT_TYPE_SEND) == 0) + goto invalid_right; + + urefs = IE_BITS_UREFS(bits); + if (urefs < 2) + goto invalid_right; + + port = (ipc_port_t) entry->ie_object; + assert(port != IP_NULL); + + if (ipc_right_check(space, port, name, entry)) { + goto invalid_right; + } + /* port is locked and active */ + + assert(port->ip_srights > 0); + + if (urefs == 2) { + if (bits & MACH_PORT_TYPE_RECEIVE) { + assert(port->ip_receiver_name == name); + assert(port->ip_receiver == space); + assert(IE_BITS_TYPE(bits) == + MACH_PORT_TYPE_SEND_RECEIVE); + + port->ip_srights++; + ip_reference(port); + ip_reference(port); + } else { + assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND); + + dnrequest = ipc_right_dncancel_macro(space, port, + name, entry); + + ipc_reverse_remove(space, (ipc_object_t) port); + + if (bits & IE_BITS_MAREQUEST) + ipc_marequest_cancel(space, name); + + port->ip_srights++; + ip_reference(port); + entry->ie_object = IO_NULL; + } + entry->ie_bits = bits &~ + (IE_BITS_UREFS_MASK|MACH_PORT_TYPE_SEND); + } else { + port->ip_srights += 2; + ip_reference(port); + ip_reference(port); + entry->ie_bits = bits-2; /* decrement urefs */ + } + ip_unlock(port); + + *objectp = (ipc_object_t) port; + *sorightp = dnrequest; + return KERN_SUCCESS; + + invalid_right: + return KERN_INVALID_RIGHT; +} + +/* + * Routine: ipc_right_copyout + * Purpose: + * Copyout a capability to a space. + * If successful, consumes a ref for the object. + * + * Always succeeds when given a newly-allocated entry, + * because user-reference overflow isn't a possibility. + * + * If copying out the object would cause the user-reference + * count in the entry to overflow, and overflow is TRUE, + * then instead the user-reference count is left pegged + * to its maximum value and the copyout succeeds anyway. + * Conditions: + * The space is write-locked and active. + * The object is locked and active. + * The object is unlocked; the space isn't. + * Returns: + * KERN_SUCCESS Copied out capability. + * KERN_UREFS_OVERFLOW User-refs would overflow; + * guaranteed not to happen with a fresh entry + * or if overflow=TRUE was specified. + */ + +kern_return_t +ipc_right_copyout( + ipc_space_t space, + mach_port_name_t name, + ipc_entry_t entry, + mach_msg_type_name_t msgt_name, + boolean_t overflow, + ipc_object_t object) +{ + ipc_entry_bits_t bits = entry->ie_bits; + ipc_port_t port; + + assert(IO_VALID(object)); + assert(io_otype(object) == IOT_PORT); + assert(io_active(object)); + assert(entry->ie_object == object); + + port = (ipc_port_t) object; + + switch (msgt_name) { + case MACH_MSG_TYPE_PORT_SEND_ONCE: + assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_NONE); + assert(port->ip_sorights > 0); + + /* transfer send-once right and ref to entry */ + ip_unlock(port); + + entry->ie_bits = bits | (MACH_PORT_TYPE_SEND_ONCE | 1); + break; + + case MACH_MSG_TYPE_PORT_SEND: + assert(port->ip_srights > 0); + + if (bits & MACH_PORT_TYPE_SEND) { + mach_port_urefs_t urefs = IE_BITS_UREFS(bits); + + assert(port->ip_srights > 1); + assert(urefs > 0); + assert(urefs < MACH_PORT_UREFS_MAX); + + if (urefs+1 == MACH_PORT_UREFS_MAX) { + if (overflow) { + /* leave urefs pegged to maximum */ + + port->ip_srights--; + ip_release(port); + ip_unlock(port); + return KERN_SUCCESS; + } + + ip_unlock(port); + return KERN_UREFS_OVERFLOW; + } + + port->ip_srights--; + ip_release(port); + ip_unlock(port); + } else if (bits & MACH_PORT_TYPE_RECEIVE) { + assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_RECEIVE); + assert(IE_BITS_UREFS(bits) == 0); + + /* transfer send right to entry */ + ip_release(port); + ip_unlock(port); + } else { + assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_NONE); + assert(IE_BITS_UREFS(bits) == 0); + + /* transfer send right and ref to entry */ + ip_unlock(port); + + /* entry is locked holding ref, so can use port */ + + entry->ie_name = name; + ipc_reverse_insert(space, (ipc_object_t) port, entry); + } + + entry->ie_bits = (bits | MACH_PORT_TYPE_SEND) + 1; + break; + + case MACH_MSG_TYPE_PORT_RECEIVE: { + ipc_port_t dest; + + assert(port->ip_mscount == 0); + assert(port->ip_receiver_name == MACH_PORT_NULL); + dest = port->ip_destination; + + port->ip_receiver_name = name; + port->ip_receiver = space; + + /* + * Clear the protected payload field to retain + * the behavior of mach_msg. + */ + ipc_port_flag_protected_payload_clear(port); + + assert((bits & MACH_PORT_TYPE_RECEIVE) == 0); + + if (bits & MACH_PORT_TYPE_SEND) { + assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND); + assert(IE_BITS_UREFS(bits) > 0); + assert(port->ip_srights > 0); + + ip_release(port); + ip_unlock(port); + + /* entry is locked holding ref, so can use port */ + + ipc_reverse_remove(space, (ipc_object_t) port); + } else { + assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_NONE); + assert(IE_BITS_UREFS(bits) == 0); + + /* transfer ref to entry */ + ip_unlock(port); + } + + entry->ie_bits = bits | MACH_PORT_TYPE_RECEIVE; + + if (dest != IP_NULL) + ipc_port_release(dest); + break; + } + + default: +#if MACH_ASSERT + assert(!"ipc_right_copyout: strange rights"); +#else + panic("ipc_right_copyout: strange rights"); +#endif + } + + return KERN_SUCCESS; +} + +/* + * Routine: ipc_right_rename + * Purpose: + * Transfer an entry from one name to another. + * The old entry is deallocated. + * Conditions: + * The space is write-locked and active. + * The new entry is unused. Upon return, + * the space is unlocked. + * Returns: + * KERN_SUCCESS Moved entry to new name. + */ + +kern_return_t +ipc_right_rename( + ipc_space_t space, + mach_port_name_t oname, + ipc_entry_t oentry, + mach_port_name_t nname, + ipc_entry_t nentry) +{ + ipc_entry_bits_t bits = oentry->ie_bits; + ipc_port_request_index_t request = oentry->ie_request; + ipc_object_t object = oentry->ie_object; + + assert(space->is_active); + assert(oname != nname); + + /* + * If IE_BITS_COMPAT, we can't allow the entry to be renamed + * if the port is dead. (This would foil ipc_port_destroy.) + * Instead we should fail because oentry shouldn't exist. + * Note IE_BITS_COMPAT implies ie_request != 0. + */ + + if (request != 0) { + ipc_port_t port; + + assert(bits & MACH_PORT_TYPE_PORT_RIGHTS); + port = (ipc_port_t) object; + assert(port != IP_NULL); + + if (ipc_right_check(space, port, oname, oentry)) { + bits = oentry->ie_bits; + assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME); + assert(oentry->ie_request == 0); + request = 0; + assert(oentry->ie_object == IO_NULL); + object = IO_NULL; + } else { + /* port is locked and active */ + + ipc_port_dnrename(port, request, oname, nname); + ip_unlock(port); + oentry->ie_request = 0; + } + } + + if (bits & IE_BITS_MAREQUEST) { + assert(bits & MACH_PORT_TYPE_SEND_RECEIVE); + + ipc_marequest_rename(space, oname, nname); + } + + /* initialize nentry before letting ipc_reverse_insert see it */ + + assert((nentry->ie_bits & IE_BITS_RIGHT_MASK) == 0); + nentry->ie_bits |= bits & IE_BITS_RIGHT_MASK; + nentry->ie_request = request; + nentry->ie_object = object; + + switch (IE_BITS_TYPE(bits)) { + case MACH_PORT_TYPE_SEND: { + ipc_port_t port; + + port = (ipc_port_t) object; + assert(port != IP_NULL); + + ipc_reverse_remove(space, (ipc_object_t) port); + nentry->ie_name = nname; + ipc_reverse_insert(space, (ipc_object_t) port, nentry); + break; + } + + case MACH_PORT_TYPE_RECEIVE: + case MACH_PORT_TYPE_SEND_RECEIVE: { + ipc_port_t port; + + port = (ipc_port_t) object; + assert(port != IP_NULL); + + ip_lock(port); + assert(ip_active(port)); + assert(port->ip_receiver_name == oname); + assert(port->ip_receiver == space); + + port->ip_receiver_name = nname; + ip_unlock(port); + break; + } + + case MACH_PORT_TYPE_PORT_SET: { + ipc_pset_t pset; + + pset = (ipc_pset_t) object; + assert(pset != IPS_NULL); + + ips_lock(pset); + assert(ips_active(pset)); + assert(pset->ips_local_name == oname); + + pset->ips_local_name = nname; + ips_unlock(pset); + break; + } + + case MACH_PORT_TYPE_SEND_ONCE: + case MACH_PORT_TYPE_DEAD_NAME: + break; + + default: +#if MACH_ASSERT + assert(!"ipc_right_rename: strange rights"); +#else + panic("ipc_right_rename: strange rights"); +#endif + } + + assert(oentry->ie_request == 0); + oentry->ie_object = IO_NULL; + ipc_entry_dealloc(space, oname, oentry); + is_write_unlock(space); + + return KERN_SUCCESS; +} diff --git a/ipc/ipc_right.h b/ipc/ipc_right.h new file mode 100644 index 0000000..6802abb --- /dev/null +++ b/ipc/ipc_right.h @@ -0,0 +1,112 @@ +/* + * 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. + */ +/* + */ +/* + * File: ipc/ipc_right.h + * Author: Rich Draves + * Date: 1989 + * + * Declarations of functions to manipulate IPC capabilities. + */ + +#ifndef _IPC_IPC_RIGHT_H_ +#define _IPC_IPC_RIGHT_H_ + +#include +#include +#include +#include + +#define ipc_right_lookup_read ipc_right_lookup_write + +extern kern_return_t +ipc_right_lookup_write(ipc_space_t, mach_port_name_t, ipc_entry_t *); + +extern boolean_t +ipc_right_reverse(ipc_space_t, ipc_object_t, + mach_port_name_t *, ipc_entry_t *); + +extern kern_return_t +ipc_right_dnrequest(ipc_space_t, mach_port_name_t, boolean_t, + ipc_port_t, ipc_port_t *); + +extern ipc_port_t +ipc_right_dncancel(ipc_space_t, ipc_port_t, mach_port_name_t, ipc_entry_t); + +#define ipc_right_dncancel_macro(space, port, name, entry) \ + (((entry)->ie_request == 0) ? IP_NULL : \ + ipc_right_dncancel((space), (port), (name), (entry))) + +extern boolean_t +ipc_right_inuse(ipc_space_t, mach_port_name_t, ipc_entry_t); + +extern boolean_t +ipc_right_check(ipc_space_t, ipc_port_t, mach_port_name_t, ipc_entry_t); + +extern void +ipc_right_clean(ipc_space_t, mach_port_name_t, ipc_entry_t); + +extern kern_return_t +ipc_right_destroy(ipc_space_t, mach_port_name_t, ipc_entry_t); + +extern kern_return_t +ipc_right_dealloc(ipc_space_t, mach_port_name_t, ipc_entry_t); + +extern kern_return_t +ipc_right_delta(ipc_space_t, mach_port_name_t, ipc_entry_t, + mach_port_right_t, mach_port_delta_t); + +extern kern_return_t +ipc_right_info(ipc_space_t, mach_port_name_t, ipc_entry_t, + mach_port_type_t *, mach_port_urefs_t *); + +extern boolean_t +ipc_right_copyin_check(ipc_space_t, mach_port_name_t, ipc_entry_t, + mach_msg_type_name_t); + +extern kern_return_t +ipc_right_copyin(ipc_space_t, mach_port_name_t, ipc_entry_t, + mach_msg_type_name_t, boolean_t, + ipc_object_t *, ipc_port_t *); + +extern void +ipc_right_copyin_undo(ipc_space_t, mach_port_name_t, ipc_entry_t, + mach_msg_type_name_t, ipc_object_t, ipc_port_t); + +extern kern_return_t +ipc_right_copyin_two(ipc_space_t, mach_port_name_t, ipc_entry_t, + ipc_object_t *, ipc_port_t *); + +extern kern_return_t +ipc_right_copyout(ipc_space_t, mach_port_name_t, ipc_entry_t, + mach_msg_type_name_t, boolean_t, ipc_object_t); + +extern kern_return_t +ipc_right_rename(ipc_space_t, mach_port_name_t, ipc_entry_t, + mach_port_name_t, ipc_entry_t); + +#endif /* _IPC_IPC_RIGHT_H_ */ diff --git a/ipc/ipc_space.c b/ipc/ipc_space.c new file mode 100644 index 0000000..77040d1 --- /dev/null +++ b/ipc/ipc_space.c @@ -0,0 +1,215 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University. + * Copyright (c) 1993,1994 The University of Utah and + * the Computer Systems Laboratory (CSL). + * 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, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF + * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM 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. + */ +/* + */ +/* + * File: ipc/ipc_space.c + * Author: Rich Draves + * Date: 1989 + * + * Functions to manipulate IPC capability spaces. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +struct kmem_cache ipc_space_cache; +ipc_space_t ipc_space_kernel; +ipc_space_t ipc_space_reply; + +/* + * Routine: ipc_space_reference + * Routine: ipc_space_release + * Purpose: + * Function versions of the IPC space macros. + * The "is_" cover macros can be defined to use the + * macros or the functions, as desired. + */ + +void +ipc_space_reference( + ipc_space_t space) +{ + ipc_space_reference_macro(space); +} + +void +ipc_space_release( + ipc_space_t space) +{ + ipc_space_release_macro(space); +} + +/* A place-holder object for the zeroth entry. */ +struct ipc_entry zero_entry; + +/* + * Routine: ipc_space_create + * Purpose: + * Creates a new IPC space. + * + * The new space has two references, one for the caller + * and one because it is active. + * Conditions: + * Nothing locked. Allocates memory. + * Returns: + * KERN_SUCCESS Created a space. + * KERN_RESOURCE_SHORTAGE Couldn't allocate memory. + */ + +kern_return_t +ipc_space_create( + ipc_space_t *spacep) +{ + ipc_space_t space; + + space = is_alloc(); + if (space == IS_NULL) + return KERN_RESOURCE_SHORTAGE; + + is_ref_lock_init(space); + space->is_references = 2; + + is_lock_init(space); + space->is_active = TRUE; + + rdxtree_init(&space->is_map); + rdxtree_init(&space->is_reverse_map); + /* The zeroth entry is reserved. */ + rdxtree_insert(&space->is_map, 0, &zero_entry); + space->is_size = 1; + space->is_free_list = NULL; + space->is_free_list_size = 0; + + *spacep = space; + return KERN_SUCCESS; +} + +/* + * Routine: ipc_space_create_special + * Purpose: + * Create a special space. A special space + * doesn't hold rights in the normal way. + * Instead it is place-holder for holding + * disembodied (naked) receive rights. + * See ipc_port_alloc_special/ipc_port_dealloc_special. + * Conditions: + * Nothing locked. + * Returns: + * KERN_SUCCESS Created a space. + * KERN_RESOURCE_SHORTAGE Couldn't allocate memory. + */ + +kern_return_t +ipc_space_create_special( + ipc_space_t *spacep) +{ + ipc_space_t space; + + space = is_alloc(); + if (space == IS_NULL) + return KERN_RESOURCE_SHORTAGE; + + is_ref_lock_init(space); + space->is_references = 1; + + is_lock_init(space); + space->is_active = FALSE; + + *spacep = space; + return KERN_SUCCESS; +} + +/* + * Routine: ipc_space_destroy + * Purpose: + * Marks the space as dead and cleans up the entries. + * Does nothing if the space is already dead. + * Conditions: + * Nothing locked. + */ + +void +ipc_space_destroy( + ipc_space_t space) +{ + boolean_t active; + + assert(space != IS_NULL); + + is_write_lock(space); + active = space->is_active; + space->is_active = FALSE; + is_write_unlock(space); + + if (!active) + return; + + ipc_entry_t entry; + struct rdxtree_iter iter; + rdxtree_for_each(&space->is_map, &iter, entry) { + if (entry->ie_name == MACH_PORT_NULL) + continue; + + mach_port_type_t type = IE_BITS_TYPE(entry->ie_bits); + + if (type != MACH_PORT_TYPE_NONE) { + mach_port_name_t name = + MACH_PORT_MAKEB(entry->ie_name, entry->ie_bits); + + ipc_right_clean(space, name, entry); + } + + ie_free(entry); + } + rdxtree_remove_all(&space->is_map); + rdxtree_remove_all(&space->is_reverse_map); + + /* + * Because the space is now dead, + * we must release the "active" reference for it. + * Our caller still has his reference. + */ + + is_release(space); +} diff --git a/ipc/ipc_space.h b/ipc/ipc_space.h new file mode 100644 index 0000000..96d5894 --- /dev/null +++ b/ipc/ipc_space.h @@ -0,0 +1,324 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University. + * Copyright (c) 1993,1994 The University of Utah and + * the Computer Systems Laboratory (CSL). + * 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, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF + * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM 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. + */ +/* + */ +/* + * File: ipc/ipc_space.h + * Author: Rich Draves + * Date: 1989 + * + * Definitions for IPC spaces of capabilities. + */ + +#ifndef _IPC_IPC_SPACE_H_ +#define _IPC_IPC_SPACE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Every task has a space of IPC capabilities. + * IPC operations like send and receive use this space. + * IPC kernel calls manipulate the space of the target task. + */ + +typedef unsigned int ipc_space_refs_t; + +struct ipc_space { + decl_simple_lock_data(,is_ref_lock_data) + ipc_space_refs_t is_references; + + struct lock is_lock_data; + boolean_t is_active; /* is the space alive? */ + struct rdxtree is_map; /* a map of entries */ + size_t is_size; /* number of entries */ + struct rdxtree is_reverse_map; /* maps objects to entries */ + ipc_entry_t is_free_list; /* a linked list of free entries */ + size_t is_free_list_size; /* number of free entries */ +#define IS_FREE_LIST_SIZE_LIMIT 64 /* maximum number of entries + in the free list */ +}; + + +#define IS_NULL ((ipc_space_t) 0) + +extern struct kmem_cache ipc_space_cache; + +#define is_alloc() ((ipc_space_t) kmem_cache_alloc(&ipc_space_cache)) +#define is_free(is) kmem_cache_free(&ipc_space_cache, (vm_offset_t) (is)) + +extern struct ipc_space *ipc_space_kernel; +extern struct ipc_space *ipc_space_reply; + +#define is_ref_lock_init(is) simple_lock_init(&(is)->is_ref_lock_data) + +#define ipc_space_reference_macro(is) \ +MACRO_BEGIN \ + simple_lock(&(is)->is_ref_lock_data); \ + assert((is)->is_references > 0); \ + (is)->is_references++; \ + simple_unlock(&(is)->is_ref_lock_data); \ +MACRO_END + +#define ipc_space_release_macro(is) \ +MACRO_BEGIN \ + ipc_space_refs_t _refs; \ + \ + simple_lock(&(is)->is_ref_lock_data); \ + assert((is)->is_references > 0); \ + _refs = --(is)->is_references; \ + simple_unlock(&(is)->is_ref_lock_data); \ + \ + if (_refs == 0) \ + is_free(is); \ +MACRO_END + +#define is_lock_init(is) lock_init(&(is)->is_lock_data, TRUE) + +#define is_read_lock(is) lock_read(&(is)->is_lock_data) +#define is_read_unlock(is) lock_done(&(is)->is_lock_data) + +#define is_write_lock(is) lock_write(&(is)->is_lock_data) +#define is_write_lock_try(is) lock_try_write(&(is)->is_lock_data) +#define is_write_unlock(is) lock_done(&(is)->is_lock_data) + +#define is_write_to_read_lock(is) lock_write_to_read(&(is)->is_lock_data) + +extern void ipc_space_reference(struct ipc_space *space); +extern void ipc_space_release(struct ipc_space *space); + +#define is_reference(is) ipc_space_reference_macro(is) +#define is_release(is) ipc_space_release_macro(is) + +kern_return_t ipc_space_create(ipc_space_t *); +kern_return_t ipc_space_create_special(struct ipc_space **); +void ipc_space_destroy(struct ipc_space *); + +/* IPC entry lookups. */ + +/* + * Routine: ipc_entry_lookup + * Purpose: + * Searches for an entry, given its name. + * Conditions: + * The space must be read or write locked throughout. + * The space must be active. + */ + +static inline ipc_entry_t +ipc_entry_lookup( + ipc_space_t space, + mach_port_name_t name) +{ + ipc_entry_t entry; + + assert(space->is_active); + entry = rdxtree_lookup(&space->is_map, (rdxtree_key_t) name); + if (entry != IE_NULL + && IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_NONE) + entry = NULL; + assert((entry == IE_NULL) || IE_BITS_TYPE(entry->ie_bits)); + return entry; +} + +extern volatile boolean_t mach_port_deallocate_debug; + +static inline void +ipc_entry_lookup_failed(mach_msg_header_t *msg, mach_port_name_t name) +{ + if (name == MACH_PORT_NAME_NULL || name == MACH_PORT_NAME_DEAD) + return; + printf("task %.*s looked up a bogus port %lu for %d, most probably a bug.\n", (int) sizeof current_task()->name, current_task()->name, (unsigned long) name, msg->msgh_id); + if (mach_port_deallocate_debug) + SoftDebugger("ipc_entry_lookup"); +} + +/* + * Routine: ipc_entry_get + * Purpose: + * Tries to allocate an entry out of the space. + * Conditions: + * The space is write-locked and active throughout. + * An object may be locked. Will not allocate memory. + * Returns: + * KERN_SUCCESS A free entry was found. + * KERN_NO_SPACE No entry allocated. + */ + +static inline kern_return_t +ipc_entry_get( + ipc_space_t space, + mach_port_name_t *namep, + ipc_entry_t *entryp) +{ + mach_port_name_t new_name; + ipc_entry_t free_entry; + + assert(space->is_active); + + /* Get entry from the free list. */ + free_entry = space->is_free_list; + if (free_entry == IE_NULL) + return KERN_NO_SPACE; + + space->is_free_list = free_entry->ie_next_free; + space->is_free_list_size -= 1; + + /* + * Initialize the new entry. We need only + * increment the generation number and clear ie_request. + */ + + { + mach_port_gen_t gen; + + assert((free_entry->ie_bits &~ IE_BITS_GEN_MASK) == 0); + gen = free_entry->ie_bits + IE_BITS_GEN_ONE; + free_entry->ie_bits = gen; + free_entry->ie_request = 0; + new_name = MACH_PORT_MAKE(free_entry->ie_name, gen); + } + + /* + * The new name can't be MACH_PORT_NULL because index + * is non-zero. It can't be MACH_PORT_DEAD because + * the table isn't allowed to grow big enough. + * (See comment in ipc/ipc_table.h.) + */ + + assert(MACH_PORT_NAME_VALID(new_name)); + assert(free_entry->ie_object == IO_NULL); + + space->is_size += 1; + *namep = new_name; + *entryp = free_entry; + return KERN_SUCCESS; +} + +/* + * Routine: ipc_entry_dealloc + * Purpose: + * Deallocates an entry from a space. + * Conditions: + * The space must be write-locked throughout. + * The space must be active. + */ + +static inline void +ipc_entry_dealloc( + ipc_space_t space, + mach_port_name_t name, + ipc_entry_t entry) +{ + assert(space->is_active); + assert(entry->ie_object == IO_NULL); + assert(entry->ie_request == 0); + + if (space->is_free_list_size < IS_FREE_LIST_SIZE_LIMIT) { + space->is_free_list_size += 1; + entry->ie_bits &= IE_BITS_GEN_MASK; + entry->ie_next_free = space->is_free_list; + space->is_free_list = entry; + } else { + rdxtree_remove(&space->is_map, (rdxtree_key_t) name); + ie_free(entry); + } + space->is_size -= 1; +} + +/* Reverse lookups. */ + +/* Cast a pointer to a suitable key. */ +#define KEY(X) \ + ({ \ + assert((((unsigned long) (X)) & 0x07) == 0); \ + ((unsigned long long) \ + (((unsigned long) (X) - VM_MIN_KERNEL_ADDRESS) >> 3)); \ + }) + +/* Insert (OBJ, ENTRY) pair into the reverse mapping. SPACE must + be write-locked. */ +static inline kern_return_t +ipc_reverse_insert(ipc_space_t space, + ipc_object_t obj, + ipc_entry_t entry) +{ + assert(space != IS_NULL); + assert(obj != IO_NULL); + return (kern_return_t) rdxtree_insert(&space->is_reverse_map, + KEY(obj), entry); +} + +/* Remove OBJ from the reverse mapping. SPACE must be + write-locked. */ +static inline ipc_entry_t +ipc_reverse_remove(ipc_space_t space, + ipc_object_t obj) +{ + assert(space != IS_NULL); + assert(obj != IO_NULL); + return rdxtree_remove(&space->is_reverse_map, KEY(obj)); +} + +/* Remove all entries from the reverse mapping. SPACE must be + write-locked. */ +static inline void +ipc_reverse_remove_all(ipc_space_t space) +{ + assert(space != IS_NULL); + rdxtree_remove_all(&space->is_reverse_map); + assert(space->is_reverse_map.height == 0); + assert(space->is_reverse_map.root == NULL); +} + +/* Return ENTRY related to OBJ, or NULL if no such entry is found in + the reverse mapping. SPACE must be read-locked or + write-locked. */ +static inline ipc_entry_t +ipc_reverse_lookup(ipc_space_t space, + ipc_object_t obj) +{ + assert(space != IS_NULL); + assert(obj != IO_NULL); + return rdxtree_lookup(&space->is_reverse_map, KEY(obj)); +} + +#undef KEY + +#endif /* _IPC_IPC_SPACE_H_ */ diff --git a/ipc/ipc_table.c b/ipc/ipc_table.c new file mode 100644 index 0000000..0f8592a --- /dev/null +++ b/ipc/ipc_table.c @@ -0,0 +1,135 @@ +/* + * 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. + */ +/* + */ +/* + * File: ipc/ipc_table.c + * Author: Rich Draves + * Date: 1989 + * + * Functions to manipulate tables of IPC capabilities. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +ipc_table_size_t ipc_table_dnrequests; +const unsigned int ipc_table_dnrequests_size = 64; + +void +ipc_table_fill( + ipc_table_size_t its, /* array to fill */ + unsigned int num, /* size of array */ + unsigned int min, /* at least this many elements */ + vm_size_t elemsize) /* size of elements */ +{ + unsigned int index; + vm_size_t minsize = min * elemsize; + vm_size_t size; + vm_size_t incrsize; + + /* first use powers of two, up to the page size */ + + for (index = 0, size = 1; + (index < num) && (size < PAGE_SIZE); + size <<= 1) { + if (size >= minsize) { + its[index].its_size = size / elemsize; + index++; + } + } + + /* then increments of a page, then two pages, etc. */ + + for (incrsize = PAGE_SIZE; index < num;) { + unsigned int period; + + for (period = 0; + (period < 15) && (index < num); + period++, size += incrsize) { + if (size >= minsize) { + its[index].its_size = size / elemsize; + index++; + } + } + if (incrsize < (PAGE_SIZE << 3)) + incrsize <<= 1; + } +} + +void +ipc_table_init(void) +{ + ipc_table_dnrequests = (ipc_table_size_t) + kalloc(sizeof(struct ipc_table_size) * + ipc_table_dnrequests_size); + assert(ipc_table_dnrequests != ITS_NULL); + + ipc_table_fill(ipc_table_dnrequests, ipc_table_dnrequests_size - 1, + 2, sizeof(struct ipc_port_request)); + + /* the last element should have zero size */ + + ipc_table_dnrequests[ipc_table_dnrequests_size - 1].its_size = 0; +} + +/* + * Routine: ipc_table_alloc + * Purpose: + * Allocate a table. + * Conditions: + * May block. + */ + +vm_offset_t +ipc_table_alloc( + vm_size_t size) +{ + return kalloc(size); +} + +/* + * Routine: ipc_table_free + * Purpose: + * Free a table allocated with ipc_table_alloc or + * ipc_table_realloc. + * Conditions: + * May block. + */ + +void +ipc_table_free( + vm_size_t size, + vm_offset_t table) +{ + kfree(table, size); +} diff --git a/ipc/ipc_table.h b/ipc/ipc_table.h new file mode 100644 index 0000000..7968e6b --- /dev/null +++ b/ipc/ipc_table.h @@ -0,0 +1,101 @@ +/* + * 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. + */ +/* + */ +/* + * File: ipc/ipc_table.h + * Author: Rich Draves + * Date: 1989 + * + * Definitions for tables, used for dead-name requests + * (ipc_port_request_t). + */ + +#ifndef _IPC_IPC_TABLE_H_ +#define _IPC_IPC_TABLE_H_ + +#include +#include + +/* + * Every its_size value must must be a power of two. + * + * The ipr_size field of the first element in a table of + * dead-name requests (ipc_port_request_t) points to the + * ipc_table_size structure. The structures must be elements + * of ipc_table_dnrequests. ipc_table_dnrequests must end + * with an element with zero its_size, and except for this last + * element, the its_size values must be strictly increasing. + * + * The ipr_size field points to the currently used ipc_table_size. + */ + +typedef unsigned int ipc_table_index_t; /* index into tables */ +typedef unsigned int ipc_table_elems_t; /* size of tables */ + +typedef struct ipc_table_size { + ipc_table_elems_t its_size; /* number of elements in table */ +} *ipc_table_size_t; + +#define ITS_NULL ((ipc_table_size_t) 0) + +extern ipc_table_size_t ipc_table_dnrequests; + +extern void +ipc_table_init(void); + +/* + * Note that ipc_table_alloc, and ipc_table_free all potentially + * use the VM system. Hence simple locks can't be held across + * them. + */ + +/* Allocate a table */ +extern vm_offset_t ipc_table_alloc( + vm_size_t size); + +/* Free a table */ +extern void ipc_table_free( + vm_size_t size, + vm_offset_t table); + +void ipc_table_fill( + ipc_table_size_t its, + unsigned int num, + unsigned int min, + vm_size_t elemsize); + +#define it_dnrequests_alloc(its) \ + ((ipc_port_request_t) \ + ipc_table_alloc((its)->its_size * \ + sizeof(struct ipc_port_request))) + +#define it_dnrequests_free(its, table) \ + ipc_table_free((its)->its_size * \ + sizeof(struct ipc_port_request), \ + (vm_offset_t)(table)) + +#endif /* _IPC_IPC_TABLE_H_ */ diff --git a/ipc/ipc_target.c b/ipc/ipc_target.c new file mode 100644 index 0000000..94c5d40 --- /dev/null +++ b/ipc/ipc_target.c @@ -0,0 +1,78 @@ +/* + * Copyright (c) 1994 The University of Utah and + * the Computer Systems Laboratory (CSL). 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. + * + * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS + * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF + * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * CSL requests users of this software to return to csl-dist@cs.utah.edu any + * improvements that they make and grant CSL redistribution rights. + */ +/* + * File: ipc_target.c + * + * Implementation for common part of IPC ports and port sets + * representing a target of messages and migrating RPCs. + */ + +#include +#include "ipc_target.h" + +void +ipc_target_init(struct ipc_target *ipt, mach_port_name_t name) +{ + ipt->ipt_name = name; + ipc_mqueue_init(&ipt->ipt_messages); + +#ifdef MIGRATING_THREADS + ipt->ipt_type = IPT_TYPE_MESSAGE_RPC; + ipt->ipt_acts = 0; + + ipc_target_machine_init(ipt); +#endif +} + +void +ipc_target_terminate(struct ipc_target *ipt) +{ +} + +#ifdef MIGRATING_THREADS +struct Act * +ipc_target_block(struct ipc_target *ipt) +{ + struct Act *act; + + ipt_lock(ipt); + while ((act = ipt->ipt_acts) == 0) { + /* XXX mp unsafe */ + ipt->ipt_waiting = 1; + ipt_unlock(ipt); + thread_wait((int)&ipt->ipt_acts, FALSE); + ipt_lock(ipt); + } + ipt->ipt_acts = act->ipt_next; + ipt_unlock(ipt); + + return act; +} + +void +ipc_target_wakeup(struct ipc_target *ipt) +{ + ipt_lock(ipt); + if (ipt->ipt_waiting) { + thread_wakeup((int)&ipt->ipt_acts); + ipt->ipt_waiting = 0; + } + ipt_unlock(ipt); +} +#endif /* MIGRATING_THREADS */ + diff --git a/ipc/ipc_target.h b/ipc/ipc_target.h new file mode 100644 index 0000000..c2cc924 --- /dev/null +++ b/ipc/ipc_target.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 1994 The University of Utah and + * the Computer Systems Laboratory (CSL). 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. + * + * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS + * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF + * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * CSL requests users of this software to return to csl-dist@cs.utah.edu any + * improvements that they make and grant CSL redistribution rights. + */ +/* + * File: ipc_target.h + * + * Common part of IPC ports and port sets + * representing a target of messages and migrating RPCs. + */ + +#ifndef _IPC_IPC_RECEIVER_H_ +#define _IPC_IPC_RECEIVER_H_ + +#include "ipc_mqueue.h" +#include "ipc_object.h" + +typedef struct ipc_target { + + struct ipc_object ipt_object; + + mach_port_name_t ipt_name; + struct ipc_mqueue ipt_messages; + +#ifdef MIGRATING_THREADS + /*** Migrating RPC stuff ***/ + + int ipt_type; + + /* User entry info for migrating RPC */ + rpc_info_t ipt_rpcinfo; + + /* List of available activations, all active but not in use. */ + struct Act *ipt_acts; + + /* TRUE if someone is waiting for an activation from this pool. */ + int ipt_waiting; +#endif /* MIGRATING_THREADS */ + +} *ipc_target_t; + +#define IPT_TYPE_MESSAGE_RPC 1 +#define IPT_TYPE_MIGRATE_RPC 2 + +void ipc_target_init(struct ipc_target *ipt, mach_port_name_t name); +void ipc_target_terminate(struct ipc_target *ipt); + +#define ipt_lock(ipt) io_lock(&(ipt)->ipt_object) +#define ipt_unlock(ipt) io_unlock(&(ipt)->ipt_object) +#define ipt_reference(ipt) io_reference(&(ipt)->ipt_object) +#define ipt_release(ipt) io_release(&(ipt)->ipt_object) +#define ipt_check_unlock(ipt) io_check_unlock(&(ipt)->ipt_object) + +#endif /* _IPC_IPC_RECEIVER_H_ */ diff --git a/ipc/ipc_thread.c b/ipc/ipc_thread.c new file mode 100644 index 0000000..1e738a5 --- /dev/null +++ b/ipc/ipc_thread.c @@ -0,0 +1,107 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University. + * Copyright (c) 1993,1994 The University of Utah and + * the Computer Systems Laboratory (CSL). + * 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, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF + * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM 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. + */ +/* + */ +/* + * File: ipc/ipc_thread.c + * Author: Rich Draves + * Date: 1989 + * + * IPC operations on threads. + */ + +#include +#include + +/* + * Routine: ipc_thread_enqueue + * Purpose: + * Enqueue a thread. + */ + +void +ipc_thread_enqueue( + ipc_thread_queue_t queue, + ipc_thread_t thread) +{ + ipc_thread_enqueue_macro(queue, thread); +} + +/* + * Routine: ipc_thread_dequeue + * Purpose: + * Dequeue and return a thread. + */ + +ipc_thread_t +ipc_thread_dequeue( + ipc_thread_queue_t queue) +{ + ipc_thread_t first; + + first = ipc_thread_queue_first(queue); + + if (first != ITH_NULL) + ipc_thread_rmqueue_first_macro(queue, first); + + return first; +} + +/* + * Routine: ipc_thread_rmqueue + * Purpose: + * Pull a thread out of a queue. + */ + +void +ipc_thread_rmqueue( + ipc_thread_queue_t queue, + ipc_thread_t thread) +{ + ipc_thread_t next, prev; + + assert(queue->ithq_base != ITH_NULL); + + next = thread->ith_next; + prev = thread->ith_prev; + + if (next == thread) { + assert(prev == thread); + assert(queue->ithq_base == thread); + + queue->ithq_base = ITH_NULL; + } else { + if (queue->ithq_base == thread) + queue->ithq_base = next; + + next->ith_prev = prev; + prev->ith_next = next; + ipc_thread_links_init(thread); + } +} diff --git a/ipc/ipc_thread.h b/ipc/ipc_thread.h new file mode 100644 index 0000000..008ab4a --- /dev/null +++ b/ipc/ipc_thread.h @@ -0,0 +1,129 @@ +/* + * 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. + */ +/* + */ +/* + * File: ipc/ipc_thread.h + * Author: Rich Draves + * Date: 1989 + * + * Definitions for the IPC component of threads. + */ + +#ifndef _IPC_IPC_THREAD_H_ +#define _IPC_IPC_THREAD_H_ + +#include + +typedef thread_t ipc_thread_t; + +#define ITH_NULL THREAD_NULL + +#define ith_lock_init(thread) simple_lock_init(&(thread)->ith_lock_data) +#define ith_lock(thread) simple_lock(&(thread)->ith_lock_data) +#define ith_unlock(thread) simple_unlock(&(thread)->ith_lock_data) + +/* + * Note that this isn't a queue, but rather a stack. This causes + * threads that were recently running to be reused earlier, which + * helps improve locality of reference. + */ +typedef struct ipc_thread_queue { + ipc_thread_t ithq_base; +} *ipc_thread_queue_t; + +#define ITHQ_NULL ((ipc_thread_queue_t) 0) + + +#define ipc_thread_links_init(thread) \ +MACRO_BEGIN \ + (thread)->ith_next = (thread); \ + (thread)->ith_prev = (thread); \ +MACRO_END + +#define ipc_thread_queue_init(queue) \ +MACRO_BEGIN \ + (queue)->ithq_base = ITH_NULL; \ +MACRO_END + +#define ipc_thread_queue_empty(queue) ((queue)->ithq_base == ITH_NULL) + +#define ipc_thread_queue_first(queue) ((queue)->ithq_base) + +#define ipc_thread_rmqueue_first_macro(queue, thread) \ +MACRO_BEGIN \ + ipc_thread_t _next; \ + \ + assert((queue)->ithq_base == (thread)); \ + \ + _next = (thread)->ith_next; \ + if (_next == (thread)) { \ + assert((thread)->ith_prev == (thread)); \ + (queue)->ithq_base = ITH_NULL; \ + } else { \ + ipc_thread_t _prev = (thread)->ith_prev; \ + \ + (queue)->ithq_base = _next; \ + _next->ith_prev = _prev; \ + _prev->ith_next = _next; \ + ipc_thread_links_init(thread); \ + } \ +MACRO_END + +#define ipc_thread_enqueue_macro(queue, thread) \ +MACRO_BEGIN \ + ipc_thread_t _first = (queue)->ithq_base; \ + \ + if (_first == ITH_NULL) { \ + (queue)->ithq_base = (thread); \ + assert((thread)->ith_next == (thread)); \ + assert((thread)->ith_prev == (thread)); \ + } else { \ + ipc_thread_t _last = _first->ith_prev; \ + \ + (thread)->ith_next = _first; \ + (thread)->ith_prev = _last; \ + _first->ith_prev = (thread); \ + _last->ith_next = (thread); \ + (queue)->ithq_base = (thread); \ + } \ +MACRO_END + +/* Enqueue a thread on a message queue */ +extern void ipc_thread_enqueue( + ipc_thread_queue_t queue, + ipc_thread_t thread); + +/* Dequeue a thread from a message queue */ +extern ipc_thread_t ipc_thread_dequeue( + ipc_thread_queue_t queue); + +/* Remove a thread from a message queue */ +extern void ipc_thread_rmqueue( + ipc_thread_queue_t queue, + ipc_thread_t thread); + +#endif /* _IPC_IPC_THREAD_H_ */ diff --git a/ipc/ipc_types.h b/ipc/ipc_types.h new file mode 100644 index 0000000..c8f0d0b --- /dev/null +++ b/ipc/ipc_types.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 1995, 1994, 1993, 1992, 1991, 1990 + * Open Software Foundation, Inc. + * + * 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 + * FOR A PARTICULAR PURPOSE. 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 + */ +/* + * OSF Research Institute MK6.1 (unencumbered) 1/31/1995 + */ + +#ifndef _IPC_TYPES_H_ +#define _IPC_TYPES_H_ + +typedef struct ipc_space *ipc_space_t; +typedef struct ipc_port *ipc_port_t; + +#endif /* _IPC_TYPES_H_ */ diff --git a/ipc/mach_debug.c b/ipc/mach_debug.c new file mode 100644 index 0000000..7dca4b6 --- /dev/null +++ b/ipc/mach_debug.c @@ -0,0 +1,288 @@ +/* + * 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. + */ +/* + */ +/* + * File: ipc/mach_debug.c + * Author: Rich Draves + * Date: 1989 + * + * Exported kernel calls. See mach_debug/mach_debug.defs. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +/* + * Routine: mach_port_get_srights [kernel call] + * Purpose: + * Retrieve the number of extant send rights + * that a receive right has. + * Conditions: + * Nothing locked. + * Returns: + * KERN_SUCCESS Retrieved number of send rights. + * KERN_INVALID_TASK The space is null. + * KERN_INVALID_TASK The space is dead. + * KERN_INVALID_NAME The name doesn't denote a right. + * KERN_INVALID_RIGHT Name doesn't denote receive rights. + */ + +kern_return_t +mach_port_get_srights( + ipc_space_t space, + mach_port_name_t name, + mach_port_rights_t *srightsp) +{ + ipc_port_t port; + kern_return_t kr; + mach_port_rights_t srights; + + if (space == IS_NULL) + return KERN_INVALID_TASK; + + kr = ipc_port_translate_receive(space, name, &port); + if (kr != KERN_SUCCESS) + return kr; + /* port is locked and active */ + + srights = port->ip_srights; + ip_unlock(port); + + *srightsp = srights; + return KERN_SUCCESS; +} + +/* + * Routine: host_ipc_marequest_info + * Purpose: + * Return information about the marequest hash table. + * Conditions: + * Nothing locked. Obeys CountInOut protocol. + * Returns: + * KERN_SUCCESS Returned information. + * KERN_INVALID_HOST The host is null. + * KERN_RESOURCE_SHORTAGE Couldn't allocate memory. + */ + +kern_return_t +host_ipc_marequest_info( + host_t host, + unsigned int *maxp, + hash_info_bucket_array_t *infop, + unsigned int *countp) +{ + vm_offset_t addr; + vm_size_t size = 0; /* '=0' to shut up lint */ + hash_info_bucket_t *info; + unsigned int potential, actual; + kern_return_t kr; + + if (host == HOST_NULL) + return KERN_INVALID_HOST; + + /* start with in-line data */ + + info = *infop; + potential = *countp; + + for (;;) { + actual = ipc_marequest_info(maxp, info, potential); + if (actual <= potential) + break; + + /* allocate more memory */ + + if (info != *infop) + kmem_free(ipc_kernel_map, addr, size); + + size = round_page(actual * sizeof *info); + kr = kmem_alloc_pageable(ipc_kernel_map, &addr, size); + if (kr != KERN_SUCCESS) + return KERN_RESOURCE_SHORTAGE; + + info = (hash_info_bucket_t *) addr; + potential = size/sizeof *info; + } + + if (info == *infop) { + /* data fit in-line; nothing to deallocate */ + + *countp = actual; + } else if (actual == 0) { + kmem_free(ipc_kernel_map, addr, size); + + *countp = 0; + } else { + vm_map_copy_t copy; + vm_size_t used; + + used = round_page(actual * sizeof *info); + + if (used != size) + kmem_free(ipc_kernel_map, addr + used, size - used); + + kr = vm_map_copyin(ipc_kernel_map, addr, used, + TRUE, ©); + assert(kr == KERN_SUCCESS); + + *infop = (hash_info_bucket_t *) copy; + *countp = actual; + } + + return KERN_SUCCESS; +} + +/* + * Routine: mach_port_dnrequest_info + * Purpose: + * Returns information about the dead-name requests + * registered with the named receive right. + * Conditions: + * Nothing locked. + * Returns: + * KERN_SUCCESS Retrieved information. + * KERN_INVALID_TASK The space is null. + * KERN_INVALID_TASK The space is dead. + * KERN_INVALID_NAME The name doesn't denote a right. + * KERN_INVALID_RIGHT Name doesn't denote receive rights. + */ + +kern_return_t +mach_port_dnrequest_info( + ipc_space_t space, + mach_port_name_t name, + unsigned int *totalp, + unsigned int *usedp) +{ + unsigned int total, used; + ipc_port_t port; + kern_return_t kr; + + if (space == IS_NULL) + return KERN_INVALID_TASK; + + kr = ipc_port_translate_receive(space, name, &port); + if (kr != KERN_SUCCESS) + return kr; + /* port is locked and active */ + + if (port->ip_dnrequests == IPR_NULL) { + total = 0; + used = 0; + } else { + ipc_port_request_t dnrequests = port->ip_dnrequests; + ipc_port_request_index_t index; + + total = dnrequests->ipr_size->its_size; + + for (index = 1, used = 0; + index < total; index++) { + ipc_port_request_t ipr = &dnrequests[index]; + + if (ipr->ipr_name != MACH_PORT_NULL) + used++; + } + } + ip_unlock(port); + + *totalp = total; + *usedp = used; + return KERN_SUCCESS; +} + +/* + * Routine: mach_port_kernel_object [kernel call] + * Purpose: + * Retrieve the type and address of the kernel object + * represented by a send or receive right. + * Conditions: + * Nothing locked. + * Returns: + * KERN_SUCCESS Retrieved kernel object info. + * KERN_INVALID_TASK The space is null. + * KERN_INVALID_TASK The space is dead. + * KERN_INVALID_NAME The name doesn't denote a right. + * KERN_INVALID_RIGHT Name doesn't denote + * send or receive rights. + */ + +kern_return_t +mach_port_kernel_object( + ipc_space_t space, + mach_port_name_t name, + unsigned int *typep, + vm_offset_t *addrp) +{ + ipc_entry_t entry; + ipc_port_t port; + kern_return_t kr; + + if (space == IS_NULL) + return KERN_INVALID_TASK; + + kr = ipc_right_lookup_read(space, name, &entry); + if (kr != KERN_SUCCESS) + return kr; + /* space is read-locked and active */ + + if ((entry->ie_bits & MACH_PORT_TYPE_SEND_RECEIVE) == 0) { + is_read_unlock(space); + return KERN_INVALID_RIGHT; + } + + port = (ipc_port_t) entry->ie_object; + assert(port != IP_NULL); + + ip_lock(port); + is_read_unlock(space); + + if (!ip_active(port)) { + ip_unlock(port); + return KERN_INVALID_RIGHT; + } + + *typep = ip_kotype(port); + *addrp = port->ip_kobject; + ip_unlock(port); + return KERN_SUCCESS; +} diff --git a/ipc/mach_msg.c b/ipc/mach_msg.c new file mode 100644 index 0000000..6194ef7 --- /dev/null +++ b/ipc/mach_msg.c @@ -0,0 +1,1709 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University. + * Copyright (c) 1993,1994 The University of Utah and + * the Computer Systems Laboratory (CSL). + * 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, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF + * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM 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. + */ +/* + */ +/* + * File: ipc/mach_msg.c + * Author: Rich Draves + * Date: 1989 + * + * Exported message traps. See mach/message.h. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Routine: mach_msg_send + * Purpose: + * Send a message. + * Conditions: + * Nothing locked. + * Returns: + * MACH_MSG_SUCCESS Sent the message. + * MACH_SEND_MSG_TOO_SMALL Message smaller than a header. + * MACH_SEND_NO_BUFFER Couldn't allocate buffer. + * MACH_SEND_INVALID_DATA Couldn't copy message data. + * MACH_SEND_INVALID_HEADER + * Illegal value in the message header bits. + * MACH_SEND_INVALID_DEST The space is dead. + * MACH_SEND_INVALID_NOTIFY Bad notify port. + * MACH_SEND_INVALID_DEST Can't copyin destination port. + * MACH_SEND_INVALID_REPLY Can't copyin reply port. + * MACH_SEND_TIMED_OUT Timeout expired without delivery. + * MACH_SEND_INTERRUPTED Delivery interrupted. + * MACH_SEND_NO_NOTIFY Can't allocate a msg-accepted request. + * MACH_SEND_WILL_NOTIFY Msg-accepted notif. requested. + * MACH_SEND_NOTIFY_IN_PROGRESS + * This space has already forced a message to this port. + */ + +mach_msg_return_t +mach_msg_send( + mach_msg_user_header_t *msg, + mach_msg_option_t option, + mach_msg_size_t send_size, + mach_msg_timeout_t time_out, + mach_port_name_t notify) +{ + ipc_space_t space = current_space(); + vm_map_t map = current_map(); + ipc_kmsg_t kmsg; + mach_msg_return_t mr; + + mr = ipc_kmsg_get(msg, send_size, &kmsg); + if (mr != MACH_MSG_SUCCESS) + return mr; + + if (option & MACH_SEND_CANCEL) { + if (notify == MACH_PORT_NULL) + mr = MACH_SEND_INVALID_NOTIFY; + else + mr = ipc_kmsg_copyin(kmsg, space, map, notify); + } else + mr = ipc_kmsg_copyin(kmsg, space, map, MACH_PORT_NULL); + if (mr != MACH_MSG_SUCCESS) { + ikm_free(kmsg); + return mr; + } + + if (option & MACH_SEND_NOTIFY) { + mr = ipc_mqueue_send(kmsg, MACH_SEND_TIMEOUT, + ((option & MACH_SEND_TIMEOUT) ? + time_out : MACH_MSG_TIMEOUT_NONE)); + if (mr == MACH_SEND_TIMED_OUT) { + ipc_port_t dest = (ipc_port_t) + kmsg->ikm_header.msgh_remote_port; + + if (notify == MACH_PORT_NULL) + mr = MACH_SEND_INVALID_NOTIFY; + else + mr = ipc_marequest_create(space, dest, + notify, &kmsg->ikm_marequest); + if (mr == MACH_MSG_SUCCESS) { + ipc_mqueue_send_always(kmsg); + return MACH_SEND_WILL_NOTIFY; + } + } + } else + mr = ipc_mqueue_send(kmsg, option & MACH_SEND_TIMEOUT, + time_out); + + if (mr != MACH_MSG_SUCCESS) { + mr |= ipc_kmsg_copyout_pseudo(kmsg, space, map); + + assert(kmsg->ikm_marequest == IMAR_NULL); + (void) ipc_kmsg_put(msg, kmsg, kmsg->ikm_header.msgh_size); + } + + return mr; +} + +/* + * Routine: mach_msg_receive + * Purpose: + * Receive a message. + * Conditions: + * Nothing locked. + * Returns: + * MACH_MSG_SUCCESS Received a message. + * MACH_RCV_INVALID_NAME The name doesn't denote a right, + * or the denoted right is not receive or port set. + * MACH_RCV_IN_SET Receive right is a member of a set. + * MACH_RCV_TOO_LARGE Message wouldn't fit into buffer. + * MACH_RCV_TIMED_OUT Timeout expired without a message. + * MACH_RCV_INTERRUPTED Reception interrupted. + * MACH_RCV_PORT_DIED Port/set died while receiving. + * MACH_RCV_PORT_CHANGED Port moved into set while receiving. + * MACH_RCV_INVALID_DATA Couldn't copy to user buffer. + * MACH_RCV_INVALID_NOTIFY Bad notify port. + * MACH_RCV_HEADER_ERROR + */ + +mach_msg_return_t +mach_msg_receive( + mach_msg_user_header_t *msg, + mach_msg_option_t option, + mach_msg_size_t rcv_size, + mach_port_name_t rcv_name, + mach_msg_timeout_t time_out, + mach_port_name_t notify) +{ + ipc_thread_t self = current_thread(); + ipc_space_t space = current_space(); + vm_map_t map = current_map(); + ipc_object_t object; + ipc_mqueue_t mqueue; + ipc_kmsg_t kmsg; + mach_port_seqno_t seqno; + mach_msg_return_t mr; + + mr = ipc_mqueue_copyin(space, rcv_name, &mqueue, &object); + if (mr != MACH_MSG_SUCCESS) + return mr; + /* hold ref for object; mqueue is locked */ + + /* + * ipc_mqueue_receive may not return, because if we block + * then our kernel stack may be discarded. So we save + * state here for mach_msg_receive_continue to pick up. + */ + + self->ith_msg = msg; + self->ith_option = option; + self->ith_rcv_size = rcv_size; + self->ith_timeout = time_out; + self->ith_notify = notify; + self->ith_object = object; + self->ith_mqueue = mqueue; + + if (option & MACH_RCV_LARGE) { + mr = ipc_mqueue_receive(mqueue, option & MACH_RCV_TIMEOUT, + rcv_size, time_out, + FALSE, mach_msg_receive_continue, + &kmsg, &seqno); + /* mqueue is unlocked */ + ipc_object_release(object); + if (mr != MACH_MSG_SUCCESS) { + if (mr == MACH_RCV_TOO_LARGE) { + mach_msg_size_t real_size = + (mach_msg_size_t) (vm_offset_t) kmsg; + + assert(real_size > rcv_size); + + (void) copyout(&real_size, + &msg->msgh_size, + sizeof(mach_msg_size_t)); + } + + return mr; + } + + kmsg->ikm_header.msgh_seqno = seqno; + assert(kmsg->ikm_header.msgh_size <= rcv_size); + } else { + mr = ipc_mqueue_receive(mqueue, option & MACH_RCV_TIMEOUT, + MACH_MSG_SIZE_MAX, time_out, + FALSE, mach_msg_receive_continue, + &kmsg, &seqno); + /* mqueue is unlocked */ + ipc_object_release(object); + if (mr != MACH_MSG_SUCCESS) + return mr; + + kmsg->ikm_header.msgh_seqno = seqno; + if (msg_usize(&kmsg->ikm_header) > rcv_size) { + ipc_kmsg_copyout_dest(kmsg, space); + (void) ipc_kmsg_put(msg, kmsg, sizeof *msg); + return MACH_RCV_TOO_LARGE; + } + } + + if (option & MACH_RCV_NOTIFY) { + if (notify == MACH_PORT_NULL) + mr = MACH_RCV_INVALID_NOTIFY; + else + mr = ipc_kmsg_copyout(kmsg, space, map, notify); + } else + mr = ipc_kmsg_copyout(kmsg, space, map, MACH_PORT_NULL); + if (mr != MACH_MSG_SUCCESS) { + if ((mr &~ MACH_MSG_MASK) == MACH_RCV_BODY_ERROR) { + (void) ipc_kmsg_put(msg, kmsg, + kmsg->ikm_header.msgh_size); + } else { + ipc_kmsg_copyout_dest(kmsg, space); + (void) ipc_kmsg_put(msg, kmsg, sizeof *msg); + } + + return mr; + } + + return ipc_kmsg_put(msg, kmsg, kmsg->ikm_header.msgh_size); +} + +/* + * Routine: mach_msg_receive_continue + * Purpose: + * Continue after blocking for a message. + * Conditions: + * Nothing locked. We are running on a new kernel stack, + * with the receive state saved in the thread. From here + * control goes back to user space. + */ + +void +mach_msg_receive_continue(void) +{ + ipc_thread_t self = current_thread(); + ipc_space_t space = current_space(); + vm_map_t map = current_map(); + mach_msg_user_header_t *msg = self->ith_msg; + mach_msg_option_t option = self->ith_option; + mach_msg_size_t rcv_size = self->ith_rcv_size; + mach_msg_timeout_t time_out = self->ith_timeout; + mach_port_name_t notify = self->ith_notify; + ipc_object_t object = self->ith_object; + ipc_mqueue_t mqueue = self->ith_mqueue; + ipc_kmsg_t kmsg; + mach_port_seqno_t seqno; + mach_msg_return_t mr; + + if (option & MACH_RCV_LARGE) { + mr = ipc_mqueue_receive(mqueue, option & MACH_RCV_TIMEOUT, + rcv_size, time_out, + TRUE, mach_msg_receive_continue, + &kmsg, &seqno); + /* mqueue is unlocked */ + ipc_object_release(object); + if (mr != MACH_MSG_SUCCESS) { + if (mr == MACH_RCV_TOO_LARGE) { + mach_msg_size_t real_size = + (mach_msg_size_t) (vm_offset_t) kmsg; + + assert(real_size > rcv_size); + + (void) copyout(&real_size, + &msg->msgh_size, + sizeof(mach_msg_size_t)); + } + + thread_syscall_return(mr); + /*NOTREACHED*/ + } + + kmsg->ikm_header.msgh_seqno = seqno; + assert(msg_usize(&kmsg->ikm_header) <= rcv_size); + } else { + mr = ipc_mqueue_receive(mqueue, option & MACH_RCV_TIMEOUT, + MACH_MSG_SIZE_MAX, time_out, + TRUE, mach_msg_receive_continue, + &kmsg, &seqno); + /* mqueue is unlocked */ + ipc_object_release(object); + if (mr != MACH_MSG_SUCCESS) { + thread_syscall_return(mr); + /*NOTREACHED*/ + } + + kmsg->ikm_header.msgh_seqno = seqno; + if (msg_usize(&kmsg->ikm_header) > rcv_size) { + ipc_kmsg_copyout_dest(kmsg, space); + (void) ipc_kmsg_put(msg, kmsg, sizeof *msg); + thread_syscall_return(MACH_RCV_TOO_LARGE); + /*NOTREACHED*/ + } + } + + if (option & MACH_RCV_NOTIFY) { + if (notify == MACH_PORT_NULL) + mr = MACH_RCV_INVALID_NOTIFY; + else + mr = ipc_kmsg_copyout(kmsg, space, map, notify); + } else + mr = ipc_kmsg_copyout(kmsg, space, map, MACH_PORT_NULL); + if (mr != MACH_MSG_SUCCESS) { + if ((mr &~ MACH_MSG_MASK) == MACH_RCV_BODY_ERROR) { + (void) ipc_kmsg_put(msg, kmsg, + kmsg->ikm_header.msgh_size); + } else { + ipc_kmsg_copyout_dest(kmsg, space); + (void) ipc_kmsg_put(msg, kmsg, sizeof *msg); + } + + thread_syscall_return(mr); + /*NOTREACHED*/ + } + + mr = ipc_kmsg_put(msg, kmsg, kmsg->ikm_header.msgh_size); + thread_syscall_return(mr); + /*NOTREACHED*/ +} + +/* + * Routine: mach_msg_trap [mach trap] + * Purpose: + * Possibly send a message; possibly receive a message. + * Conditions: + * Nothing locked. + * Returns: + * All of mach_msg_send and mach_msg_receive error codes. + */ + +mach_msg_return_t +mach_msg_trap( + mach_msg_user_header_t *msg, + mach_msg_option_t option, + mach_msg_size_t send_size, + mach_msg_size_t rcv_size, + mach_port_name_t rcv_name, + mach_msg_timeout_t time_out, + mach_port_name_t notify) +{ + mach_msg_return_t mr; + + /* first check for common cases */ + + if (option == (MACH_SEND_MSG|MACH_RCV_MSG)) { + ipc_thread_t self = current_thread(); + ipc_space_t space = self->task->itk_space; + ipc_kmsg_t kmsg; + ipc_port_t dest_port; + ipc_object_t rcv_object; + ipc_mqueue_t rcv_mqueue; + mach_msg_size_t reply_size; + + /* + * This case is divided into ten sections, each + * with a label. There are five optimized + * sections and six unoptimized sections, which + * do the same thing but handle all possible + * cases and are slower. + * + * The five sections for an RPC are + * 1) Get request message into a buffer. + * (fast_get or slow_get) + * 2) Copyin request message and rcv_name. + * (fast_copyin or slow_copyin) + * 3) Enqueue request and dequeue reply. + * (fast_send_receive or + * slow_send and slow_receive) + * 4) Copyout reply message. + * (fast_copyout or slow_copyout) + * 5) Put reply message to user's buffer. + * (fast_put or slow_put) + * + * Keep the locking hierarchy firmly in mind. + * (First spaces, then ports, then port sets, + * then message queues.) Only a non-blocking + * attempt can be made to acquire locks out of + * order, or acquire two locks on the same level. + * Acquiring two locks on the same level will + * fail if the objects are really the same, + * unless simple locking is disabled. This is OK, + * because then the extra unlock does nothing. + * + * There are two major reasons these RPCs can't use + * ipc_thread_switch, and use slow_send/slow_receive: + * 1) Kernel RPCs. + * 2) Servers fall behind clients, so + * client doesn't find a blocked server thread and + * server finds waiting messages and can't block. + */ + + /* + fast_get: + */ + /* + * optimized ipc_kmsg_get + * + * No locks, references, or messages held. + * We must clear ikm_cache before copyinmsg. + */ + + if (((send_size * IKM_EXPAND_FACTOR) > IKM_SAVED_MSG_SIZE) || + (send_size < sizeof(mach_msg_user_header_t)) || + (send_size & 3)) + goto slow_get; + + kmsg = ikm_cache_alloc_try(); + if (kmsg == IKM_NULL) + goto slow_get; + + if (copyinmsg(msg, &kmsg->ikm_header, + send_size, kmsg->ikm_size)) { + ikm_free(kmsg); + goto slow_get; + } + + fast_copyin: + /* + * optimized ipc_kmsg_copyin/ipc_mqueue_copyin + * + * We have the request message data in kmsg. + * Must still do copyin, send, receive, etc. + * + * If the message isn't simple, we can't combine + * ipc_kmsg_copyin_header and ipc_mqueue_copyin, + * because copyin of the message body might + * affect rcv_name. + */ + + switch (kmsg->ikm_header.msgh_bits) { + case MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, + MACH_MSG_TYPE_MAKE_SEND_ONCE): { + ipc_port_t reply_port; + { + mach_port_name_t reply_name = + kmsg->ikm_header.msgh_local_port; + + if (reply_name != rcv_name) + goto slow_copyin; + + is_read_lock(space); + assert(space->is_active); + + ipc_entry_t entry; + entry = ipc_entry_lookup (space, reply_name); + if (entry == IE_NULL) + { + ipc_entry_lookup_failed (msg, reply_name); + goto abort_request_copyin; + } + reply_port = (ipc_port_t) entry->ie_object; + assert(reply_port != IP_NULL); + } + + { + mach_port_name_t dest_name = + kmsg->ikm_header.msgh_remote_port; + + ipc_entry_t entry; + ipc_entry_bits_t bits; + entry = ipc_entry_lookup (space, dest_name); + if (entry == IE_NULL) + { + ipc_entry_lookup_failed (msg, dest_name); + goto abort_request_copyin; + } + bits = entry->ie_bits; + + /* check type bits */ + if (IE_BITS_TYPE (bits) != MACH_PORT_TYPE_SEND) + goto abort_request_copyin; + + assert(IE_BITS_UREFS(bits) > 0); + + dest_port = (ipc_port_t) entry->ie_object; + assert(dest_port != IP_NULL); + } + + /* + * To do an atomic copyin, need simultaneous + * locks on both ports and the space. If + * dest_port == reply_port, and simple locking is + * enabled, then we will abort. Otherwise it's + * OK to unlock twice. + */ + + ip_lock(dest_port); + if (!ip_active(dest_port) || + !ip_lock_try(reply_port)) { + ip_unlock(dest_port); + goto abort_request_copyin; + } + is_read_unlock(space); + + assert(dest_port->ip_srights > 0); + dest_port->ip_srights++; + ip_reference(dest_port); + + assert(ip_active(reply_port)); + assert(reply_port->ip_receiver_name == + kmsg->ikm_header.msgh_local_port); + assert(reply_port->ip_receiver == space); + + reply_port->ip_sorights++; + ip_reference(reply_port); + + kmsg->ikm_header.msgh_bits = + MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND, + MACH_MSG_TYPE_PORT_SEND_ONCE); + kmsg->ikm_header.msgh_remote_port = + (mach_port_t) dest_port; + kmsg->ikm_header.msgh_local_port = + (mach_port_t) reply_port; + + /* make sure we can queue to the destination */ + + if (dest_port->ip_receiver == ipc_space_kernel) { + /* + * The kernel server has a reference to + * the reply port, which it hands back + * to us in the reply message. We do + * not need to keep another reference to + * it. + */ + ip_unlock(reply_port); + + assert(ip_active(dest_port)); + ip_unlock(dest_port); + goto kernel_send; + } + + if (dest_port->ip_msgcount >= dest_port->ip_qlimit) + goto abort_request_send_receive; + + /* optimized ipc_mqueue_copyin */ + + if (reply_port->ip_pset != IPS_NULL) + goto abort_request_send_receive; + + rcv_object = (ipc_object_t) reply_port; + io_reference(rcv_object); + rcv_mqueue = &reply_port->ip_messages; + imq_lock(rcv_mqueue); + io_unlock(rcv_object); + goto fast_send_receive; + + abort_request_copyin: + is_read_unlock(space); + goto slow_copyin; + + abort_request_send_receive: + ip_unlock(dest_port); + ip_unlock(reply_port); + goto slow_send; + } + + case MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE, 0): { + /* sending a reply message */ + + { + mach_port_name_t reply_name = + kmsg->ikm_header.msgh_local_port; + + if (reply_name != MACH_PORT_NULL) + goto slow_copyin; + } + + is_write_lock(space); + assert(space->is_active); + + { + ipc_entry_t entry; + mach_port_name_t dest_name = + kmsg->ikm_header.msgh_remote_port; + + entry = ipc_entry_lookup (space, dest_name); + if (entry == IE_NULL) + { + ipc_entry_lookup_failed (msg, dest_name); + goto abort_reply_dest_copyin; + } + + /* check type bits */ + if (IE_BITS_TYPE (entry->ie_bits) != + MACH_PORT_TYPE_SEND_ONCE) + goto abort_reply_dest_copyin; + + /* optimized ipc_right_copyin */ + + assert(IE_BITS_TYPE(entry->ie_bits) == + MACH_PORT_TYPE_SEND_ONCE); + assert(IE_BITS_UREFS(entry->ie_bits) == 1); + assert((entry->ie_bits & IE_BITS_MAREQUEST) == 0); + + if (entry->ie_request != 0) + goto abort_reply_dest_copyin; + + dest_port = (ipc_port_t) entry->ie_object; + assert(dest_port != IP_NULL); + + ip_lock(dest_port); + if (!ip_active(dest_port)) { + ip_unlock(dest_port); + goto abort_reply_dest_copyin; + } + + assert(dest_port->ip_sorights > 0); + entry->ie_object = IO_NULL; + ipc_entry_dealloc (space, dest_name, entry); + } + + kmsg->ikm_header.msgh_bits = + MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, + 0); + kmsg->ikm_header.msgh_remote_port = + (mach_port_t) dest_port; + + /* make sure we can queue to the destination */ + + assert(dest_port->ip_receiver != ipc_space_kernel); + + /* optimized ipc_mqueue_copyin */ + + { + ipc_entry_t entry; + ipc_entry_bits_t bits; + entry = ipc_entry_lookup (space, rcv_name); + if (entry == IE_NULL) + { + ipc_entry_lookup_failed (msg, rcv_name); + goto abort_reply_rcv_copyin; + } + bits = entry->ie_bits; + + /* check type bits; looking for receive or set */ + + if (bits & MACH_PORT_TYPE_PORT_SET) { + ipc_pset_t rcv_pset; + + rcv_pset = (ipc_pset_t) entry->ie_object; + assert(rcv_pset != IPS_NULL); + + ips_lock(rcv_pset); + assert(ips_active(rcv_pset)); + + rcv_object = (ipc_object_t) rcv_pset; + rcv_mqueue = &rcv_pset->ips_messages; + } else if (bits & MACH_PORT_TYPE_RECEIVE) { + ipc_port_t rcv_port; + + rcv_port = (ipc_port_t) entry->ie_object; + assert(rcv_port != IP_NULL); + + if (!ip_lock_try(rcv_port)) + goto abort_reply_rcv_copyin; + assert(ip_active(rcv_port)); + + if (rcv_port->ip_pset != IPS_NULL) { + ip_unlock(rcv_port); + goto abort_reply_rcv_copyin; + } + + rcv_object = (ipc_object_t) rcv_port; + rcv_mqueue = &rcv_port->ip_messages; + } else + goto abort_reply_rcv_copyin; + } + + is_write_unlock(space); + io_reference(rcv_object); + imq_lock(rcv_mqueue); + io_unlock(rcv_object); + goto fast_send_receive; + + abort_reply_dest_copyin: + is_write_unlock(space); + goto slow_copyin; + + abort_reply_rcv_copyin: + ip_unlock(dest_port); + is_write_unlock(space); + goto slow_send; + } + + default: + goto slow_copyin; + } + /*NOTREACHED*/ + + fast_send_receive: + /* + * optimized ipc_mqueue_send/ipc_mqueue_receive + * + * Finished get/copyin of kmsg and copyin of rcv_name. + * space is unlocked, dest_port is locked, + * we can queue kmsg to dest_port, + * rcv_mqueue is locked, rcv_object holds a ref, + * if rcv_object is a port it isn't in a port set + * + * Note that if simple locking is turned off, + * then we could have dest_mqueue == rcv_mqueue + * and not abort when we try to lock dest_mqueue. + */ + + assert(ip_active(dest_port)); + assert(dest_port->ip_receiver != ipc_space_kernel); + assert((dest_port->ip_msgcount < dest_port->ip_qlimit) || + (MACH_MSGH_BITS_REMOTE(kmsg->ikm_header.msgh_bits) == + MACH_MSG_TYPE_PORT_SEND_ONCE)); + assert((kmsg->ikm_header.msgh_bits & + MACH_MSGH_BITS_CIRCULAR) == 0); + + { + ipc_mqueue_t dest_mqueue; + ipc_thread_t receiver; + + { + ipc_pset_t dest_pset; + + dest_pset = dest_port->ip_pset; + if (dest_pset == IPS_NULL) + dest_mqueue = &dest_port->ip_messages; + else + dest_mqueue = &dest_pset->ips_messages; + } + + if (!imq_lock_try(dest_mqueue)) { + abort_send_receive: + ip_unlock(dest_port); + imq_unlock(rcv_mqueue); + ipc_object_release(rcv_object); + goto slow_send; + } + + receiver = ipc_thread_queue_first(&dest_mqueue->imq_threads); + if ((receiver == ITH_NULL) || + (ipc_kmsg_queue_first(&rcv_mqueue->imq_messages) + != IKM_NULL)) { + imq_unlock(dest_mqueue); + goto abort_send_receive; + } + + /* + * There is a receiver thread waiting, and + * there is no reply message for us to pick up. + * We have hope of hand-off, so save state. + */ + + self->ith_msg = msg; + self->ith_rcv_size = rcv_size; + self->ith_object = rcv_object; + self->ith_mqueue = rcv_mqueue; + + if ((receiver->swap_func == mach_msg_continue) && + thread_handoff(self, mach_msg_continue, receiver)) { + assert(current_thread() == receiver); + + /* + * We can use the optimized receive code, + * because the receiver is using no options. + */ + } else if ((receiver->swap_func == + exception_raise_continue) && + thread_handoff(self, mach_msg_continue, receiver)) { + counter(c_mach_msg_trap_block_exc++); + assert(current_thread() == receiver); + + /* + * We are a reply message coming back through + * the optimized exception-handling path. + * Finish with rcv_mqueue and dest_mqueue, + * and then jump to exception code with + * dest_port still locked. We don't bother + * with a sequence number in this case. + */ + + ipc_thread_enqueue_macro( + &rcv_mqueue->imq_threads, self); + self->ith_state = MACH_RCV_IN_PROGRESS; + self->ith_msize = MACH_MSG_SIZE_MAX; + imq_unlock(rcv_mqueue); + + ipc_thread_rmqueue_first_macro( + &dest_mqueue->imq_threads, receiver); + imq_unlock(dest_mqueue); + + exception_raise_continue_fast(dest_port, kmsg); + /*NOTREACHED*/ + return MACH_MSG_SUCCESS; + } else if ((send_size <= receiver->ith_msize) && + thread_handoff(self, mach_msg_continue, receiver)) { + assert(current_thread() == receiver); + + if ((receiver->swap_func == + mach_msg_receive_continue) && + ((receiver->ith_option & MACH_RCV_NOTIFY) == 0)) { + /* + * We can still use the optimized code. + */ + } else { + counter(c_mach_msg_trap_block_slow++); + /* + * We are running as the receiver, + * but we can't use the optimized code. + * Finish send/receive processing. + */ + + dest_port->ip_msgcount++; + ip_unlock(dest_port); + + ipc_thread_enqueue_macro( + &rcv_mqueue->imq_threads, self); + self->ith_state = MACH_RCV_IN_PROGRESS; + self->ith_msize = MACH_MSG_SIZE_MAX; + imq_unlock(rcv_mqueue); + + ipc_thread_rmqueue_first_macro( + &dest_mqueue->imq_threads, receiver); + receiver->ith_state = MACH_MSG_SUCCESS; + receiver->ith_kmsg = kmsg; + receiver->ith_seqno = dest_port->ip_seqno++; + imq_unlock(dest_mqueue); + + /* + * Call the receiver's continuation. + */ + + receiver->wait_result = THREAD_AWAKENED; + (*receiver->swap_func)(); + /*NOTREACHED*/ + return MACH_MSG_SUCCESS; + } + } else { + /* + * The receiver can't accept the message, + * or we can't switch to the receiver. + */ + + imq_unlock(dest_mqueue); + goto abort_send_receive; + } + counter(c_mach_msg_trap_block_fast++); + + /* + * Safe to unlock dest_port now that we are + * committed to this path, because we hold + * dest_mqueue locked. We never bother changing + * dest_port->ip_msgcount. + */ + + ip_unlock(dest_port); + + /* + * We need to finish preparing self for its + * time asleep in rcv_mqueue. + */ + + ipc_thread_enqueue_macro(&rcv_mqueue->imq_threads, self); + self->ith_state = MACH_RCV_IN_PROGRESS; + self->ith_msize = MACH_MSG_SIZE_MAX; + imq_unlock(rcv_mqueue); + + /* + * Finish extracting receiver from dest_mqueue. + */ + + ipc_thread_rmqueue_first_macro( + &dest_mqueue->imq_threads, receiver); + kmsg->ikm_header.msgh_seqno = dest_port->ip_seqno++; + imq_unlock(dest_mqueue); + + /* + * We don't have to do any post-dequeue processing of + * the message. We never incremented ip_msgcount, we + * know it has no msg-accepted request, and blocked + * senders aren't a worry because we found the port + * with a receiver waiting. + */ + + self = receiver; + space = self->task->itk_space; + + msg = self->ith_msg; + rcv_size = self->ith_rcv_size; + rcv_object = self->ith_object; + + /* inline ipc_object_release */ + io_lock(rcv_object); + io_release(rcv_object); + io_check_unlock(rcv_object); + } + + fast_copyout: + /* + * Nothing locked and no references held, except + * we have kmsg with msgh_seqno filled in. Must + * still check against rcv_size and do + * ipc_kmsg_copyout/ipc_kmsg_put. + */ + + assert((ipc_port_t) kmsg->ikm_header.msgh_remote_port + == dest_port); + + reply_size = kmsg->ikm_header.msgh_size; + if (rcv_size < msg_usize(&kmsg->ikm_header)) + goto slow_copyout; + + /* optimized ipc_kmsg_copyout/ipc_kmsg_copyout_header */ + + switch (kmsg->ikm_header.msgh_bits) { + case MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND, + MACH_MSG_TYPE_PORT_SEND_ONCE): { + ipc_port_t reply_port = + (ipc_port_t) kmsg->ikm_header.msgh_local_port; + mach_port_name_t dest_name, reply_name; + rpc_uintptr_t payload; + + /* receiving a request message */ + + if (!IP_VALID(reply_port)) + goto slow_copyout; + + is_write_lock(space); + assert(space->is_active); + + /* + * To do an atomic copyout, need simultaneous + * locks on both ports and the space. If + * dest_port == reply_port, and simple locking is + * enabled, then we will abort. Otherwise it's + * OK to unlock twice. + */ + + ip_lock(dest_port); + if (!ip_active(dest_port) || + !ip_lock_try(reply_port)) + goto abort_request_copyout; + + if (!ip_active(reply_port)) { + ip_unlock(reply_port); + goto abort_request_copyout; + } + + assert(reply_port->ip_sorights > 0); + ip_unlock(reply_port); + + { + ipc_entry_t entry; + kern_return_t kr; + kr = ipc_entry_get (space, &reply_name, &entry); + if (kr) + goto abort_request_copyout; + assert (entry != NULL); + + { + mach_port_gen_t gen; + + assert((entry->ie_bits &~ IE_BITS_GEN_MASK) == 0); + gen = entry->ie_bits + IE_BITS_GEN_ONE; + + /* optimized ipc_right_copyout */ + + entry->ie_bits = gen | (MACH_PORT_TYPE_SEND_ONCE | 1); + } + + assert(MACH_PORT_NAME_VALID(reply_name)); + entry->ie_object = (ipc_object_t) reply_port; + is_write_unlock(space); + } + + /* optimized ipc_object_copyout_dest */ + + assert(dest_port->ip_srights > 0); + ip_release(dest_port); + + if (dest_port->ip_receiver == space) + dest_name = dest_port->ip_receiver_name; + else + dest_name = MACH_PORT_NULL; + payload = dest_port->ip_protected_payload; + + if ((--dest_port->ip_srights == 0) && + (dest_port->ip_nsrequest != IP_NULL)) { + ipc_port_t nsrequest; + mach_port_mscount_t mscount; + + /* a rather rare case */ + + nsrequest = dest_port->ip_nsrequest; + mscount = dest_port->ip_mscount; + dest_port->ip_nsrequest = IP_NULL; + ip_unlock(dest_port); + + ipc_notify_no_senders(nsrequest, mscount); + } else + ip_unlock(dest_port); + + if (! ipc_port_flag_protected_payload(dest_port)) { + kmsg->ikm_header.msgh_bits = MACH_MSGH_BITS( + MACH_MSG_TYPE_PORT_SEND_ONCE, + MACH_MSG_TYPE_PORT_SEND); + kmsg->ikm_header.msgh_local_port = dest_name; + } else { + kmsg->ikm_header.msgh_bits = MACH_MSGH_BITS( + MACH_MSG_TYPE_PORT_SEND_ONCE, + MACH_MSG_TYPE_PROTECTED_PAYLOAD); + kmsg->ikm_header.msgh_protected_payload = + payload; + } + kmsg->ikm_header.msgh_remote_port = reply_name; + goto fast_put; + + abort_request_copyout: + ip_unlock(dest_port); + is_write_unlock(space); + goto slow_copyout; + } + + case MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, 0): { + mach_port_name_t dest_name; + rpc_uintptr_t payload; + + /* receiving a reply message */ + + ip_lock(dest_port); + if (!ip_active(dest_port)) + goto slow_copyout; + + /* optimized ipc_object_copyout_dest */ + + assert(dest_port->ip_sorights > 0); + + payload = dest_port->ip_protected_payload; + + if (dest_port->ip_receiver == space) { + ip_release(dest_port); + dest_port->ip_sorights--; + dest_name = dest_port->ip_receiver_name; + ip_unlock(dest_port); + } else { + ip_unlock(dest_port); + + ipc_notify_send_once(dest_port); + dest_name = MACH_PORT_NULL; + } + + if (! ipc_port_flag_protected_payload(dest_port)) { + kmsg->ikm_header.msgh_bits = MACH_MSGH_BITS( + 0, + MACH_MSG_TYPE_PORT_SEND_ONCE); + kmsg->ikm_header.msgh_local_port = dest_name; + } else { + kmsg->ikm_header.msgh_bits = MACH_MSGH_BITS( + 0, + MACH_MSG_TYPE_PROTECTED_PAYLOAD); + kmsg->ikm_header.msgh_protected_payload = + payload; + } + kmsg->ikm_header.msgh_remote_port = MACH_PORT_NULL; + goto fast_put; + } + + case MACH_MSGH_BITS_COMPLEX| + MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, 0): { + mach_port_name_t dest_name; + rpc_uintptr_t payload; + + /* receiving a complex reply message */ + + ip_lock(dest_port); + if (!ip_active(dest_port)) + goto slow_copyout; + + /* optimized ipc_object_copyout_dest */ + + assert(dest_port->ip_sorights > 0); + + payload = dest_port->ip_protected_payload; + + if (dest_port->ip_receiver == space) { + ip_release(dest_port); + dest_port->ip_sorights--; + dest_name = dest_port->ip_receiver_name; + ip_unlock(dest_port); + } else { + ip_unlock(dest_port); + + ipc_notify_send_once(dest_port); + dest_name = MACH_PORT_NULL; + } + + if (! ipc_port_flag_protected_payload(dest_port)) { + kmsg->ikm_header.msgh_bits = + MACH_MSGH_BITS_COMPLEX + | MACH_MSGH_BITS( + 0, + MACH_MSG_TYPE_PORT_SEND_ONCE); + kmsg->ikm_header.msgh_local_port = dest_name; + } else { + kmsg->ikm_header.msgh_bits = + MACH_MSGH_BITS_COMPLEX + | MACH_MSGH_BITS( + 0, + MACH_MSG_TYPE_PROTECTED_PAYLOAD); + kmsg->ikm_header.msgh_protected_payload = + payload; + } + kmsg->ikm_header.msgh_remote_port = MACH_PORT_NULL; + + mr = ipc_kmsg_copyout_body( + kmsg, + space, + current_map()); + + if (mr != MACH_MSG_SUCCESS) { + (void) ipc_kmsg_put(msg, kmsg, + kmsg->ikm_header.msgh_size); + return mr | MACH_RCV_BODY_ERROR; + } + goto fast_put; + } + + default: + goto slow_copyout; + } + /*NOTREACHED*/ + + fast_put: + /* + * We have the reply message data in kmsg, + * and the reply message size in reply_size. + * Just need to copy it out to the user and free kmsg. + * We must check ikm_cache after copyoutmsg. + */ + + ikm_check_initialized(kmsg, kmsg->ikm_size); + + if ((kmsg->ikm_size != IKM_SAVED_KMSG_SIZE) || + copyoutmsg(&kmsg->ikm_header, msg, + reply_size)) + goto slow_put; + + if (!ikm_cache_free_try(kmsg)) + goto slow_put; + + thread_syscall_return(MACH_MSG_SUCCESS); + /*NOTREACHED*/ + return MACH_MSG_SUCCESS; /* help for the compiler */ + + /* + * The slow path has a few non-register temporary + * variables used only for call-by-reference. + */ + + { + ipc_kmsg_t temp_kmsg; + mach_port_seqno_t temp_seqno; + ipc_object_t temp_rcv_object; + ipc_mqueue_t temp_rcv_mqueue; + + slow_get: + /* + * No locks, references, or messages held. + * Still have to get the request, send it, + * receive reply, etc. + */ + + mr = ipc_kmsg_get(msg, send_size, &temp_kmsg); + if (mr != MACH_MSG_SUCCESS) { + thread_syscall_return(mr); + /*NOTREACHED*/ + } + kmsg = temp_kmsg; + + /* try to get back on optimized path */ + goto fast_copyin; + + slow_copyin: + /* + * We have the message data in kmsg, but + * we still need to copyin, send it, + * receive a reply, and do copyout. + */ + + mr = ipc_kmsg_copyin(kmsg, space, current_map(), + MACH_PORT_NULL); + if (mr != MACH_MSG_SUCCESS) { + ikm_free(kmsg); + thread_syscall_return(mr); + /*NOTREACHED*/ + } + + /* try to get back on optimized path */ + + if (kmsg->ikm_header.msgh_bits & MACH_MSGH_BITS_CIRCULAR) + goto slow_send; + + dest_port = (ipc_port_t) kmsg->ikm_header.msgh_remote_port; + assert(IP_VALID(dest_port)); + + ip_lock(dest_port); + if (dest_port->ip_receiver == ipc_space_kernel) { + assert(ip_active(dest_port)); + ip_unlock(dest_port); + goto kernel_send; + } + + if (ip_active(dest_port) && + ((dest_port->ip_msgcount < dest_port->ip_qlimit) || + (MACH_MSGH_BITS_REMOTE(kmsg->ikm_header.msgh_bits) == + MACH_MSG_TYPE_PORT_SEND_ONCE))) + { + /* + * Try an optimized ipc_mqueue_copyin. + * It will work if this is a request message. + */ + + ipc_port_t reply_port; + + reply_port = (ipc_port_t) + kmsg->ikm_header.msgh_local_port; + if (IP_VALID(reply_port)) { + if (ip_lock_try(reply_port)) { + if (ip_active(reply_port) && + reply_port->ip_receiver == space && + reply_port->ip_receiver_name == rcv_name && + reply_port->ip_pset == IPS_NULL) + { + /* Grab a reference to the reply port. */ + rcv_object = (ipc_object_t) reply_port; + io_reference(rcv_object); + rcv_mqueue = &reply_port->ip_messages; + imq_lock(rcv_mqueue); + io_unlock(rcv_object); + goto fast_send_receive; + } + ip_unlock(reply_port); + } + } + } + + ip_unlock(dest_port); + goto slow_send; + + kernel_send: + /* + * Special case: send message to kernel services. + * The request message has been copied into the + * kmsg. Nothing is locked. + */ + + { + ipc_port_t reply_port; + + /* + * Perform the kernel function. + */ + + kmsg = ipc_kobject_server(kmsg); + if (kmsg == IKM_NULL) { + /* + * No reply. Take the + * slow receive path. + */ + goto slow_get_rcv_port; + } + + /* + * Check that: + * the reply port is alive + * we hold the receive right + * the name has not changed. + * the port is not in a set + * If any of these are not true, + * we cannot directly receive the reply + * message. + */ + reply_port = (ipc_port_t) kmsg->ikm_header.msgh_remote_port; + ip_lock(reply_port); + + if ((!ip_active(reply_port)) || + (reply_port->ip_receiver != space) || + (reply_port->ip_receiver_name != rcv_name) || + (reply_port->ip_pset != IPS_NULL)) + { + ip_unlock(reply_port); + ipc_mqueue_send_always(kmsg); + goto slow_get_rcv_port; + } + + rcv_mqueue = &reply_port->ip_messages; + imq_lock(rcv_mqueue); + /* keep port locked, and don`t change ref count yet */ + + /* + * If there are messages on the port + * or other threads waiting for a message, + * we cannot directly receive the reply. + */ + if ((ipc_thread_queue_first(&rcv_mqueue->imq_threads) + != ITH_NULL) || + (ipc_kmsg_queue_first(&rcv_mqueue->imq_messages) + != IKM_NULL)) + { + imq_unlock(rcv_mqueue); + ip_unlock(reply_port); + ipc_mqueue_send_always(kmsg); + goto slow_get_rcv_port; + } + + /* + * We can directly receive this reply. + * Since the kernel reply never blocks, + * it holds no message_accepted request. + * Since there were no messages queued + * on the reply port, there should be + * no threads blocked waiting to send. + */ + + assert(kmsg->ikm_marequest == IMAR_NULL); + assert(ipc_thread_queue_first(&reply_port->ip_blocked) + == ITH_NULL); + + dest_port = reply_port; + kmsg->ikm_header.msgh_seqno = dest_port->ip_seqno++; + imq_unlock(rcv_mqueue); + + /* + * inline ipc_object_release. + * Port is still locked. + * Reference count was not incremented. + */ + ip_check_unlock(reply_port); + + /* copy out the kernel reply */ + goto fast_copyout; + } + + slow_send: + /* + * Nothing is locked. We have acquired kmsg, but + * we still need to send it and receive a reply. + */ + + mr = ipc_mqueue_send(kmsg, MACH_MSG_OPTION_NONE, + MACH_MSG_TIMEOUT_NONE); + if (mr != MACH_MSG_SUCCESS) { + mr |= ipc_kmsg_copyout_pseudo(kmsg, space, + current_map()); + + assert(kmsg->ikm_marequest == IMAR_NULL); + (void) ipc_kmsg_put(msg, kmsg, + kmsg->ikm_header.msgh_size); + thread_syscall_return(mr); + /*NOTREACHED*/ + } + + slow_get_rcv_port: + /* + * We have sent the message. Copy in the receive port. + */ + mr = ipc_mqueue_copyin(space, rcv_name, + &temp_rcv_mqueue, &temp_rcv_object); + if (mr != MACH_MSG_SUCCESS) { + thread_syscall_return(mr); + /*NOTREACHED*/ + } + rcv_mqueue = temp_rcv_mqueue; + rcv_object = temp_rcv_object; + /* hold ref for rcv_object; rcv_mqueue is locked */ + + /* + slow_receive: + */ + /* + * Now we have sent the request and copied in rcv_name, + * so rcv_mqueue is locked and hold ref for rcv_object. + * Just receive a reply and try to get back to fast path. + * + * ipc_mqueue_receive may not return, because if we block + * then our kernel stack may be discarded. So we save + * state here for mach_msg_continue to pick up. + */ + + self->ith_msg = msg; + self->ith_rcv_size = rcv_size; + self->ith_object = rcv_object; + self->ith_mqueue = rcv_mqueue; + + mr = ipc_mqueue_receive(rcv_mqueue, + MACH_MSG_OPTION_NONE, + MACH_MSG_SIZE_MAX, + MACH_MSG_TIMEOUT_NONE, + FALSE, mach_msg_continue, + &temp_kmsg, &temp_seqno); + /* rcv_mqueue is unlocked */ + ipc_object_release(rcv_object); + if (mr != MACH_MSG_SUCCESS) { + thread_syscall_return(mr); + /*NOTREACHED*/ + } + + (kmsg = temp_kmsg)->ikm_header.msgh_seqno = temp_seqno; + dest_port = (ipc_port_t) kmsg->ikm_header.msgh_remote_port; + goto fast_copyout; + + slow_copyout: + /* + * Nothing locked and no references held, except + * we have kmsg with msgh_seqno filled in. Must + * still check against rcv_size and do + * ipc_kmsg_copyout/ipc_kmsg_put. + */ + + reply_size = kmsg->ikm_header.msgh_size; + if (rcv_size < msg_usize(&kmsg->ikm_header)) { + ipc_kmsg_copyout_dest(kmsg, space); + (void) ipc_kmsg_put(msg, kmsg, sizeof *msg); + thread_syscall_return(MACH_RCV_TOO_LARGE); + /*NOTREACHED*/ + } + + mr = ipc_kmsg_copyout(kmsg, space, current_map(), + MACH_PORT_NULL); + if (mr != MACH_MSG_SUCCESS) { + if ((mr &~ MACH_MSG_MASK) == MACH_RCV_BODY_ERROR) { + (void) ipc_kmsg_put(msg, kmsg, + kmsg->ikm_header.msgh_size); + } else { + ipc_kmsg_copyout_dest(kmsg, space); + (void) ipc_kmsg_put(msg, kmsg, sizeof *msg); + } + + thread_syscall_return(mr); + /*NOTREACHED*/ + } + + /* try to get back on optimized path */ + + goto fast_put; + + slow_put: + mr = ipc_kmsg_put(msg, kmsg, reply_size); + thread_syscall_return(mr); + /*NOTREACHED*/ + } + } else if (option == MACH_SEND_MSG) { + ipc_space_t space = current_space(); + vm_map_t map = current_map(); + ipc_kmsg_t kmsg; + + mr = ipc_kmsg_get(msg, send_size, &kmsg); + if (mr != MACH_MSG_SUCCESS) + return mr; + + mr = ipc_kmsg_copyin(kmsg, space, map, MACH_PORT_NULL); + if (mr != MACH_MSG_SUCCESS) { + ikm_free(kmsg); + return mr; + } + + mr = ipc_mqueue_send(kmsg, MACH_MSG_OPTION_NONE, + MACH_MSG_TIMEOUT_NONE); + if (mr != MACH_MSG_SUCCESS) { + mr |= ipc_kmsg_copyout_pseudo(kmsg, space, map); + + assert(kmsg->ikm_marequest == IMAR_NULL); + (void) ipc_kmsg_put(msg, kmsg, + kmsg->ikm_header.msgh_size); + } + + return mr; + } else if (option == MACH_RCV_MSG) { + ipc_thread_t self = current_thread(); + ipc_space_t space = current_space(); + vm_map_t map = current_map(); + ipc_object_t object; + ipc_mqueue_t mqueue; + ipc_kmsg_t kmsg; + mach_port_seqno_t seqno; + + mr = ipc_mqueue_copyin(space, rcv_name, &mqueue, &object); + if (mr != MACH_MSG_SUCCESS) + return mr; + /* hold ref for object; mqueue is locked */ + + /* + * ipc_mqueue_receive may not return, because if we block + * then our kernel stack may be discarded. So we save + * state here for mach_msg_continue to pick up. + */ + + self->ith_msg = msg; + self->ith_rcv_size = rcv_size; + self->ith_object = object; + self->ith_mqueue = mqueue; + + mr = ipc_mqueue_receive(mqueue, + MACH_MSG_OPTION_NONE, + MACH_MSG_SIZE_MAX, + MACH_MSG_TIMEOUT_NONE, + FALSE, mach_msg_continue, + &kmsg, &seqno); + /* mqueue is unlocked */ + ipc_object_release(object); + if (mr != MACH_MSG_SUCCESS) + return mr; + + kmsg->ikm_header.msgh_seqno = seqno; + if (rcv_size < msg_usize(&kmsg->ikm_header)) { + ipc_kmsg_copyout_dest(kmsg, space); + (void) ipc_kmsg_put(msg, kmsg, sizeof *msg); + return MACH_RCV_TOO_LARGE; + } + + mr = ipc_kmsg_copyout(kmsg, space, map, MACH_PORT_NULL); + if (mr != MACH_MSG_SUCCESS) { + if ((mr &~ MACH_MSG_MASK) == MACH_RCV_BODY_ERROR) { + (void) ipc_kmsg_put(msg, kmsg, + kmsg->ikm_header.msgh_size); + } else { + ipc_kmsg_copyout_dest(kmsg, space); + (void) ipc_kmsg_put(msg, kmsg, sizeof *msg); + } + + return mr; + } + + return ipc_kmsg_put(msg, kmsg, kmsg->ikm_header.msgh_size); + } else if (option == MACH_MSG_OPTION_NONE) { + /* + * We can measure the "null mach_msg_trap" + * (syscall entry and thread_syscall_return exit) + * with this path. + */ + + thread_syscall_return(MACH_MSG_SUCCESS); + /*NOTREACHED*/ + } + + if (option & MACH_SEND_MSG) { + mr = mach_msg_send(msg, option, send_size, + time_out, notify); + if (mr != MACH_MSG_SUCCESS) + return mr; + } + + if (option & MACH_RCV_MSG) { + mr = mach_msg_receive(msg, option, rcv_size, rcv_name, + time_out, notify); + if (mr != MACH_MSG_SUCCESS) + return mr; + } + + return MACH_MSG_SUCCESS; +} + +/* + * Routine: mach_msg_continue + * Purpose: + * Continue after blocking for a message. + * Conditions: + * Nothing locked. We are running on a new kernel stack, + * with the receive state saved in the thread. From here + * control goes back to user space. + */ + +void +mach_msg_continue(void) +{ + ipc_thread_t thread = current_thread(); + task_t task = thread->task; + ipc_space_t space = task->itk_space; + vm_map_t map = task->map; + mach_msg_user_header_t *msg = thread->ith_msg; + mach_msg_size_t rcv_size = thread->ith_rcv_size; + ipc_object_t object = thread->ith_object; + ipc_mqueue_t mqueue = thread->ith_mqueue; + ipc_kmsg_t kmsg; + mach_port_seqno_t seqno; + mach_msg_return_t mr; + + mr = ipc_mqueue_receive(mqueue, MACH_MSG_OPTION_NONE, + MACH_MSG_SIZE_MAX, MACH_MSG_TIMEOUT_NONE, + TRUE, mach_msg_continue, &kmsg, &seqno); + /* mqueue is unlocked */ + ipc_object_release(object); + if (mr != MACH_MSG_SUCCESS) { + thread_syscall_return(mr); + /*NOTREACHED*/ + } + + kmsg->ikm_header.msgh_seqno = seqno; + if (msg_usize(&kmsg->ikm_header) > rcv_size) { + ipc_kmsg_copyout_dest(kmsg, space); + (void) ipc_kmsg_put(msg, kmsg, sizeof *msg); + thread_syscall_return(MACH_RCV_TOO_LARGE); + /*NOTREACHED*/ + } + + mr = ipc_kmsg_copyout(kmsg, space, map, MACH_PORT_NULL); + if (mr != MACH_MSG_SUCCESS) { + if ((mr &~ MACH_MSG_MASK) == MACH_RCV_BODY_ERROR) { + (void) ipc_kmsg_put(msg, kmsg, + kmsg->ikm_header.msgh_size); + } else { + ipc_kmsg_copyout_dest(kmsg, space); + (void) ipc_kmsg_put(msg, kmsg, sizeof *msg); + } + + thread_syscall_return(mr); + /*NOTREACHED*/ + } + + mr = ipc_kmsg_put(msg, kmsg, kmsg->ikm_header.msgh_size); + thread_syscall_return(mr); + /*NOTREACHED*/ +} + +/* + * Routine: mach_msg_interrupt + * Purpose: + * Attempts to force a thread waiting at mach_msg_continue or + * mach_msg_receive_continue into a clean point. Returns TRUE + * if this was possible. + * Conditions: + * Nothing locked. The thread must NOT be runnable. + */ + +boolean_t +mach_msg_interrupt(thread_t thread) +{ + ipc_mqueue_t mqueue; + + assert((thread->swap_func == mach_msg_continue) || + (thread->swap_func == mach_msg_receive_continue)); + + mqueue = thread->ith_mqueue; + imq_lock(mqueue); + if (thread->ith_state != MACH_RCV_IN_PROGRESS) { + /* + * The thread is no longer waiting for a message. + * It may have a message sitting in ith_kmsg. + * We can't clean this up. + */ + + imq_unlock(mqueue); + return FALSE; + } + ipc_thread_rmqueue(&mqueue->imq_threads, thread); + imq_unlock(mqueue); + + ipc_object_release(thread->ith_object); + + thread_set_syscall_return(thread, MACH_RCV_INTERRUPTED); + thread->swap_func = thread_exception_return; + return TRUE; +} diff --git a/ipc/mach_msg.h b/ipc/mach_msg.h new file mode 100644 index 0000000..2951bce --- /dev/null +++ b/ipc/mach_msg.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 "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. + */ +/* + */ +/* + * File: ipc/mach_msg.h + * Author: Rich Draves + * Date: 1989 + * + * Declarations of internal messaging primitives. + */ + +#ifndef _IPC_MACH_MSG_H_ +#define _IPC_MACH_MSG_H_ + +#include +#include + +extern mach_msg_return_t +mach_msg_send(mach_msg_user_header_t *, mach_msg_option_t, + mach_msg_size_t, mach_msg_timeout_t, mach_port_name_t); + +extern mach_msg_return_t +mach_msg_receive(mach_msg_user_header_t *, mach_msg_option_t, + mach_msg_size_t, mach_port_name_t, + mach_msg_timeout_t, mach_port_name_t); + +extern void +mach_msg_receive_continue(void); + +extern void +mach_msg_continue(void); + +extern boolean_t +mach_msg_interrupt(thread_t); + +#endif /* _IPC_MACH_MSG_H_ */ diff --git a/ipc/mach_port.c b/ipc/mach_port.c new file mode 100644 index 0000000..d8696e2 --- /dev/null +++ b/ipc/mach_port.c @@ -0,0 +1,1578 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University. + * Copyright (c) 1993,1994 The University of Utah and + * the Computer Systems Laboratory (CSL). + * 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, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF + * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM 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. + */ +/* + */ +/* + * File: ipc/mach_port.c + * Author: Rich Draves + * Date: 1989 + * + * Exported kernel calls. See mach/mach_port.defs. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef MIGRATING_THREADS +#include +#include +#endif /* MIGRATING_THREADS */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* + * Routine: mach_port_names_helper + * Purpose: + * A helper function for mach_port_names. + */ + +static void +mach_port_names_helper( + ipc_port_timestamp_t timestamp, + ipc_entry_t entry, + mach_port_name_t name, + mach_port_name_t *names, + mach_port_type_t *types, + ipc_entry_num_t *actualp) +{ + ipc_entry_bits_t bits = entry->ie_bits; + ipc_port_request_index_t request = entry->ie_request; + mach_port_type_t type; + ipc_entry_num_t actual; + + if (bits & MACH_PORT_TYPE_SEND_RIGHTS) { + ipc_port_t port; + boolean_t died; + + port = (ipc_port_t) entry->ie_object; + assert(port != IP_NULL); + + /* + * The timestamp serializes mach_port_names + * with ipc_port_destroy. If the port died, + * but after mach_port_names started, pretend + * that it isn't dead. + */ + + ip_lock(port); + died = (!ip_active(port) && + IP_TIMESTAMP_ORDER(port->ip_timestamp, timestamp)); + ip_unlock(port); + + if (died) { + /* pretend this is a dead-name entry */ + + bits &= ~(IE_BITS_TYPE_MASK|IE_BITS_MAREQUEST); + bits |= MACH_PORT_TYPE_DEAD_NAME; + if (request != 0) + bits++; + request = 0; + } + } + + type = IE_BITS_TYPE(bits); + if (request != 0) + type |= MACH_PORT_TYPE_DNREQUEST; + if (bits & IE_BITS_MAREQUEST) + type |= MACH_PORT_TYPE_MAREQUEST; + + actual = *actualp; + names[actual] = name; + types[actual] = type; + *actualp = actual+1; +} + +/* + * Routine: mach_port_names [kernel call] + * Purpose: + * Retrieves a list of the rights present in the space, + * along with type information. (Same as returned + * by mach_port_type.) The names are returned in + * no particular order, but they (and the type info) + * are an accurate snapshot of the space. + * Conditions: + * Nothing locked. + * Returns: + * KERN_SUCCESS Arrays of names and types returned. + * KERN_INVALID_TASK The space is null. + * KERN_INVALID_TASK The space is dead. + * KERN_RESOURCE_SHORTAGE Couldn't allocate memory. + */ + +kern_return_t +mach_port_names( + ipc_space_t space, + mach_port_name_t **namesp, + mach_msg_type_number_t *namesCnt, + mach_port_type_t **typesp, + mach_msg_type_number_t *typesCnt) +{ + ipc_entry_num_t actual; /* this many names */ + ipc_port_timestamp_t timestamp; /* logical time of this operation */ + mach_port_name_t *names; + mach_port_type_t *types; + kern_return_t kr; + + vm_size_t size; /* size of allocated memory */ + vm_offset_t addr1; /* allocated memory, for names */ + vm_offset_t addr2; /* allocated memory, for types */ + vm_map_copy_t memory1; /* copied-in memory, for names */ + vm_map_copy_t memory2; /* copied-in memory, for types */ + ipc_entry_num_t bound; + + /* safe simplifying assumption */ + assert_static(sizeof(mach_port_name_t) == sizeof(mach_port_type_t)); + + if (space == IS_NULL) + return KERN_INVALID_TASK; + + size = 0; + + for (;;) { + vm_size_t size_needed; + + is_read_lock(space); + if (!space->is_active) { + is_read_unlock(space); + if (size != 0) { + kmem_free(ipc_kernel_map, addr1, size); + kmem_free(ipc_kernel_map, addr2, size); + } + return KERN_INVALID_TASK; + } + + /* upper bound on number of names in the space */ + + bound = space->is_size; + size_needed = round_page(bound * sizeof(mach_port_name_t)); + + if (size_needed <= size) + break; + + is_read_unlock(space); + + if (size != 0) { + kmem_free(ipc_kernel_map, addr1, size); + kmem_free(ipc_kernel_map, addr2, size); + } + size = size_needed; + + kr = vm_allocate(ipc_kernel_map, &addr1, size, TRUE); + if (kr != KERN_SUCCESS) { + printf_once("no more room in ipc_kernel_map\n"); + return KERN_RESOURCE_SHORTAGE; + } + + kr = vm_allocate(ipc_kernel_map, &addr2, size, TRUE); + if (kr != KERN_SUCCESS) { + printf_once("no more room in ipc_kernel_map\n"); + kmem_free(ipc_kernel_map, addr1, size); + return KERN_RESOURCE_SHORTAGE; + } + + /* can't fault while we hold locks */ + + kr = vm_map_pageable(ipc_kernel_map, addr1, addr1 + size, + VM_PROT_READ|VM_PROT_WRITE, TRUE, TRUE); + assert(kr == KERN_SUCCESS); + + kr = vm_map_pageable(ipc_kernel_map, addr2, addr2 + size, + VM_PROT_READ|VM_PROT_WRITE, TRUE, TRUE); + assert(kr == KERN_SUCCESS); + } + /* space is read-locked and active */ + + names = (mach_port_name_t *) addr1; + types = (mach_port_type_t *) addr2; + actual = 0; + + timestamp = ipc_port_timestamp(); + + ipc_entry_t entry; + struct rdxtree_iter iter; + rdxtree_for_each(&space->is_map, &iter, entry) { + ipc_entry_bits_t bits = entry->ie_bits; + + if (IE_BITS_TYPE(bits) != MACH_PORT_TYPE_NONE) { + mach_port_names_helper(timestamp, entry, entry->ie_name, + names, types, &actual); + } + } + assert(actual < bound); + is_read_unlock(space); + + if (actual == 0) { + memory1 = VM_MAP_COPY_NULL; + memory2 = VM_MAP_COPY_NULL; + + if (size != 0) { + kmem_free(ipc_kernel_map, addr1, size); + kmem_free(ipc_kernel_map, addr2, size); + } + } else { + vm_size_t size_used; + + size_used = round_page(actual * sizeof(mach_port_name_t)); + + /* + * Make used memory pageable and get it into + * copied-in form. Free any unused memory. + */ + + kr = vm_map_pageable(ipc_kernel_map, + addr1, addr1 + size_used, + VM_PROT_NONE, TRUE, TRUE); + assert(kr == KERN_SUCCESS); + + kr = vm_map_pageable(ipc_kernel_map, + addr2, addr2 + size_used, + VM_PROT_NONE, TRUE, TRUE); + assert(kr == KERN_SUCCESS); + + kr = vm_map_copyin(ipc_kernel_map, addr1, size_used, + TRUE, &memory1); + assert(kr == KERN_SUCCESS); + + kr = vm_map_copyin(ipc_kernel_map, addr2, size_used, + TRUE, &memory2); + assert(kr == KERN_SUCCESS); + + if (size_used != size) { + kmem_free(ipc_kernel_map, + addr1 + size_used, size - size_used); + kmem_free(ipc_kernel_map, + addr2 + size_used, size - size_used); + } + } + + *namesp = (mach_port_name_t *) memory1; + *namesCnt = actual; + *typesp = (mach_port_type_t *) memory2; + *typesCnt = actual; + return KERN_SUCCESS; +} + +/* + * Routine: mach_port_type [kernel call] + * Purpose: + * Retrieves the type of a right in the space. + * The type is a bitwise combination of one or more + * of the following type bits: + * MACH_PORT_TYPE_SEND + * MACH_PORT_TYPE_RECEIVE + * MACH_PORT_TYPE_SEND_ONCE + * MACH_PORT_TYPE_PORT_SET + * MACH_PORT_TYPE_DEAD_NAME + * In addition, the following pseudo-type bits may be present: + * MACH_PORT_TYPE_DNREQUEST + * A dead-name notification is requested. + * MACH_PORT_TYPE_MAREQUEST + * The send/receive right is blocked; + * a msg-accepted notification is outstanding. + * MACH_PORT_TYPE_COMPAT + * This is a compatibility-mode right; + * when the port dies, it will disappear + * instead of turning into a dead-name. + * Conditions: + * Nothing locked. + * Returns: + * KERN_SUCCESS Type is returned. + * KERN_INVALID_TASK The space is null. + * KERN_INVALID_TASK The space is dead. + * KERN_INVALID_NAME The name doesn't denote a right. + */ + +kern_return_t +mach_port_type( + ipc_space_t space, + mach_port_name_t name, + mach_port_type_t *typep) +{ + mach_port_urefs_t urefs; + ipc_entry_t entry; + kern_return_t kr; + + if (space == IS_NULL) + return KERN_INVALID_TASK; + + kr = ipc_right_lookup_write(space, name, &entry); + if (kr != KERN_SUCCESS) + return kr; + /* space is write-locked and active */ + + kr = ipc_right_info(space, name, entry, typep, &urefs); + if (kr == KERN_SUCCESS) + is_write_unlock(space); + /* space is unlocked */ + return kr; +} + +/* + * Routine: mach_port_rename [kernel call] + * Purpose: + * Changes the name denoting a right, + * from oname to nname. + * Conditions: + * Nothing locked. + * Returns: + * KERN_SUCCESS The right is renamed. + * KERN_INVALID_TASK The space is null. + * KERN_INVALID_TASK The space is dead. + * KERN_INVALID_NAME The oname doesn't denote a right. + * KERN_INVALID_VALUE The nname isn't a legal name. + * KERN_NAME_EXISTS The nname already denotes a right. + * KERN_RESOURCE_SHORTAGE Couldn't allocate memory. + */ + +kern_return_t +mach_port_rename( + ipc_space_t space, + mach_port_name_t oname, + mach_port_name_t nname) +{ + if (space == IS_NULL) + return KERN_INVALID_TASK; + + if (!MACH_PORT_NAME_VALID(nname)) + return KERN_INVALID_VALUE; + + return ipc_object_rename(space, oname, nname); +} + +/* + * Routine: mach_port_allocate_name [kernel call] + * Purpose: + * Allocates a right in a space, using a specific name + * for the new right. Possible rights: + * MACH_PORT_RIGHT_RECEIVE + * MACH_PORT_RIGHT_PORT_SET + * MACH_PORT_RIGHT_DEAD_NAME + * + * A new port (allocated with MACH_PORT_RIGHT_RECEIVE) + * has no extant send or send-once rights and no queued + * messages. Its queue limit is MACH_PORT_QLIMIT_DEFAULT + * and its make-send count is 0. It is not a member of + * a port set. It has no registered no-senders or + * port-destroyed notification requests. + * + * A new port set has no members. + * + * A new dead name has one user reference. + * Conditions: + * Nothing locked. + * Returns: + * KERN_SUCCESS The right is allocated. + * KERN_INVALID_TASK The space is null. + * KERN_INVALID_TASK The space is dead. + * KERN_INVALID_VALUE The name isn't a legal name. + * KERN_INVALID_VALUE "right" isn't a legal kind of right. + * KERN_NAME_EXISTS The name already denotes a right. + * KERN_RESOURCE_SHORTAGE Couldn't allocate memory. + */ + +kern_return_t +mach_port_allocate_name( + ipc_space_t space, + mach_port_right_t right, + mach_port_name_t name) +{ + kern_return_t kr; + + if (space == IS_NULL) + return KERN_INVALID_TASK; + + if (!MACH_PORT_NAME_VALID(name)) + return KERN_INVALID_VALUE; + + switch (right) { + case MACH_PORT_RIGHT_RECEIVE: { + ipc_port_t port; + + kr = ipc_port_alloc_name(space, name, &port); + if (kr == KERN_SUCCESS) + ip_unlock(port); + break; + } + + case MACH_PORT_RIGHT_PORT_SET: { + ipc_pset_t pset; + + kr = ipc_pset_alloc_name(space, name, &pset); + if (kr == KERN_SUCCESS) + ips_unlock(pset); + break; + } + + case MACH_PORT_RIGHT_DEAD_NAME: + kr = ipc_object_alloc_dead_name(space, name); + break; + + default: + kr = KERN_INVALID_VALUE; + break; + } + + return kr; +} + +/* + * Routine: mach_port_allocate [kernel call] + * Purpose: + * Allocates a right in a space. Like mach_port_allocate_name, + * except that the implementation picks a name for the right. + * The name may be any legal name in the space that doesn't + * currently denote a right. + * Conditions: + * Nothing locked. + * Returns: + * KERN_SUCCESS The right is allocated. + * KERN_INVALID_TASK The space is null. + * KERN_INVALID_TASK The space is dead. + * KERN_INVALID_VALUE "right" isn't a legal kind of right. + * KERN_RESOURCE_SHORTAGE Couldn't allocate memory. + * KERN_NO_SPACE No room in space for another right. + */ + +kern_return_t +mach_port_allocate( + ipc_space_t space, + mach_port_right_t right, + mach_port_name_t *namep) +{ + kern_return_t kr; + + if (space == IS_NULL) + return KERN_INVALID_TASK; + + switch (right) { + case MACH_PORT_RIGHT_RECEIVE: { + ipc_port_t port; + + kr = ipc_port_alloc(space, namep, &port); + if (kr == KERN_SUCCESS) + ip_unlock(port); + break; + } + + case MACH_PORT_RIGHT_PORT_SET: { + ipc_pset_t pset; + + kr = ipc_pset_alloc(space, namep, &pset); + if (kr == KERN_SUCCESS) + ips_unlock(pset); + break; + } + + case MACH_PORT_RIGHT_DEAD_NAME: + kr = ipc_object_alloc_dead(space, namep); + break; + + default: + kr = KERN_INVALID_VALUE; + break; + } + + return (kr); +} + +/* + * Routine: mach_port_destroy [kernel call] + * Purpose: + * Cleans up and destroys all rights denoted by a name + * in a space. The destruction of a receive right + * destroys the port, unless a port-destroyed request + * has been made for it; the destruction of a port-set right + * destroys the port set. + * Conditions: + * Nothing locked. + * Returns: + * KERN_SUCCESS The name is destroyed. + * KERN_INVALID_TASK The space is null. + * KERN_INVALID_TASK The space is dead. + * KERN_INVALID_NAME The name doesn't denote a right. + */ + +volatile boolean_t mach_port_deallocate_debug = FALSE; + +kern_return_t +mach_port_destroy( + ipc_space_t space, + mach_port_name_t name) +{ + ipc_entry_t entry; + kern_return_t kr; + + if (space == IS_NULL) + return KERN_INVALID_TASK; + + kr = ipc_right_lookup_write(space, name, &entry); + if (kr != KERN_SUCCESS) { + if (MACH_PORT_NAME_VALID (name) && space == current_space()) { + printf("task %.*s destroying a bogus port %lu, most probably a bug.\n", (int) sizeof current_task()->name, current_task()->name, (unsigned long) name); + if (mach_port_deallocate_debug) + SoftDebugger("mach_port_deallocate"); + } + return kr; + } + /* space is write-locked and active */ + + kr = ipc_right_destroy(space, name, entry); /* unlocks space */ + return kr; +} + +/* + * Routine: mach_port_deallocate [kernel call] + * Purpose: + * Deallocates a user reference from a send right, + * send-once right, or a dead-name right. May + * deallocate the right, if this is the last uref, + * and destroy the name, if it doesn't denote + * other rights. + * Conditions: + * Nothing locked. + * Returns: + * KERN_SUCCESS The uref is deallocated. + * KERN_INVALID_TASK The space is null. + * KERN_INVALID_TASK The space is dead. + * KERN_INVALID_NAME The name doesn't denote a right. + * KERN_INVALID_RIGHT The right isn't correct. + */ + +kern_return_t +mach_port_deallocate( + ipc_space_t space, + mach_port_name_t name) +{ + ipc_entry_t entry; + kern_return_t kr; + + if (space == IS_NULL) + return KERN_INVALID_TASK; + + kr = ipc_right_lookup_write(space, name, &entry); + if (kr != KERN_SUCCESS) { + if (MACH_PORT_NAME_VALID (name) && space == current_space()) { + printf("task %.*s deallocating a bogus port %lu, most probably a bug.\n", (int) sizeof current_task()->name, current_task()->name, (unsigned long) name); + if (mach_port_deallocate_debug) + SoftDebugger("mach_port_deallocate"); + } + return kr; + } + /* space is write-locked */ + + kr = ipc_right_dealloc(space, name, entry); /* unlocks space */ + return kr; +} + +/* + * Routine: mach_port_get_refs [kernel call] + * Purpose: + * Retrieves the number of user references held by a right. + * Receive rights, port-set rights, and send-once rights + * always have one user reference. Returns zero if the + * name denotes a right, but not the queried right. + * Conditions: + * Nothing locked. + * Returns: + * KERN_SUCCESS Number of urefs returned. + * KERN_INVALID_TASK The space is null. + * KERN_INVALID_TASK The space is dead. + * KERN_INVALID_VALUE "right" isn't a legal value. + * KERN_INVALID_NAME The name doesn't denote a right. + */ + +kern_return_t +mach_port_get_refs( + ipc_space_t space, + mach_port_name_t name, + mach_port_right_t right, + mach_port_urefs_t *urefsp) +{ + mach_port_type_t type; + mach_port_urefs_t urefs; + ipc_entry_t entry; + kern_return_t kr; + + if (space == IS_NULL) + return KERN_INVALID_TASK; + + if (right >= MACH_PORT_RIGHT_NUMBER) + return KERN_INVALID_VALUE; + + kr = ipc_right_lookup_write(space, name, &entry); + if (kr != KERN_SUCCESS) + return kr; + /* space is write-locked and active */ + + kr = ipc_right_info(space, name, entry, &type, &urefs); /* unlocks */ + if (kr != KERN_SUCCESS) + return kr; /* space is unlocked */ + is_write_unlock(space); + + if (type & MACH_PORT_TYPE(right)) + switch (right) { + case MACH_PORT_RIGHT_SEND_ONCE: + assert(urefs == 1); + /* fall-through */ + + case MACH_PORT_RIGHT_PORT_SET: + case MACH_PORT_RIGHT_RECEIVE: + *urefsp = 1; + break; + + case MACH_PORT_RIGHT_DEAD_NAME: + case MACH_PORT_RIGHT_SEND: + assert(urefs > 0); + *urefsp = urefs; + break; + + default: + panic("mach_port_get_refs: strange rights"); + } + else + *urefsp = 0; + + return kr; +} + +/* + * Routine: mach_port_mod_refs + * Purpose: + * Modifies the number of user references held by a right. + * The resulting number of user references must be non-negative. + * If it is zero, the right is deallocated. If the name + * doesn't denote other rights, it is destroyed. + * Conditions: + * Nothing locked. + * Returns: + * KERN_SUCCESS Modified number of urefs. + * KERN_INVALID_TASK The space is null. + * KERN_INVALID_TASK The space is dead. + * KERN_INVALID_VALUE "right" isn't a legal value. + * KERN_INVALID_NAME The name doesn't denote a right. + * KERN_INVALID_RIGHT Name doesn't denote specified right. + * KERN_INVALID_VALUE Impossible modification to urefs. + * KERN_UREFS_OVERFLOW Urefs would overflow. + */ + +kern_return_t +mach_port_mod_refs( + ipc_space_t space, + mach_port_name_t name, + mach_port_right_t right, + mach_port_delta_t delta) +{ + ipc_entry_t entry; + kern_return_t kr; + + if (space == IS_NULL) + return KERN_INVALID_TASK; + + if (right >= MACH_PORT_RIGHT_NUMBER) + return KERN_INVALID_VALUE; + + kr = ipc_right_lookup_write(space, name, &entry); + if (kr != KERN_SUCCESS) { + if (MACH_PORT_NAME_VALID (name) && space == current_space()) { + printf("task %.*s %screasing a bogus port " + "%u by %d, most probably a bug.\n", + (int) (sizeof current_task()->name), + current_task()->name, + delta < 0 ? "de" : "in", name, + delta < 0 ? -delta : delta); + if (mach_port_deallocate_debug) + SoftDebugger("mach_port_mod_refs"); + } + return kr; + } + /* space is write-locked and active */ + + kr = ipc_right_delta(space, name, entry, right, delta); /* unlocks */ + return kr; +} + +/* + * Routine: mach_port_set_qlimit [kernel call] + * Purpose: + * Changes a receive right's queue limit. + * The new queue limit must be between 0 and + * MACH_PORT_QLIMIT_MAX, inclusive. + * Conditions: + * Nothing locked. + * Returns: + * KERN_SUCCESS Set queue limit. + * KERN_INVALID_TASK The space is null. + * KERN_INVALID_TASK The space is dead. + * KERN_INVALID_NAME The name doesn't denote a right. + * KERN_INVALID_RIGHT Name doesn't denote receive rights. + * KERN_INVALID_VALUE Illegal queue limit. + */ + +kern_return_t +mach_port_set_qlimit( + ipc_space_t space, + mach_port_name_t name, + mach_port_msgcount_t qlimit) +{ + ipc_port_t port; + kern_return_t kr; + + if (space == IS_NULL) + return KERN_INVALID_TASK; + + if (qlimit > MACH_PORT_QLIMIT_MAX) + return KERN_INVALID_VALUE; + + kr = ipc_port_translate_receive(space, name, &port); + if (kr != KERN_SUCCESS) + return kr; + /* port is locked and active */ + + ipc_port_set_qlimit(port, qlimit); + + ip_unlock(port); + return KERN_SUCCESS; +} + +/* + * Routine: mach_port_set_mscount [kernel call] + * Purpose: + * Changes a receive right's make-send count. + * Conditions: + * Nothing locked. + * Returns: + * KERN_SUCCESS Set make-send count. + * KERN_INVALID_TASK The space is null. + * KERN_INVALID_TASK The space is dead. + * KERN_INVALID_NAME The name doesn't denote a right. + * KERN_INVALID_RIGHT Name doesn't denote receive rights. + */ + +kern_return_t +mach_port_set_mscount( + ipc_space_t space, + mach_port_name_t name, + mach_port_mscount_t mscount) +{ + ipc_port_t port; + kern_return_t kr; + + if (space == IS_NULL) + return KERN_INVALID_TASK; + + kr = ipc_port_translate_receive(space, name, &port); + if (kr != KERN_SUCCESS) + return kr; + /* port is locked and active */ + + ipc_port_set_mscount(port, mscount); + + ip_unlock(port); + return KERN_SUCCESS; +} + +/* + * Routine: mach_port_set_seqno [kernel call] + * Purpose: + * Changes a receive right's sequence number. + * Conditions: + * Nothing locked. + * Returns: + * KERN_SUCCESS Set sequence number. + * KERN_INVALID_TASK The space is null. + * KERN_INVALID_TASK The space is dead. + * KERN_INVALID_NAME The name doesn't denote a right. + * KERN_INVALID_RIGHT Name doesn't denote receive rights. + */ + +kern_return_t +mach_port_set_seqno( + ipc_space_t space, + mach_port_name_t name, + mach_port_seqno_t seqno) +{ + ipc_port_t port; + kern_return_t kr; + + if (space == IS_NULL) + return KERN_INVALID_TASK; + + kr = ipc_port_translate_receive(space, name, &port); + if (kr != KERN_SUCCESS) + return kr; + /* port is locked and active */ + + ipc_port_set_seqno(port, seqno); + + ip_unlock(port); + return KERN_SUCCESS; +} + +/* + * Routine: mach_port_gst_helper + * Purpose: + * A helper function for mach_port_get_set_status. + */ + +static void +mach_port_gst_helper( + ipc_pset_t pset, + ipc_port_t port, + ipc_entry_num_t maxnames, + mach_port_name_t *names, + ipc_entry_num_t *actualp) +{ + ipc_pset_t ip_pset; + mach_port_name_t name; + + assert(port != IP_NULL); + + ip_lock(port); + assert(ip_active(port)); + + name = port->ip_receiver_name; + assert(name != MACH_PORT_NULL); + ip_pset = port->ip_pset; + + ip_unlock(port); + + if (pset == ip_pset) { + ipc_entry_num_t actual = *actualp; + + if (actual < maxnames) + names[actual] = name; + + *actualp = actual+1; + } +} + +/* + * Routine: mach_port_get_set_status [kernel call] + * Purpose: + * Retrieves a list of members in a port set. + * Returns the space's name for each receive right member. + * Conditions: + * Nothing locked. + * Returns: + * KERN_SUCCESS Retrieved list of members. + * KERN_INVALID_TASK The space is null. + * KERN_INVALID_TASK The space is dead. + * KERN_INVALID_NAME The name doesn't denote a right. + * KERN_INVALID_RIGHT Name doesn't denote a port set. + * KERN_RESOURCE_SHORTAGE Couldn't allocate memory. + */ + +kern_return_t +mach_port_get_set_status( + ipc_space_t space, + mach_port_name_t name, + mach_port_name_t **members, + mach_msg_type_number_t *membersCnt) +{ + ipc_entry_num_t actual; /* this many members */ + ipc_entry_num_t maxnames; /* space for this many members */ + kern_return_t kr; + + vm_size_t size; /* size of allocated memory */ + vm_offset_t addr; /* allocated memory */ + vm_map_copy_t memory; /* copied-in memory */ + + if (space == IS_NULL) + return KERN_INVALID_TASK; + + size = PAGE_SIZE; /* initial guess */ + + for (;;) { + ipc_entry_t entry; + mach_port_name_t *names; + ipc_pset_t pset; + + kr = vm_allocate(ipc_kernel_map, &addr, size, TRUE); + if (kr != KERN_SUCCESS) { + printf_once("no more room in ipc_kernel_map\n"); + return KERN_RESOURCE_SHORTAGE; + } + + /* can't fault while we hold locks */ + + kr = vm_map_pageable(ipc_kernel_map, addr, addr + size, + VM_PROT_READ|VM_PROT_WRITE, TRUE, TRUE); + assert(kr == KERN_SUCCESS); + + kr = ipc_right_lookup_read(space, name, &entry); + if (kr != KERN_SUCCESS) { + kmem_free(ipc_kernel_map, addr, size); + return kr; + } + /* space is read-locked and active */ + + if (IE_BITS_TYPE(entry->ie_bits) != MACH_PORT_TYPE_PORT_SET) { + is_read_unlock(space); + kmem_free(ipc_kernel_map, addr, size); + return KERN_INVALID_RIGHT; + } + + pset = (ipc_pset_t) entry->ie_object; + assert(pset != IPS_NULL); + /* the port set must be active */ + + names = (mach_port_name_t *) addr; + maxnames = size / sizeof(mach_port_name_t); + actual = 0; + + ipc_entry_t ientry; + struct rdxtree_iter iter; + rdxtree_for_each(&space->is_map, &iter, ientry) { + ipc_entry_bits_t bits = ientry->ie_bits; + + if (bits & MACH_PORT_TYPE_RECEIVE) { + ipc_port_t port = + (ipc_port_t) ientry->ie_object; + + mach_port_gst_helper(pset, port, maxnames, + names, &actual); + } + } + + is_read_unlock(space); + + if (actual <= maxnames) + break; + + /* didn't have enough memory; allocate more */ + + kmem_free(ipc_kernel_map, addr, size); + size = round_page(actual * sizeof(mach_port_name_t)) + PAGE_SIZE; + } + + if (actual == 0) { + memory = VM_MAP_COPY_NULL; + + kmem_free(ipc_kernel_map, addr, size); + } else { + vm_size_t size_used; + + size_used = round_page(actual * sizeof(mach_port_name_t)); + + /* + * Make used memory pageable and get it into + * copied-in form. Free any unused memory. + */ + + kr = vm_map_pageable(ipc_kernel_map, + addr, addr + size_used, + VM_PROT_NONE, TRUE, TRUE); + assert(kr == KERN_SUCCESS); + + kr = vm_map_copyin(ipc_kernel_map, addr, size_used, + TRUE, &memory); + assert(kr == KERN_SUCCESS); + + if (size_used != size) + kmem_free(ipc_kernel_map, + addr + size_used, size - size_used); + } + + *members = (mach_port_name_t *) memory; + *membersCnt = actual; + return KERN_SUCCESS; +} + +/* + * Routine: mach_port_move_member [kernel call] + * Purpose: + * If after is MACH_PORT_NULL, removes member + * from the port set it is in. Otherwise, adds + * member to after, removing it from any set + * it might already be in. + * Conditions: + * Nothing locked. + * Returns: + * KERN_SUCCESS Moved the port. + * KERN_INVALID_TASK The space is null. + * KERN_INVALID_TASK The space is dead. + * KERN_INVALID_NAME Member didn't denote a right. + * KERN_INVALID_RIGHT Member didn't denote a receive right. + * KERN_INVALID_NAME After didn't denote a right. + * KERN_INVALID_RIGHT After didn't denote a port set right. + * KERN_NOT_IN_SET + * After is MACH_PORT_NULL and Member isn't in a port set. + */ + +kern_return_t +mach_port_move_member( + ipc_space_t space, + mach_port_name_t member, + mach_port_name_t after) +{ + ipc_entry_t entry; + ipc_port_t port; + ipc_pset_t nset; + kern_return_t kr; + + if (space == IS_NULL) + return KERN_INVALID_TASK; + + kr = ipc_right_lookup_read(space, member, &entry); + if (kr != KERN_SUCCESS) + return kr; + /* space is read-locked and active */ + + if ((entry->ie_bits & MACH_PORT_TYPE_RECEIVE) == 0) { + is_read_unlock(space); + return KERN_INVALID_RIGHT; + } + + port = (ipc_port_t) entry->ie_object; + assert(port != IP_NULL); + + if (after == MACH_PORT_NULL) + nset = IPS_NULL; + else { + entry = ipc_entry_lookup(space, after); + if (entry == IE_NULL) { + is_read_unlock(space); + return KERN_INVALID_NAME; + } + + if ((entry->ie_bits & MACH_PORT_TYPE_PORT_SET) == 0) { + is_read_unlock(space); + return KERN_INVALID_RIGHT; + } + + nset = (ipc_pset_t) entry->ie_object; + assert(nset != IPS_NULL); + } + + kr = ipc_pset_move(space, port, nset); + /* space is unlocked */ + return kr; +} + +/* + * Routine: mach_port_request_notification [kernel call] + * Purpose: + * Requests a notification. The caller supplies + * a send-once right for the notification to use, + * and the call returns the previously registered + * send-once right, if any. Possible types: + * + * MACH_NOTIFY_PORT_DESTROYED + * Requests a port-destroyed notification + * for a receive right. Sync should be zero. + * MACH_NOTIFY_NO_SENDERS + * Requests a no-senders notification for a + * receive right. If there are currently no + * senders, sync is less than or equal to the + * current make-send count, and a send-once right + * is supplied, then an immediate no-senders + * notification is generated. + * MACH_NOTIFY_DEAD_NAME + * Requests a dead-name notification for a send + * or receive right. If the name is already a + * dead name, sync is non-zero, and a send-once + * right is supplied, then an immediate dead-name + * notification is generated. + * Conditions: + * Nothing locked. + * Returns: + * KERN_SUCCESS Requested a notification. + * KERN_INVALID_TASK The space is null. + * KERN_INVALID_TASK The space is dead. + * KERN_INVALID_VALUE Bad id value. + * KERN_INVALID_NAME Name doesn't denote a right. + * KERN_INVALID_RIGHT Name doesn't denote appropriate right. + * KERN_INVALID_CAPABILITY The notify port is dead. + * MACH_NOTIFY_PORT_DESTROYED: + * KERN_INVALID_VALUE Sync isn't zero. + * MACH_NOTIFY_DEAD_NAME: + * KERN_RESOURCE_SHORTAGE Couldn't allocate memory. + * KERN_INVALID_ARGUMENT Name denotes dead name, but + * sync is zero or notify is IP_NULL. + * KERN_UREFS_OVERFLOW Name denotes dead name, but + * generating immediate notif. would overflow urefs. + */ + +kern_return_t +mach_port_request_notification( + ipc_space_t space, + mach_port_name_t name, + mach_msg_id_t id, + mach_port_mscount_t sync, + ipc_port_t notify, + ipc_port_t *previousp) +{ + kern_return_t kr; + + if (space == IS_NULL) + return KERN_INVALID_TASK; + + if (notify == IP_DEAD) + return KERN_INVALID_CAPABILITY; + + switch (id) { + case MACH_NOTIFY_PORT_DESTROYED: { + ipc_port_t port, previous; + + if (sync != 0) + return KERN_INVALID_VALUE; + + kr = ipc_port_translate_receive(space, name, &port); + if (kr != KERN_SUCCESS) + return kr; + /* port is locked and active */ + + ipc_port_pdrequest(port, notify, &previous); + /* port is unlocked */ + + *previousp = previous; + break; + } + + case MACH_NOTIFY_NO_SENDERS: { + ipc_port_t port; + + kr = ipc_port_translate_receive(space, name, &port); + if (kr != KERN_SUCCESS) + return kr; + /* port is locked and active */ + + ipc_port_nsrequest(port, sync, notify, previousp); + /* port is unlocked */ + break; + } + + case MACH_NOTIFY_DEAD_NAME: + kr = ipc_right_dnrequest(space, name, sync != 0, + notify, previousp); + if (kr != KERN_SUCCESS) + return kr; + break; + + default: + return KERN_INVALID_VALUE; + } + + return KERN_SUCCESS; +} + +/* + * Routine: mach_port_insert_right [kernel call] + * Purpose: + * Inserts a right into a space, as if the space + * voluntarily received the right in a message, + * except that the right gets the specified name. + * Conditions: + * Nothing locked. + * Returns: + * KERN_SUCCESS Inserted the right. + * KERN_INVALID_TASK The space is null. + * KERN_INVALID_TASK The space is dead. + * KERN_INVALID_VALUE The name isn't a legal name. + * KERN_NAME_EXISTS The name already denotes a right. + * KERN_INVALID_VALUE Message doesn't carry a port right. + * KERN_INVALID_CAPABILITY Port is null or dead. + * KERN_UREFS_OVERFLOW Urefs limit would be exceeded. + * KERN_RIGHT_EXISTS Space has rights under another name. + * KERN_RESOURCE_SHORTAGE Couldn't allocate memory. + */ + +kern_return_t +mach_port_insert_right( + ipc_space_t space, + mach_port_name_t name, + ipc_port_t poly, + mach_msg_type_name_t polyPoly) +{ + if (space == IS_NULL) + return KERN_INVALID_TASK; + + if (!MACH_PORT_NAME_VALID(name) || + !MACH_MSG_TYPE_PORT_ANY_RIGHT(polyPoly)) + return KERN_INVALID_VALUE; + + if (!IO_VALID((ipc_object_t)poly)) + return KERN_INVALID_CAPABILITY; + + return ipc_object_copyout_name(space, (ipc_object_t)poly, + polyPoly, FALSE, name); +} + +/* + * Routine: mach_port_extract_right [kernel call] + * Purpose: + * Extracts a right from a space, as if the space + * voluntarily sent the right to the caller. + * Conditions: + * Nothing locked. + * Returns: + * KERN_SUCCESS Extracted the right. + * KERN_INVALID_TASK The space is null. + * KERN_INVALID_TASK The space is dead. + * KERN_INVALID_VALUE Requested type isn't a port right. + * KERN_INVALID_NAME Name doesn't denote a right. + * KERN_INVALID_RIGHT Name doesn't denote appropriate right. + */ + +kern_return_t +mach_port_extract_right( + ipc_space_t space, + mach_port_name_t name, + mach_msg_type_name_t msgt_name, + ipc_port_t *poly, + mach_msg_type_name_t *polyPoly) +{ + kern_return_t kr; + + if (space == IS_NULL) + return KERN_INVALID_TASK; + + if (!MACH_MSG_TYPE_PORT_ANY(msgt_name)) + return KERN_INVALID_VALUE; + + kr = ipc_object_copyin(space, name, msgt_name, (ipc_object_t *) poly); + + if (kr == KERN_SUCCESS) + *polyPoly = ipc_object_copyin_type(msgt_name); + return kr; +} + +/* + * Routine: mach_port_get_receive_status [kernel call] + * Purpose: + * Retrieves mucho info about a receive right. + * Conditions: + * Nothing locked. + * Returns: + * KERN_SUCCESS Retrieved status. + * KERN_INVALID_TASK The space is null. + * KERN_INVALID_TASK The space is dead. + * KERN_INVALID_NAME The name doesn't denote a right. + * KERN_INVALID_RIGHT Name doesn't denote receive rights. + */ + +kern_return_t +mach_port_get_receive_status( + ipc_space_t space, + mach_port_name_t name, + mach_port_status_t *statusp) +{ + ipc_port_t port; + kern_return_t kr; + + if (space == IS_NULL) + return KERN_INVALID_TASK; + + kr = ipc_port_translate_receive(space, name, &port); + if (kr != KERN_SUCCESS) + return kr; + /* port is locked and active */ + + if (port->ip_pset != IPS_NULL) { + ipc_pset_t pset = port->ip_pset; + + ips_lock(pset); + if (!ips_active(pset)) { + ipc_pset_remove(pset, port); + ips_check_unlock(pset); + goto no_port_set; + } else { + statusp->mps_pset = pset->ips_local_name; + imq_lock(&pset->ips_messages); + statusp->mps_seqno = port->ip_seqno; + imq_unlock(&pset->ips_messages); + ips_unlock(pset); + assert(MACH_PORT_NAME_VALID(statusp->mps_pset)); + } + } else { + no_port_set: + statusp->mps_pset = MACH_PORT_NULL; + imq_lock(&port->ip_messages); + statusp->mps_seqno = port->ip_seqno; + imq_unlock(&port->ip_messages); + } + + statusp->mps_mscount = port->ip_mscount; + statusp->mps_qlimit = port->ip_qlimit; + statusp->mps_msgcount = port->ip_msgcount; + statusp->mps_sorights = port->ip_sorights; + statusp->mps_srights = port->ip_srights > 0; + statusp->mps_pdrequest = port->ip_pdrequest != IP_NULL; + statusp->mps_nsrequest = port->ip_nsrequest != IP_NULL; + ip_unlock(port); + + return KERN_SUCCESS; +} + +#ifdef MIGRATING_THREADS +kern_return_t +mach_port_set_rpcinfo( + ipc_space_t space, + mach_port_name_t name, + void *rpc_info, + unsigned int rpc_info_count) +{ + ipc_target_t target; + ipc_object_t object; + kern_return_t kr; + + if (space == IS_NULL) + return KERN_INVALID_TASK; + + kr = ipc_object_translate(space, name, + MACH_PORT_RIGHT_PORT_SET, &object); + if (kr == KERN_SUCCESS) + target = &((ipc_pset_t)object)->ips_target; + else { + kr = ipc_object_translate(space, name, + MACH_PORT_RIGHT_RECEIVE, &object); + if (kr != KERN_SUCCESS) + return kr; + target = &((ipc_port_t)object)->ip_target; + } + + /* port/pset is locked and active */ + + kr = port_machine_set_rpcinfo(target, rpc_info, rpc_info_count); + + io_unlock(object); + + return kr; +} + +#if 1 +int sacts, maxsacts; +#endif + +void sact_count(void) +{ + printf("%d server activations in use, %d max\n", sacts, maxsacts); +} + +kern_return_t +mach_port_create_act( + task_t task, + mach_port_name_t name, + vm_offset_t user_stack, + vm_offset_t user_rbuf, + vm_size_t user_rbuf_size, + Act **out_act) +{ + ipc_target_t target; + ipc_space_t space; + ipc_object_t object; + kern_return_t kr; + Act *act; + + if (task == 0) + return KERN_INVALID_TASK; + + /* First create the new activation. */ + kr = act_create(task, user_stack, user_rbuf, user_rbuf_size, &act); + if (kr != KERN_SUCCESS) + return kr; + + space = task->itk_space; + + kr = ipc_object_translate(space, name, + MACH_PORT_RIGHT_PORT_SET, &object); + if (kr == KERN_SUCCESS) + target = &((ipc_pset_t)object)->ips_target; + else { + kr = ipc_object_translate(space, name, + MACH_PORT_RIGHT_RECEIVE, &object); + if (kr != KERN_SUCCESS) { + act_terminate(act); + act_deallocate(act); + return kr; + } + target = &((ipc_port_t)object)->ip_target; + } + + /* port/pset is locked and active */ +#if 0 + printf("act port/pset %08x ipc_target %08x stack %08x act %08x\n", + object, target, user_stack, act); +#endif + + /* Assign the activation to the port's actpool. */ + kr = act_set_target(act, target); + if (kr != KERN_SUCCESS) { + io_unlock(object); + act_terminate(act); + act_deallocate(act); + return kr; + } +#if 0 + printf(" actpool %08x act %08x\n", target->ip_actpool, act); +#endif + + io_unlock(object); + + /* Pass our reference to the activation back to the user. */ + *out_act = act; + +#if 1 + sacts++; + if (sacts > maxsacts) + maxsacts = sacts; + act->mact.pcb->ss.mpsfu_high = 0x69; +#endif + return KERN_SUCCESS; +} + +#ifdef RPCKERNELSIG +kern_return_t +mach_port_set_syscall_right( + task_t task, + mach_port_name_t name) +{ + ipc_entry_t entry; + kern_return_t kr; + + if (task == IS_NULL) + return KERN_INVALID_TASK; + + kr = ipc_right_lookup_write(task, name, &entry); + if (kr != KERN_SUCCESS) { + return kr; + } + + if (!(entry->ie_bits & MACH_PORT_TYPE(MACH_PORT_RIGHT_SEND))) { + is_write_unlock(space); + return KERN_INVALID_RIGHT; + } + + task->syscall_ipc_entry = *entry; + + is_write_unlock(space); + + return KERN_SUCCESS; +} +#endif +#endif /* MIGRATING_THREADS */ + +/* + * Routine: mach_port_set_protected_payload [kernel call] + * Purpose: + * Changes a receive right's protected payload. + * Conditions: + * Nothing locked. + * Returns: + * KERN_SUCCESS Set protected payload. + * KERN_INVALID_TASK The space is null. + * KERN_INVALID_TASK The space is dead. + * KERN_INVALID_NAME The name doesn't denote a right. + * KERN_INVALID_RIGHT Name doesn't denote receive rights. + */ + +kern_return_t +mach_port_set_protected_payload( + ipc_space_t space, + mach_port_name_t name, + rpc_uintptr_t payload) +{ + ipc_port_t port; + kern_return_t kr; + + if (space == IS_NULL) + return KERN_INVALID_TASK; + + kr = ipc_port_translate_receive(space, name, &port); + if (kr != KERN_SUCCESS) + return kr; + /* port is locked and active */ + + ipc_port_set_protected_payload(port, payload); + + ip_unlock(port); + return KERN_SUCCESS; +} + +/* + * Routine: mach_port_clear_protected_payload [kernel call] + * Purpose: + * Clears a receive right's protected payload. + * Conditions: + * Nothing locked. + * Returns: + * KERN_SUCCESS Clear protected payload. + * KERN_INVALID_TASK The space is null. + * KERN_INVALID_TASK The space is dead. + * KERN_INVALID_NAME The name doesn't denote a right. + * KERN_INVALID_RIGHT Name doesn't denote receive rights. + */ + +kern_return_t +mach_port_clear_protected_payload( + ipc_space_t space, + mach_port_name_t name) +{ + ipc_port_t port; + kern_return_t kr; + + if (space == IS_NULL) + return KERN_INVALID_TASK; + + kr = ipc_port_translate_receive(space, name, &port); + if (kr != KERN_SUCCESS) + return kr; + /* port is locked and active */ + + ipc_port_clear_protected_payload(port); + + ip_unlock(port); + return KERN_SUCCESS; +} + +#if MACH_KDB + +void +db_debug_port_references (boolean_t enable) +{ + mach_port_deallocate_debug = enable; +} + +#endif /* MACH_KDB */ diff --git a/ipc/mach_port.h b/ipc/mach_port.h new file mode 100644 index 0000000..e91e495 --- /dev/null +++ b/ipc/mach_port.h @@ -0,0 +1,37 @@ +/* + * Mach Port Functions. + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Barry deFreese. + */ +/* + * Mach port functions. + * + */ + +#ifndef _IPC_MACH_PORT_H_ +#define _IPC_MACH_PORT_H_ + +#include +#include +#include + +#if MACH_KDB +void db_debug_port_references (boolean_t enable); +#endif /* MACH_KDB */ + +#endif /* _IPC_MACH_PORT_H_ */ diff --git a/ipc/mach_port.srv b/ipc/mach_port.srv new file mode 100644 index 0000000..c4f8536 --- /dev/null +++ b/ipc/mach_port.srv @@ -0,0 +1,27 @@ +/* + * Copyright (c) 1994 The University of Utah and + * the Computer Systems Laboratory at the University of Utah (CSL). + * All rights reserved. + * + * Permission to use, copy, modify and distribute this software is hereby + * granted provided that (1) source code retains these copyright, permission, + * and disclaimer notices, and (2) redistributions including binaries + * reproduce the notices in supporting documentation, and (3) all advertising + * materials mentioning features or use of this software display the following + * acknowledgement: ``This product includes software developed by the + * Computer Systems Laboratory at the University of Utah.'' + * + * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS + * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF + * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * CSL requests users of this software to return to csl-dist@cs.utah.edu any + * improvements that they make and grant CSL redistribution rights. + * + * Author: Bryan Ford, University of Utah CSL + */ +/* This is a server presentation file. */ + +#define KERNEL_SERVER 1 + +#include diff --git a/ipc/notify.defs b/ipc/notify.defs new file mode 100644 index 0000000..db059b8 --- /dev/null +++ b/ipc/notify.defs @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2015 Free Software Foundation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* We use custom functions to send notifications. These functions can + be found in `ipc_notify.c'. We use this file merely to produce the + list of message ids. */ + +#include diff --git a/ipc/port.h b/ipc/port.h new file mode 100644 index 0000000..c85685d --- /dev/null +++ b/ipc/port.h @@ -0,0 +1,106 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University. + * Copyright (c) 1993,1994 The University of Utah and + * the Computer Systems Laboratory (CSL). + * 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, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF + * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM 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. + */ +/* + */ +/* + * File: ipc/port.h + * Author: Rich Draves + * Date: 1989 + * + * Implementation specific complement to mach/port.h. + */ + +#ifndef _IPC_PORT_H_ +#define _IPC_PORT_H_ + +#include +#include + +/* + * mach_port_name_t must be an unsigned type. Port values + * have two parts, a generation number and an index. + * These macros encapsulate all knowledge of how + * a mach_port_name_t is laid out. + * + * If the size of generation numbers changes, + * be sure to update IE_BITS_GEN_MASK and friends + * in ipc/ipc_entry.h. + */ + +#if PORT_GENERATIONS +#define MACH_PORT_INDEX(name) ((name) >> 8) +#define MACH_PORT_GEN(name) (((name) & 0xff) << 24) +#define MACH_PORT_MAKE(index, gen) (((index) << 8) | ((gen) >> 24)) +#else +#define MACH_PORT_INDEX(name) (name) +#define MACH_PORT_GEN(name) 0 +#define MACH_PORT_MAKE(index, gen) (index) +#endif + +#define MACH_PORT_NGEN(name) MACH_PORT_MAKE(0, MACH_PORT_GEN(name)) +#define MACH_PORT_MAKEB(index, bits) MACH_PORT_MAKE(index, IE_BITS_GEN(bits)) + +/* + * Typedefs for code cleanliness. These must all have + * the same (unsigned) type as mach_port_name_t. + */ + +typedef mach_port_name_t mach_port_gen_t; /* generation numbers */ + + +#define MACH_PORT_UREFS_MAX ((mach_port_urefs_t) ((1 << 16) - 1)) + +#define MACH_PORT_UREFS_OVERFLOW(urefs, delta) \ + (((delta) > 0) && \ + ((((urefs) + (delta)) <= (urefs)) || \ + (((urefs) + (delta)) > MACH_PORT_UREFS_MAX))) + +#define MACH_PORT_UREFS_UNDERFLOW(urefs, delta) \ + (((delta) < 0) && (-(delta) > (urefs))) + + +static inline mach_port_t invalid_name_to_port(mach_port_name_t name) +{ + if (name == MACH_PORT_NAME_NULL) + return MACH_PORT_NULL; + if (name == MACH_PORT_NAME_DEAD) + return MACH_PORT_DEAD; + panic("invalid_name_to_port() called with a valid port"); +} + +static inline mach_port_name_t invalid_port_to_name(mach_port_t port) +{ + if (port == MACH_PORT_NULL) + return MACH_PORT_NAME_NULL; + if (port == MACH_PORT_DEAD) + return MACH_PORT_NAME_DEAD; + panic("invalid_port_to_name() called with a valid name"); +} + +#endif /* _IPC_PORT_H_ */ diff --git a/kern/.gitignore b/kern/.gitignore new file mode 100644 index 0000000..72bccc6 --- /dev/null +++ b/kern/.gitignore @@ -0,0 +1,2 @@ +exc.none.defs.c +exc.none.msgids diff --git a/kern/act.c b/kern/act.c new file mode 100644 index 0000000..3819ef3 --- /dev/null +++ b/kern/act.c @@ -0,0 +1,1118 @@ +/* + * Copyright (c) 1993,1994 The University of Utah and + * the Computer Systems Laboratory (CSL). 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. + * + * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS + * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF + * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * CSL requests users of this software to return to csl-dist@cs.utah.edu any + * improvements that they make and grant CSL redistribution rights. + * + * Author: Bryan Ford, University of Utah CSL + */ +/* + * File: act.c + * + * Activation management routines + * + */ + +#ifdef MIGRATING_THREADS + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ipc_target.h" + +static void special_handler(ReturnHandler *rh, struct Act *act); + +#ifdef ACT_STATIC_KLUDGE +#undef ACT_STATIC_KLUDGE +#define ACT_STATIC_KLUDGE 300 +#endif + +#ifndef ACT_STATIC_KLUDGE +static struct kmem_cache act_cache; +#else +static Act *act_freelist; +static Act free_acts[ACT_STATIC_KLUDGE]; +#endif + +/* This is a rather special activation + which resides at the top and bottom of every thread. + When the last "real" activation on a thread is destroyed, + the null_act on the bottom gets invoked, destroying the thread. + At the top, the null_act acts as an "invalid" cached activation, + which will always fail the cached-activation test on RPC paths. + + As you might expect, most of its members have no particular value. + alerts is zero. */ +Act null_act; + +void +global_act_init(void) +{ +#ifndef ACT_STATIC_KLUDGE + kmem_cache_init(&act_cache, "Act", sizeof(struct Act), 0, + NULL, 0); +#else + int i; + +printf("activations: [%x-%x]\n", &free_acts[0], &free_acts[ACT_STATIC_KLUDGE]); + act_freelist = &free_acts[0]; + free_acts[0].ipt_next = 0; + for (i = 1; i < ACT_STATIC_KLUDGE; i++) { + free_acts[i].ipt_next = act_freelist; + act_freelist = &free_acts[i]; + } + /* XXX simple_lock_init(&act_freelist->lock); */ +#endif + +#if 0 + simple_lock_init(&null_act.lock); + refcount_init(&null_act.ref_count, 1); +#endif + + act_machine_init(); +} + +/* Create a new activation in a specific task. + Locking: Task */ +kern_return_t act_create(task_t task, vm_offset_t user_stack, + vm_offset_t user_rbuf, vm_size_t user_rbuf_size, + struct Act **new_act) +{ + Act *act; + +#ifndef ACT_STATIC_KLUDGE + act = (Act*)kmem_cache_alloc(&act_cache); + if (act == 0) + return(KERN_RESOURCE_SHORTAGE); +#else + /* XXX ipt_lock(act_freelist); */ + act = act_freelist; + if (act == 0) panic("out of activations"); + act_freelist = act->ipt_next; + /* XXX ipt_unlock(act_freelist); */ + act->ipt_next = 0; +#endif + memset(act, 0, sizeof(*act)); /*XXX shouldn't be needed */ + +#ifdef DEBUG + act->lower = act->higher = 0; +#endif + + /* Start with one reference for being active, another for the caller */ + simple_lock_init(&act->lock); + refcount_init(&act->ref_count, 2); + + /* Latch onto the task. */ + act->task = task; + task_reference(task); + + /* Other simple setup */ + act->ipt = 0; + act->thread = 0; + act->suspend_count = 0; + act->active = 1; + act->handlers = 0; + + /* The special_handler will always be last on the returnhandlers list. */ + act->special_handler.next = 0; + act->special_handler.handler = special_handler; + + ipc_act_init(task, act); + act_machine_create(task, act, user_stack, user_rbuf, user_rbuf_size); + + task_lock(task); + + /* Chain the act onto the task's list */ + act->task_links.next = task->acts.next; + act->task_links.prev = &task->acts; + task->acts.next->prev = &act->task_links; + task->acts.next = &act->task_links; + task->act_count++; + + task_unlock(task); + + *new_act = act; + return KERN_SUCCESS; +} + +/* This is called when an act's ref_count drops to zero. + This can only happen when thread is zero (not in use), + ipt is zero (not attached to any ipt), + and active is false (terminated). */ +static void act_free(Act *inc) +{ + act_machine_destroy(inc); + ipc_act_destroy(inc); + + /* Drop the task reference. */ + task_deallocate(inc->task); + + /* Put the act back on the act cache */ +#ifndef ACT_STATIC_KLUDGE + kmem_cache_free(&act_cache, (vm_offset_t)inc); +#else + /* XXX ipt_lock(act_freelist); */ + inc->ipt_next = act_freelist; + act_freelist = inc; + /* XXX ipt_unlock(act_freelist); */ +#endif +} + +void act_deallocate(Act *inc) +{ + refcount_drop(&inc->ref_count, act_free(inc)); +} + +/* Attach an act to the top of a thread ("push the stack"). + The thread must be either the current one or a brand-new one. + Assumes the act is active but not in use. + Assumes that if it is attached to an ipt (i.e. the ipt pointer is nonzero), + the act has already been taken off the ipt's list. + + Already locked: cur_thread, act */ +void act_attach(Act *act, thread_t thread, unsigned init_alert_mask) +{ + Act *lower; + + act->thread = thread; + + /* The thread holds a reference to the activation while using it. */ + refcount_take(&act->ref_count); + + /* XXX detach any cached activations from above the target */ + + /* Chain the act onto the thread's act stack. */ + lower = thread->top_act; + act->lower = lower; + lower->higher = act; + thread->top_act = act; + + act->alert_mask = init_alert_mask; + act->alerts = lower->alerts & init_alert_mask; +} + +/* Remove the current act from the top of the current thread ("pop the stack"). + Return it to the ipt it lives on, if any. + Locking: Thread > Act(not on ipt) > ipc_target */ +void act_detach(Act *cur_act) +{ + thread_t cur_thread = cur_act->thread; + + thread_lock(cur_thread); + act_lock(cur_act); + + /* Unlink the act from the thread's act stack */ + cur_thread->top_act = cur_act->lower; + cur_act->thread = 0; +#ifdef DEBUG + cur_act->lower = cur_act->higher = 0; +#endif + + thread_unlock(cur_thread); + + /* Return it to the ipt's list */ + if (cur_act->ipt) + { + ipt_lock(cur_act->ipt); + cur_act->ipt_next = cur_act->ipt->ipt_acts; + cur_act->ipt->ipt_acts = cur_act; + ipt_unlock(cur_act->ipt); +#if 0 + printf(" return to ipt %x\n", cur_act->ipt); +#endif + } + + act_unlock(cur_act); + + /* Drop the act reference taken for being in use. */ + refcount_drop(&cur_act->ref_count, act_free(cur_act)); +} + + + +/*** Activation control support routines ***/ + +/* This is called by system-dependent code + when it detects that act->handlers is non-null + while returning into user mode. + Activations linked onto an ipt always have null act->handlers, + so RPC entry paths need not check it. + + Locking: Act */ +void act_execute_returnhandlers(void) +{ + Act *act = current_act(); + +#if 0 + printf("execute_returnhandlers\n"); +#endif + while (1) { + ReturnHandler *rh; + + /* Grab the next returnhandler */ + act_lock(act); + rh = act->handlers; + if (!rh) { + act_unlock(act); + return; + } + act->handlers = rh->next; + act_unlock(act); + + /* Execute it */ + (*rh->handler)(rh, act); + } +} + +/* Try to nudge an act into executing its returnhandler chain. + Ensures that the activation will execute its returnhandlers + before it next executes any of its user-level code. + Also ensures that it is safe to break the thread's activation chain + immediately above this activation, + by rolling out of any outstanding two-way-optimized RPC. + + The target activation is not necessarily active + or even in use by a thread. + If it isn't, this routine does nothing. + + Already locked: Act */ +static void act_nudge(struct Act *act) +{ + /* If it's suspended, wake it up. */ + thread_wakeup(&act->suspend_count); + + /* Do a machine-dependent low-level nudge. + If we're on a multiprocessor, + this may mean sending an interprocessor interrupt. + In any case, it means rolling out of two-way-optimized RPC paths. */ + act_machine_nudge(act); +} + +/* Install the special returnhandler that handles suspension and termination, + if it hasn't been installed already. + + Already locked: Act */ +static void install_special_handler(struct Act *act) +{ + ReturnHandler **rh; + + /* The work handler must always be the last ReturnHandler on the list, + because it can do tricky things like detach the act. */ + for (rh = &act->handlers; *rh; rh = &(*rh)->next); + if (rh != &act->special_handler.next) { + *rh = &act->special_handler; + } + + /* Nudge the target activation, + to ensure that it will see the returnhandler we're adding. */ + act_nudge(act); +} + +/* Locking: Act */ +static void special_handler(ReturnHandler *rh, struct Act *cur_act) +{ + retry: + + act_lock(cur_act); + + /* If someone has killed this invocation, + invoke the return path with a terminated exception. */ + if (!cur_act->active) { + act_unlock(cur_act); + act_machine_return(KERN_TERMINATED); + /* XXX should just set the activation's reentry_routine + and then return from special_handler(). + The magic reentry_routine should just pop its own activation + and chain to the reentry_routine of the _lower_ activation. + If that lower activation is the null_act, + the thread will then be terminated. */ + } + + /* If we're suspended, go to sleep and wait for someone to wake us up. */ + if (cur_act->suspend_count) { + act_unlock(cur_act); + /* XXX mp unsafe */ + thread_wait((int)&cur_act->suspend_count, FALSE); + + act_lock(cur_act); + + /* If we're still (or again) suspended, + go to sleep again after executing any new returnhandlers that may have appeared. */ + if (cur_act->suspend_count) + install_special_handler(cur_act); + } + + act_unlock(cur_act); +} + +#if 0 /************************ OLD SEMI-OBSOLETE CODE *********************/ +static __dead void act_throughcall_return(Act *act) +{ + /* Done - destroy the act and return */ + act_detach(act); + act_terminate(act); + act_deallocate(act); + + /* XXX */ + thread_terminate_self(); +} + +__dead void act_throughcall(task_t task, void (*infunc)()) +{ + thread_t thread = current_thread(); + Act *act; + ReturnHandler rh; + int rc; + + rc = act_create(task, 0, 0, 0, &act); + if (rc) return rc; + + act->return_routine = act_throughcall_return; + + thread_lock(thread); + act_lock(act); + + act_attach(thread, act, 0); + + rh.handler = infunc; + rh.next = act->handlers; + act->handlers = &rh; + + act_unlock(act); + thread_unlock(thread); + + /* Call through the act into the returnhandler list */ + act_machine_throughcall(act); +} + + +/* Grab an act from the specified pool, to pass to act_upcall. + Returns with the act locked, since it's in an inconsistent state + (not on its ipt but not on a thread either). + Returns null if no acts are available on the ipt. + + Locking: ipc_target > Act(on ipt) */ +Act *act_grab(struct ipc_target *ipt) +{ + Act *act; + + ipt_lock(ipt); + + retry: + + /* Pull an act off the ipt's list. */ + act = ipt->acts; + if (!act) + goto none_avail; + ipt->acts = act->ipt_next; + + act_lock(act); + + /* If it's been terminated, drop it and get another one. */ + if (!act->active) { +#if 0 + printf("dropping terminated act %08x\n", act); +#endif + /* XXX ipt_deallocate(ipt); */ + act->ipt = 0; + act_unlock(act); + act_deallocate(act); + goto retry; + } + +none_avail: + ipt_unlock(ipt); + + return act; +} + +/* Try to make an upcall with an act on the specified ipt. + If the ipt is empty, returns KERN_RESOURCE_SHORTAGE. XXX??? + + Locking: ipc_target > Act > Thread */ +kern_return_t act_upcall(struct Act *act, unsigned init_alert_mask, + vm_offset_t user_entrypoint, vm_offset_t user_data) +{ + thread_t cur_thread = current_thread(); + int rc; + + /* XXX locking */ + + act_attach(cur_thread, act, init_alert_mask); + + /* Make the upcall into the destination task */ + rc = act_machine_upcall(act, user_entrypoint, user_data); + + /* Done - detach the act and return */ + act_detach(act); + + return rc; +} +#endif /************************ END OF OLD SEMI-OBSOLETE CODE *********************/ + + + + +/*** Act service routines ***/ + +/* Lock this act and its current thread. + We can only find the thread from the act + and the thread must be locked before the act, + requiring a little icky juggling. + + If the thread is not currently on any thread, + returns with only the act locked. + + Note that this routine is not called on any performance-critical path. + It is only for explicit act operations + which don't happen often. + + Locking: Thread > Act */ +static thread_t act_lock_thread(Act *act) +{ + thread_t thread; + + retry: + + /* Find the thread */ + act_lock(act); + thread = act->thread; + if (thread == 0) + { + act_unlock(act); + return 0; + } + thread_reference(thread); + act_unlock(act); + + /* Lock the thread and re-lock the act, + and make sure the thread didn't change. */ + thread_lock(thread); + act_lock(act); + if (act->thread != thread) + { + act_unlock(act); + thread_unlock(thread); + thread_deallocate(thread); + goto retry; + } + + thread_deallocate(thread); + + return thread; +} + +/* Already locked: act->task + Locking: Task > Act */ +kern_return_t act_terminate_task_locked(struct Act *act) +{ + act_lock(act); + + if (act->active) + { + /* Unlink the act from the task's act list, + so it doesn't appear in calls to task_acts and such. + The act still keeps its ref on the task, however, + until it loses all its own references and is freed. */ + act->task_links.next->prev = act->task_links.prev; + act->task_links.prev->next = act->task_links.next; + act->task->act_count--; + + /* Remove it from any ipc_target. XXX is this right? */ + act_set_target(act, 0); + + /* This will allow no more control operations on this act. */ + act->active = 0; + + /* When the special_handler gets executed, + it will see the terminated condition and exit immediately. */ + install_special_handler(act); + + /* Drop the act reference taken for being active. + (There is still at least one reference left: the one we were passed.) */ + act_deallocate(act); + } + + act_unlock(act); + + return KERN_SUCCESS; +} + +/* Locking: Task > Act */ +kern_return_t act_terminate(struct Act *act) +{ + task_t task = act->task; + kern_return_t rc; + + /* act->task never changes, + so we can read it before locking the act. */ + task_lock(act->task); + + rc = act_terminate_task_locked(act); + + task_unlock(act->task); + + return rc; +} + +/* If this Act is on a Thread and is not the topmost, + yank it and everything below it off of the thread's stack + and put it all on a new thread forked from the original one. + May fail due to resource shortage, but can always be retried. + + Locking: Thread > Act */ +kern_return_t act_yank(Act *act) +{ + thread_t thread = act_lock_thread(act); + +#if 0 + printf("act_yank inc %08x thread %08x\n", act, thread); +#endif + if (thread) + { + if (thread->top_act != act) + { + printf("detaching act %08x from thread %08x\n", act, thread); + + /* Nudge the activation into a clean point for detachment. */ + act_nudge(act); + + /* Now detach the activation + and give the orphan its own flow of control. */ + /*XXX*/ + } + + thread_unlock(thread); + } + act_unlock(act); + + /* Ask the thread to return as quickly as possible, + because its results are now useless. */ + act_abort(act); + + return KERN_SUCCESS; +} + +/* Assign an activation to a specific ipc_target. + Fails if the activation is already assigned to another pool. + If ipt == 0, we remove the from its ipt. + + Locking: Act(not on ipt) > ipc_target > Act(on ipt) */ +kern_return_t act_set_target(Act *act, struct ipc_target *ipt) +{ + act_lock(act); + + if (ipt == 0) + { + Act **lact; + + ipt = act->ipt; + if (ipt == 0) + return; + + /* XXX This is a violation of the locking order. */ + ipt_lock(ipt); + for (lact = &ipt->ipt_acts; *lact; lact = &((*lact)->ipt_next)) + if (act == *lact) + { + *lact = act->ipt_next; + break; + } + ipt_unlock(ipt); + + act->ipt = 0; + /* XXX ipt_deallocate(ipt); */ + act_deallocate(act); + return; + } + if (act->ipt != ipt) + { + if (act->ipt != 0) + { + act_unlock(act); + return KERN_FAILURE; /*XXX*/ + } + act->ipt = ipt; + ipt->ipt_type |= IPT_TYPE_MIGRATE_RPC; + + /* They get references to each other. */ + act_reference(act); + ipt_reference(ipt); + + /* If it is available, + add it to the ipt's available-activation list. */ + if ((act->thread == 0) && (act->suspend_count == 0)) + { + ipt_lock(ipt); + act->ipt_next = ipt->ipt_acts; + act->ipt->ipt_acts = act; + ipt_unlock(ipt); + } + } + act_unlock(act); + + return KERN_SUCCESS; +} + +/* Register an alert from this activation. + Each set bit is propagated upward from (but not including) this activation, + until the top of the chain is reached or the bit is masked. + + Locking: Thread > Act */ +kern_return_t act_alert(struct Act *act, unsigned alerts) +{ + thread_t thread = act_lock_thread(act); + +#if 0 + printf("act_alert %08x: %08x\n", act, alerts); +#endif + if (thread) + { + struct Act *act_up = act; + while ((alerts) && (act_up != thread->top_act)) + { + act_up = act_up->higher; + alerts &= act_up->alert_mask; + act_up->alerts |= alerts; + } + + /* XXX If we reach the top, and it is blocked in glue code, do something. */ + + thread_unlock(thread); + } + act_unlock(act); + + return KERN_SUCCESS; +} + +/* Locking: Thread > Act */ +kern_return_t act_abort(struct Act *act) +{ + return act_alert(act, ALERT_ABORT_STRONG); +} + +/* Locking: Thread > Act */ +kern_return_t act_abort_safely(struct Act *act) +{ + return act_alert(act, ALERT_ABORT_SAFE); +} + +/* Locking: Thread > Act */ +kern_return_t act_alert_mask(struct Act *act, unsigned alert_mask) +{ + panic("act_alert_mask\n"); + return KERN_SUCCESS; +} + +/* Locking: Thread > Act */ +kern_return_t act_suspend(struct Act *act) +{ + thread_t thread = act_lock_thread(act); + kern_return_t rc = KERN_SUCCESS; + +#if 0 + printf("act_suspend %08x\n", act); +#endif + if (act->active) + { + if (act->suspend_count++ == 0) + { + /* XXX remove from ipt */ + install_special_handler(act); + act_nudge(act); + } + } + else + rc = KERN_TERMINATED; + + if (thread) + thread_unlock(thread); + act_unlock(act); + + return rc; +} + +/* Locking: Act */ +kern_return_t act_resume(struct Act *act) +{ +#if 0 + printf("act_resume %08x from %d\n", act, act->suspend_count); +#endif + + act_lock(act); + if (!act->active) + { + act_unlock(act); + return KERN_TERMINATED; + } + + if (act->suspend_count > 0) { + if (--act->suspend_count == 0) { + thread_wakeup(&act->suspend_count); + /* XXX return to ipt */ + } + } + + act_unlock(act); + + return KERN_SUCCESS; +} + +typedef struct GetSetState { + struct ReturnHandler rh; + int flavor; + void *state; + int *pcount; + int result; +} GetSetState; + +/* Locking: Thread */ +kern_return_t get_set_state(struct Act *act, int flavor, void *state, int *pcount, + void (*handler)(ReturnHandler *rh, struct Act *act)) +{ + GetSetState gss; + + /* Initialize a small parameter structure */ + gss.rh.handler = handler; + gss.flavor = flavor; + gss.state = state; + gss.pcount = pcount; + + /* Add it to the act's return handler list */ + act_lock(act); + gss.rh.next = act->handlers; + act->handlers = &gss.rh; + + act_nudge(act); + + act_unlock(act); + /* XXX mp unsafe */ + thread_wait((int)&gss, 0); /* XXX could be interruptible */ + + return gss.result; +} + +static void get_state_handler(ReturnHandler *rh, struct Act *act) +{ + GetSetState *gss = (GetSetState*)rh; + + gss->result = act_machine_get_state(act, gss->flavor, gss->state, gss->pcount); + thread_wakeup((int)gss); +} + +/* Locking: Thread */ +kern_return_t act_get_state(struct Act *act, int flavor, natural_t *state, natural_t *pcount) +{ + return get_set_state(act, flavor, state, pcount, get_state_handler); +} + +static void set_state_handler(ReturnHandler *rh, struct Act *act) +{ + GetSetState *gss = (GetSetState*)rh; + + gss->result = act_machine_set_state(act, gss->flavor, gss->state, *gss->pcount); + thread_wakeup((int)gss); +} + +/* Locking: Thread */ +kern_return_t act_set_state(struct Act *act, int flavor, natural_t *state, natural_t count) +{ + return get_set_state(act, flavor, state, &count, set_state_handler); +} + + + +/*** backward compatibility hacks ***/ + +#include +#include +#include + +kern_return_t act_thread_info(Act *act, int flavor, + thread_info_t thread_info_out, unsigned *thread_info_count) +{ + return thread_info(act->thread, flavor, thread_info_out, thread_info_count); +} + +kern_return_t +act_thread_assign(Act *act, processor_set_t new_pset) +{ + return thread_assign(act->thread, new_pset); +} + +kern_return_t +act_thread_assign_default(Act *act) +{ + return thread_assign_default(act->thread); +} + +kern_return_t +act_thread_get_assignment(Act *act, processor_set_t *pset) +{ + return thread_get_assignment(act->thread, pset); +} + +kern_return_t +act_thread_priority(Act *act, int priority, boolean_t set_max) +{ + return thread_priority(act->thread, priority, set_max); +} + +kern_return_t +act_thread_max_priority(Act *act, processor_set_t *pset, int max_priority) +{ + return thread_max_priority(act->thread, pset, max_priority); +} + +kern_return_t +act_thread_policy(Act *act, int policy, int data) +{ + return thread_policy(act->thread, policy, data); +} + +kern_return_t +act_thread_wire(struct host *host, Act *act, boolean_t wired) +{ + return thread_wire(host, act->thread, wired); +} + +kern_return_t +act_thread_depress_abort(Act *act) +{ + return thread_depress_abort(act->thread); +} + +/* + * Routine: act_get_special_port [kernel call] + * Purpose: + * Clones a send right for one of the thread's + * special ports. + * Conditions: + * Nothing locked. + * Returns: + * KERN_SUCCESS Extracted a send right. + * KERN_INVALID_ARGUMENT The thread is null. + * KERN_FAILURE The thread is dead. + * KERN_INVALID_ARGUMENT Invalid special port. + */ + +kern_return_t +act_get_special_port(Act *act, int which, ipc_port_t *portp) +{ + ipc_port_t *whichp; + ipc_port_t port; + +#if 0 + printf("act_get_special_port\n"); +#endif + if (act == 0) + return KERN_INVALID_ARGUMENT; + + switch (which) { + case THREAD_KERNEL_PORT: + whichp = &act->self_port; + break; + + case THREAD_EXCEPTION_PORT: + whichp = &act->exception_port; + break; + + default: + return KERN_INVALID_ARGUMENT; + } + + thread_lock(act->thread); + + if (act->self_port == IP_NULL) { + thread_unlock(act->thread); + return KERN_FAILURE; + } + + port = ipc_port_copy_send(*whichp); + thread_unlock(act->thread); + + *portp = port; + return KERN_SUCCESS; +} + +/* + * Routine: act_set_special_port [kernel call] + * Purpose: + * Changes one of the thread's special ports, + * setting it to the supplied send right. + * Conditions: + * Nothing locked. If successful, consumes + * the supplied send right. + * Returns: + * KERN_SUCCESS Changed the special port. + * KERN_INVALID_ARGUMENT The thread is null. + * KERN_FAILURE The thread is dead. + * KERN_INVALID_ARGUMENT Invalid special port. + */ + +kern_return_t +act_set_special_port(Act *act, int which, ipc_port_t port) +{ + ipc_port_t *whichp; + ipc_port_t old; + +#if 0 + printf("act_set_special_port\n"); +#endif + if (act == 0) + return KERN_INVALID_ARGUMENT; + + switch (which) { + case THREAD_KERNEL_PORT: + whichp = &act->self_port; + break; + + case THREAD_EXCEPTION_PORT: + whichp = &act->exception_port; + break; + + default: + return KERN_INVALID_ARGUMENT; + } + + thread_lock(act->thread); + if (act->self_port == IP_NULL) { + thread_unlock(act->thread); + return KERN_FAILURE; + } + + old = *whichp; + *whichp = port; + thread_unlock(act->thread); + + if (IP_VALID(old)) + ipc_port_release_send(old); + return KERN_SUCCESS; +} + +/* + * XXX lame, non-blocking ways to get/set state. + * Return thread's machine-dependent state. + */ +kern_return_t +act_get_state_immediate( + Act *act, + int flavor, + void *old_state, /* pointer to OUT array */ + unsigned int *old_state_count) /*IN/OUT*/ +{ + kern_return_t ret; + + act_lock(act); + /* not the top activation, return current state */ + if (act->thread && act->thread->top_act != act) { + ret = act_machine_get_state(act, flavor, + old_state, old_state_count); + act_unlock(act); + return ret; + } + act_unlock(act); + + /* not sure this makes sense */ + return act_get_state(act, flavor, old_state, old_state_count); +} + +/* + * Change thread's machine-dependent state. + */ +kern_return_t +act_set_state_immediate( + Act *act, + int flavor, + void *new_state, + unsigned int new_state_count) +{ + kern_return_t ret; + + act_lock(act); + /* not the top activation, set it now */ + if (act->thread && act->thread->top_act != act) { + ret = act_machine_set_state(act, flavor, + new_state, new_state_count); + act_unlock(act); + return ret; + } + act_unlock(act); + + /* not sure this makes sense */ + return act_set_state(act, flavor, new_state, new_state_count); +} + +void act_count(void) +{ + int i; + Act *act; + static int amin = ACT_STATIC_KLUDGE; + + i = 0; + for (act = act_freelist; act; act = act->ipt_next) + i++; + if (i < amin) + amin = i; + printf("%d of %d activations in use, %d max\n", + ACT_STATIC_KLUDGE-i, ACT_STATIC_KLUDGE, ACT_STATIC_KLUDGE-amin); +} + +void dump_act(act) + Act *act; +{ + act_count(); + kact_count(); + while (act) { + printf("%08.8x: thread=%x, task=%x, hi=%x, lo=%x, ref=%x\n", + act, act->thread, act->task, + act->higher, act->lower, act->ref_count); + printf("\talerts=%x, mask=%x, susp=%x, active=%x\n", + act->alerts, act->alert_mask, + act->suspend_count, act->active); + machine_dump_act(&act->mact); + if (act == act->lower) + break; + act = act->lower; + } +} + +#ifdef ACTWATCH +Act * +get_next_act(int sp) +{ + static int i; + Act *act; + + while (1) { + if (i == ACT_STATIC_KLUDGE) { + i = 0; + return 0; + } + act = &free_acts[i]; + i++; + if (act->mact.space == sp) + return act; + } +} +#endif /* ACTWATCH */ + +#endif /* MIGRATING_THREADS */ diff --git a/kern/act.h b/kern/act.h new file mode 100644 index 0000000..f46f53a --- /dev/null +++ b/kern/act.h @@ -0,0 +1,192 @@ +/* + * Copyright (c) 1993,1994 The University of Utah and + * the Computer Systems Laboratory (CSL). 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. + * + * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS + * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF + * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * CSL requests users of this software to return to csl-dist@cs.utah.edu any + * improvements that they make and grant CSL redistribution rights. + * + * Author: Bryan Ford, University of Utah CSL + */ +/* + * File: act.h + * + * This defines the Act structure, + * which is the kernel representation of a user-space activation. + * + */ + +#ifndef _KERN_ACT_H_ +#define _KERN_ACT_H_ + +#ifdef MIGRATING_THREADS + +#ifndef __dead /* XXX */ +#define __dead +#endif + +#include +#include +#include +#include +#include + +struct task; +struct thread; +struct Act; + + +struct ReturnHandler { + struct ReturnHandler *next; + void (*handler)(struct ReturnHandler *rh, struct Act *act); +}; +typedef struct ReturnHandler ReturnHandler; + + + +struct Act { + + /*** Task linkage ***/ + + /* Links for task's circular list of activations. + The activation is only on the task's activation list while active. + Must be first. */ + queue_chain_t task_links; + + /* Reference to the task this activation is in. + This is constant as long as the activation is allocated. */ + struct task *task; + + + + /*** Machine-dependent state ***/ + /* XXX should be first to allow maximum flexibility to MD code */ + MachineAct mact; + + + + /*** Consistency ***/ + RefCount ref_count; + decl_simple_lock_data(,lock) + + + + /*** ipc_target-related stuff ***/ + + /* ActPool this activation normally lives on, zero if none. + The activation and actpool hold references to each other as long as this is nonzero + (even when the activation isn't actually on the actpool's list). */ + struct ipc_target *ipt; + + /* Link on the ipt's list of activations. + The activation is only actually on the ipt's list (and hence this is valid) + when we're not in use (thread == 0) and not suspended (suspend_count == 0). */ + struct Act *ipt_next; + + + + /*** Thread linkage ***/ + + /* Thread this activation is in, zero if not in use. + The thread holds a reference on the activation while this is nonzero. */ + struct thread *thread; + + /* The rest in this section is only valid when thread is nonzero. */ + + /* Next higher and next lower activation on the thread's activation stack. + For a topmost activation or the null_act, higher is undefined. + The bottommost activation is always the null_act. */ + struct Act *higher, *lower; + + /* Alert bits pending at this activation; + some of them may have propagated from lower activations. */ + unsigned alerts; + + /* Mask of alert bits to be allowed to pass through from lower levels. */ + unsigned alert_mask; + + + + /*** Control information ***/ + + /* Number of outstanding suspensions on this activation. */ + int suspend_count; + + /* This is normally true, but is set to false when the activation is terminated. */ + int active; + + /* Chain of return handlers to be called + before the thread is allowed to return to this invocation */ + ReturnHandler *handlers; + + /* A special ReturnHandler attached to the above chain to handle suspension and such */ + ReturnHandler special_handler; + + + + /* Special ports attached to this activation */ + struct ipc_port *self; /* not a right, doesn't hold ref */ + struct ipc_port *self_port; /* a send right */ + struct ipc_port *exception_port; /* a send right */ + struct ipc_port *syscall_port; /* a send right */ +}; +typedef struct Act Act; +typedef struct Act *act_t; +typedef mach_port_t *act_array_t; + +#define ACT_NULL ((Act*)0) + + +/* Exported to world */ +kern_return_t act_create(struct task *task, vm_offset_t user_stack, vm_offset_t user_rbuf, vm_size_t user_rbuf_size, struct Act **new_act); +kern_return_t act_alert_mask(struct Act *act, unsigned alert_mask); +kern_return_t act_alert(struct Act *act, unsigned alerts); +kern_return_t act_abort(struct Act *act); +kern_return_t act_abort_safely(struct Act *act); +kern_return_t act_terminate(struct Act *act); +kern_return_t act_suspend(struct Act *act); +kern_return_t act_resume(struct Act *act); +kern_return_t act_get_state(struct Act *act, int flavor, + natural_t *state, natural_t *pcount); +kern_return_t act_set_state(struct Act *act, int flavor, + natural_t *state, natural_t count); + +#define act_lock(act) simple_lock(&(act)->lock) +#define act_unlock(act) simple_unlock(&(act)->lock) + +#define act_reference(act) refcount_take(&(act)->ref_count) +void act_deallocate(struct Act *act); + +/* Exported to startup.c */ +void act_init(void); + +/* Exported to task.c */ +kern_return_t act_terminate_task_locked(struct Act *act); + +/* Exported to thread.c */ +extern Act null_act; + +/* Exported to machine-dependent activation code */ +void act_execute_returnhandlers(void); + + + +/* System-dependent functions */ +kern_return_t act_machine_create(struct task *task, Act *inc, vm_offset_t user_stack, vm_offset_t user_rbuf, vm_size_t user_rbuf_size); +void act_machine_destroy(Act *inc); +kern_return_t act_machine_set_state(Act *inc, int flavor, int *tstate, unsigned count); +kern_return_t act_machine_get_state(Act *inc, int flavor, int *tstate, unsigned *count); + + + +#endif /* MIGRATING_THREADS */ +#endif /* _KERN_ACT_H_ */ diff --git a/kern/assert.h b/kern/assert.h new file mode 100644 index 0000000..fed2a20 --- /dev/null +++ b/kern/assert.h @@ -0,0 +1,54 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988,1987 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. + */ + +#ifndef _KERN_ASSERT_H_ +#define _KERN_ASSERT_H_ + +/* assert.h 4.2 85/01/21 */ + +#include + +#ifndef NDEBUG +#define MACH_ASSERT 1 +#endif + +#if MACH_ASSERT +extern void Assert(const char *exp, const char *filename, int line, + const char *fun) __attribute__ ((noreturn)); + +#define assert(ex) \ + (likely(ex) \ + ? (void) (0) \ + : Assert (#ex, __FILE__, __LINE__, __FUNCTION__)) + +#define assert_static(x) assert(x) + +#else /* MACH_ASSERT */ +#define assert(ex) +#define assert_static(ex) +#endif /* MACH_ASSERT */ + +#endif /* _KERN_ASSERT_H_ */ diff --git a/kern/ast.c b/kern/ast.c new file mode 100644 index 0000000..8c514b3 --- /dev/null +++ b/kern/ast.c @@ -0,0 +1,235 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University. + * Copyright (c) 1993,1994 The University of Utah and + * the Computer Systems Laboratory (CSL). + * 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, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF + * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM 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. + */ +/* + * + * This file contains routines to check whether an ast is needed. + * + * ast_check() - check whether ast is needed for interrupt or context + * switch. Usually called by clock interrupt handler. + * + */ + +#include +#include +#include +#include "cpu_number.h" +#include +#include +#include +#include +#include +#include + +#include /* for splsched */ + +#if MACH_FIXPRI +#include +#endif /* MACH_FIXPRI */ + + +volatile ast_t need_ast[NCPUS]; + +void +ast_init(void) +{ +#ifndef MACHINE_AST + int i; + + for (i=0; iidle_thread) { +#ifndef MIGRATING_THREADS + while (thread_should_halt(self)) + thread_halt_self(thread_exception_return); +#endif + + /* + * One of the previous actions might well have + * woken a high-priority thread, so we use + * csw_needed in addition to AST_BLOCK. + */ + + if ((reasons & AST_BLOCK) || + csw_needed(self, current_processor())) { + counter(c_ast_taken_block++); + thread_block(thread_exception_return); + } + } +} + +void +ast_check(void) +{ + int mycpu = cpu_number(); + processor_t myprocessor; + thread_t thread = current_thread(); + run_queue_t rq; + spl_t s = splsched(); + + /* + * Check processor state for ast conditions. + */ + myprocessor = cpu_to_processor(mycpu); + switch(myprocessor->state) { + case PROCESSOR_OFF_LINE: + case PROCESSOR_IDLE: + case PROCESSOR_DISPATCHING: + /* + * No ast. + */ + break; + +#if NCPUS > 1 + case PROCESSOR_ASSIGN: + case PROCESSOR_SHUTDOWN: + /* + * Need ast to force action thread onto processor. + * + * XXX Should check if action thread is already there. + */ + ast_on(mycpu, AST_BLOCK); + break; +#endif /* NCPUS > 1 */ + + case PROCESSOR_RUNNING: + + /* + * Propagate thread ast to processor. If we already + * need an ast, don't look for more reasons. + */ + ast_propagate(thread, mycpu); + if (ast_needed(mycpu)) + break; + + /* + * Context switch check. The csw_needed macro isn't + * used here because the rq->low hint may be wrong, + * and fixing it here avoids an extra ast. + * First check the easy cases. + */ + if (thread->state & TH_SUSP || myprocessor->runq.count > 0) { + ast_on(mycpu, AST_BLOCK); + break; + } + + /* + * Update lazy evaluated runq->low if only timesharing. + */ +#if MACH_FIXPRI + if (myprocessor->processor_set->policies & POLICY_FIXEDPRI) { + if (csw_needed(thread,myprocessor)) { + ast_on(mycpu, AST_BLOCK); + break; + } + else { + /* + * For fixed priority threads, set first_quantum + * so entire new quantum is used. + */ + if (thread->policy == POLICY_FIXEDPRI) + myprocessor->first_quantum = TRUE; + } + } + else { +#endif /* MACH_FIXPRI */ + rq = &(myprocessor->processor_set->runq); + if (!(myprocessor->first_quantum) && (rq->count > 0)) { + queue_t q; + /* + * This is not the first quantum, and there may + * be something in the processor_set runq. + * Check whether low hint is accurate. + */ + q = rq->runq + *(volatile int *)&rq->low; + if (queue_empty(q)) { + int i; + + /* + * Need to recheck and possibly update hint. + */ + runq_lock(rq); + q = rq->runq + rq->low; + if (rq->count > 0) { + for (i = rq->low; i < NRQS; i++) { + if(!(queue_empty(q))) + break; + q++; + } + rq->low = i; + } + runq_unlock(rq); + } + + if (rq->low <= thread->sched_pri) { + ast_on(mycpu, AST_BLOCK); + break; + } + } +#if MACH_FIXPRI + } +#endif /* MACH_FIXPRI */ + break; + + default: + panic("ast_check: Bad processor state (cpu %d processor %p) state: %d", + mycpu, myprocessor, myprocessor->state); + } + + (void) splx(s); +} diff --git a/kern/ast.h b/kern/ast.h new file mode 100644 index 0000000..aded167 --- /dev/null +++ b/kern/ast.h @@ -0,0 +1,139 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University. + * Copyright (c) 1993,1994 The University of Utah and + * the Computer Systems Laboratory (CSL). + * 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, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF + * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM 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. + */ +/* + * kern/ast.h: Definitions for Asynchronous System Traps. + */ + +#ifndef _KERN_AST_H_ +#define _KERN_AST_H_ + +/* + * A CPU takes an AST when it is about to return to user code. + * Instead of going back to user code, it calls ast_taken. + * Machine-dependent code is responsible for maintaining + * a set of reasons for an AST, and passing this set to ast_taken. + */ + +#include +#include +#include + +/* + * Bits for reasons + */ + +#define AST_ZILCH 0x0 +#define AST_HALT 0x1 +#define AST_TERMINATE 0x2 +#define AST_BLOCK 0x4 +#define AST_NETWORK 0x8 +#define AST_NETIPC 0x10 + +#define AST_SCHEDULING (AST_HALT|AST_TERMINATE|AST_BLOCK) + +/* + * Per-thread ASTs are reset at context-switch time. + * machine/ast.h can define MACHINE_AST_PER_THREAD. + */ + +#ifndef MACHINE_AST_PER_THREAD +#define MACHINE_AST_PER_THREAD 0 +#endif + +#define AST_PER_THREAD (AST_HALT | AST_TERMINATE | MACHINE_AST_PER_THREAD) + +typedef unsigned long ast_t; + +extern volatile ast_t need_ast[NCPUS]; + +#ifdef MACHINE_AST +/* + * machine/ast.h is responsible for defining aston and astoff. + */ +#else /* MACHINE_AST */ + +#define aston(mycpu) +#define astoff(mycpu) + +#endif /* MACHINE_AST */ + +extern void ast_taken(void); + +/* + * ast_needed, ast_on, ast_off, ast_context, and ast_propagate + * assume splsched. mycpu is always cpu_number(). It is an + * argument in case cpu_number() is expensive. + */ + +#define ast_needed(mycpu) need_ast[mycpu] + +#define ast_on(mycpu, reasons) \ +MACRO_BEGIN \ + if ((need_ast[mycpu] |= (reasons)) != AST_ZILCH) \ + { aston(mycpu); } \ +MACRO_END + +#define ast_off(mycpu, reasons) \ +MACRO_BEGIN \ + if ((need_ast[mycpu] &= ~(reasons)) == AST_ZILCH) \ + { astoff(mycpu); } \ +MACRO_END + +#define ast_propagate(thread, mycpu) ast_on((mycpu), (thread)->ast) + +#define ast_context(thread, mycpu) \ +MACRO_BEGIN \ + if ((need_ast[mycpu] = \ + (need_ast[mycpu] &~ AST_PER_THREAD) | (thread)->ast) \ + != AST_ZILCH) \ + { aston(mycpu); } \ + else \ + { astoff(mycpu); } \ +MACRO_END + + +#define thread_ast_set(thread, reason) (thread)->ast |= (reason) +#define thread_ast_clear(thread, reason) (thread)->ast &= ~(reason) +#define thread_ast_clear_all(thread) (thread)->ast = AST_ZILCH + +/* + * NOTE: if thread is the current thread, thread_ast_set should + * be followed by ast_propagate(). + */ + +extern void ast_init (void); + +extern void ast_check (void); + +#if NCPUS > 1 +extern void init_ast_check(const processor_t processor); +extern void cause_ast_check(const processor_t processor); +#endif + +#endif /* _KERN_AST_H_ */ diff --git a/kern/atomic.h b/kern/atomic.h new file mode 100644 index 0000000..00da164 --- /dev/null +++ b/kern/atomic.h @@ -0,0 +1,54 @@ +/* Copyright (C) 2017 Free Software Foundation, Inc. + Contributed by Agustina Arzille , 2017. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either + version 2 of the license, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with this program; if not, see + . +*/ + +#ifndef _KERN_ATOMIC_H_ +#define _KERN_ATOMIC_H_ 1 + +/* Atomically compare *PTR with EXP and set it to NVAL if they're equal. + * Evaluates to a boolean, indicating whether the comparison was successful.*/ +#define __atomic_cas_helper(ptr, exp, nval, mo) \ + ({ \ + typeof(exp) __e = (exp); \ + __atomic_compare_exchange_n ((ptr), &__e, (nval), 0, \ + __ATOMIC_##mo, __ATOMIC_RELAXED); \ + }) + +#define atomic_cas_acq(ptr, exp, nval) \ + __atomic_cas_helper (ptr, exp, nval, ACQUIRE) + +#define atomic_cas_rel(ptr, exp, nval) \ + __atomic_cas_helper (ptr, exp, nval, RELEASE) + +#define atomic_cas_seq(ptr, exp, nval) \ + __atomic_cas_helper (ptr, exp, nval, SEQ_CST) + +/* Atomically exchange the value of *PTR with VAL, evaluating to + * its previous value. */ +#define __atomic_swap_helper(ptr, val, mo) \ + __atomic_exchange_n ((ptr), (val), __ATOMIC_##mo) + +#define atomic_swap_acq(ptr, val) \ + __atomic_swap_helper (ptr, val, ACQUIRE) + +#define atomic_swap_rel(ptr, val) \ + __atomic_swap_helper (ptr, val, RELEASE) + +#define atomic_swap_seq(ptr, val) \ + __atomic_swap_helper (ptr, val, SEQ_CST) + +#endif diff --git a/kern/boot_script.c b/kern/boot_script.c new file mode 100644 index 0000000..07ce4b3 --- /dev/null +++ b/kern/boot_script.c @@ -0,0 +1,791 @@ +/* Boot script parser for Mach. */ + +/* Written by Shantanu Goel (goel@cs.columbia.edu). */ + +#include +#include +#include +#include "boot_script.h" +#include "bootstrap.h" + + +/* This structure describes a symbol. */ +struct sym +{ + /* Symbol name. */ + const char *name; + + /* Type of value returned by function. */ + int type; + + /* Symbol value. */ + long val; + + /* For function symbols; type of value returned by function. */ + int ret_type; + + /* For function symbols; if set, execute function at the time + of command execution, not during parsing. A function with + this field set must also have `no_arg' set. Also, the function's + `val' argument will always be NULL. */ + int run_on_exec; +}; + +/* Additional values symbols can take. + These are only used internally. */ +#define VAL_SYM 10 /* symbol table entry */ +#define VAL_FUNC 11 /* function pointer */ + +/* This structure describes an argument. */ +struct arg +{ + /* Argument text copied verbatim. 0 if none. */ + char *text; + + /* Type of value assigned. 0 if none. */ + int type; + + /* Argument value. */ + long val; +}; + +/* List of commands. */ +static struct cmd **cmds = 0; + +/* Amount allocated for `cmds'. */ +static int cmds_alloc = 0; + +/* Next available slot in `cmds'. */ +static int cmds_index = 0; + +/* Symbol table. */ +static struct sym **symtab = 0; + +/* Amount allocated for `symtab'. */ +static int symtab_alloc = 0; + +/* Next available slot in `symtab'. */ +static int symtab_index = 0; + +/* Create a task and suspend it. */ +static int +create_task (struct cmd *cmd, long *val) +{ + int err = boot_script_task_create (cmd); + *val = (long) cmd->task; + return err; +} + +/* Resume a task. */ +static int +resume_task (struct cmd *cmd, const long *val) +{ + return boot_script_task_resume (cmd); +} + +/* Resume a task when the user hits return. */ +static int +prompt_resume_task (struct cmd *cmd, const long *val) +{ + return boot_script_prompt_task_resume (cmd); +} + +/* List of builtin symbols. */ +static struct sym builtin_symbols[] = +{ + { "task-create", VAL_FUNC, (long) create_task, VAL_TASK, 0 }, + { "task-resume", VAL_FUNC, (long) resume_task, VAL_NONE, 1 }, + { "prompt-task-resume", VAL_FUNC, (long) prompt_resume_task, VAL_NONE, 1 }, +}; +#define NUM_BUILTIN (sizeof (builtin_symbols) / sizeof (builtin_symbols[0])) + +/* Free CMD and all storage associated with it. + If ABORTING is set, terminate the task associated with CMD, + otherwise just deallocate the send right. */ +static void +free_cmd (struct cmd *cmd, int aborting) +{ + if (cmd->task) + boot_script_free_task (cmd->task, aborting); + if (cmd->args) + { + int i; + for (i = 0; i < cmd->args_index; i++) + boot_script_free (cmd->args[i], sizeof *cmd->args[i]); + boot_script_free (cmd->args, sizeof cmd->args[0] * cmd->args_alloc); + } + if (cmd->exec_funcs) + boot_script_free (cmd->exec_funcs, + sizeof cmd->exec_funcs[0] * cmd->exec_funcs_alloc); + boot_script_free (cmd, sizeof *cmd); +} + +/* Free all storage allocated by the parser. + If ABORTING is set, terminate all tasks. */ +static void +cleanup (int aborting) +{ + int i; + + for (i = 0; i < cmds_index; i++) + free_cmd (cmds[i], aborting); + boot_script_free (cmds, sizeof cmds[0] * cmds_alloc); + cmds = 0; + cmds_index = cmds_alloc = 0; + + for (i = 0; i < symtab_index; i++) + boot_script_free (symtab[i], sizeof *symtab[i]); + boot_script_free (symtab, sizeof symtab[0] * symtab_alloc); + symtab = 0; + symtab_index = symtab_alloc = 0; +} + +/* Add PTR to the list of pointers PTR_LIST, which + currently has ALLOC amount of space allocated to it, and + whose next available slot is INDEX. If more space + needs to to allocated, INCR is the amount by which + to increase it. Return 0 on success, non-zero otherwise. */ +static int +add_list (void *ptr, void ***ptr_list, int *alloc, int *index, int incr) +{ + if (*index == *alloc) + { + void **p; + + *alloc += incr; + p = boot_script_malloc (*alloc * sizeof (void *)); + if (! p) + { + *alloc -= incr; + return 1; + } + if (*ptr_list) + { + memcpy (p, *ptr_list, *index * sizeof (void *)); + boot_script_free (*ptr_list, (*alloc - incr) * sizeof (void *)); + } + *ptr_list = p; + } + *(*ptr_list + *index) = ptr; + *index += 1; + return 0; +} + +/* Create an argument with TEXT, value type TYPE, and value VAL. + Add the argument to the argument list of CMD. */ +static struct arg * +add_arg (struct cmd *cmd, char *text, int type, long val) +{ + struct arg *arg; + + arg = boot_script_malloc (sizeof (struct arg)); + if (arg) + { + arg->text = text; + arg->type = type; + arg->val = val; + if (add_list (arg, (void ***) &cmd->args, + &cmd->args_alloc, &cmd->args_index, 5)) + { + boot_script_free (arg, sizeof *arg); + return 0; + } + } + return arg; +} + +/* Search for the symbol NAME in the symbol table. */ +static struct sym * +sym_lookup (const char *name) +{ + int i; + + for (i = 0; i < symtab_index; i++) + if (! strcmp (name, symtab[i]->name)) + return symtab[i]; + return 0; +} + +/* Create an entry for symbol NAME in the symbol table. */ +static struct sym * +sym_enter (const char *name) +{ + struct sym *sym; + + sym = boot_script_malloc (sizeof (struct sym)); + if (sym) + { + memset (sym, 0, sizeof (struct sym)); + sym->name = name; + if (add_list (sym, (void ***) &symtab, &symtab_alloc, &symtab_index, 20)) + { + boot_script_free (sym, sizeof *sym); + return 0; + } + } + return sym; +} + +/* Parse the command line CMDLINE. */ +int +boot_script_parse_line (void *hook, char *cmdline) +{ + char *p, *q; + int error; + struct cmd *cmd; + struct arg *arg; + + /* Extract command name. Ignore line if it lacks a command. */ + for (p = cmdline; *p == ' ' || *p == '\t'; p++) + ; + if (*p == '#') + /* Ignore comment line. */ + return 0; + +#if 0 + if (*p && *p != ' ' && *p != '\t' && *p != '\n') + { + printf ("(bootstrap): %s\n", cmdline); + } +#endif + + for (q = p; *q && *q != ' ' && *q != '\t' && *q != '\n'; q++) + ; + if (p == q) + return 0; + + *q = '\0'; + + /* Allocate a command structure. */ + cmd = boot_script_malloc (sizeof (struct cmd)); + if (! cmd) + return BOOT_SCRIPT_NOMEM; + memset (cmd, 0, sizeof (struct cmd)); + cmd->hook = hook; + cmd->path = p; + p = q + 1; + + for (arg = 0;;) + { + if (! arg) + { + /* Skip whitespace. */ + while (*p == ' ' || *p == '\t') + p++; + + /* End of command line. */ + if (! *p || *p == '\n') + { + /* Add command to list. */ + if (add_list (cmd, (void ***) &cmds, + &cmds_alloc, &cmds_index, 10)) + { + error = BOOT_SCRIPT_NOMEM; + goto bad; + } + return 0; + } + } + + /* Look for a symbol. */ + if (arg || (*p == '$' && (*(p + 1) == '{' || *(p + 1) == '('))) + { + char end_char = (*(p + 1) == '{') ? '}' : ')'; + struct sym *sym = 0; + + for (p += 2;;) + { + char c; + unsigned i; + int type; + long val; + struct sym *s; + + /* Parse symbol name. */ + for (q = p; *q && *q != '\n' && *q != end_char && *q != '='; q++) + ; + if (p == q || ! *q || *q == '\n' + || (end_char == '}' && *q != '}')) + { + error = BOOT_SCRIPT_SYNTAX_ERROR; + goto bad; + } + c = *q; + *q = '\0'; + + /* See if this is a builtin symbol. */ + for (i = 0; i < NUM_BUILTIN; i++) + if (! strcmp (p, builtin_symbols[i].name)) + break; + + if (i < NUM_BUILTIN) + s = &builtin_symbols[i]; + else + { + /* Look up symbol in symbol table. + If no entry exists, create one. */ + s = sym_lookup (p); + if (! s) + { + s = sym_enter (p); + if (! s) + { + error = BOOT_SCRIPT_NOMEM; + goto bad; + } + } + } + + /* Only values are allowed in ${...} constructs. */ + if (end_char == '}' && s->type == VAL_FUNC) + return BOOT_SCRIPT_INVALID_SYM; + + /* Check that assignment is valid. */ + if (c == '=' && s->type == VAL_FUNC) + { + error = BOOT_SCRIPT_INVALID_ASG; + goto bad; + } + + /* For function symbols, execute the function. */ + if (s->type == VAL_FUNC) + { + if (! s->run_on_exec) + { + (error + = ((*((int (*) (struct cmd *, long *)) s->val)) + (cmd, &val))); + if (error) + goto bad; + type = s->ret_type; + } + else + { + if (add_list (s, (void ***) &cmd->exec_funcs, + &cmd->exec_funcs_alloc, + &cmd->exec_funcs_index, 5)) + { + error = BOOT_SCRIPT_NOMEM; + goto bad; + } + type = VAL_NONE; + goto out; + } + } + else if (s->type == VAL_NONE) + { + type = VAL_SYM; + val = (long) s; + } + else + { + type = s->type; + val = s->val; + } + + if (sym) + { + sym->type = type; + sym->val = val; + } + else if (arg) + { + arg->type = type; + arg->val = val; + } + + out: + p = q + 1; + if (c == end_char) + { + /* Create an argument if necessary. + We create an argument if the symbol appears + in the expression by itself. + + NOTE: This is temporary till the boot filesystem + servers support arguments. When that happens, + symbol values will only be printed if they're + associated with an argument. */ + if (! arg && end_char == '}') + { + if (! add_arg (cmd, 0, type, val)) + { + error = BOOT_SCRIPT_NOMEM; + goto bad; + } + } + arg = 0; + break; + } + if (s->type != VAL_FUNC) + sym = s; + } + } + else + { + char c; + + /* Command argument; just copy the text. */ + for (q = p;; q++) + { + if (! *q || *q == ' ' || *q == '\t' || *q == '\n') + break; + if (*q == '$' && *(q + 1) == '{') + break; + } + c = *q; + *q = '\0'; + + /* Add argument to list. */ + arg = add_arg (cmd, p, VAL_NONE, 0); + if (! arg) + { + error = BOOT_SCRIPT_NOMEM; + goto bad; + } + if (c == '$') + p = q; + else + { + if (c) + p = q + 1; + else + p = q; + arg = 0; + } + } + } + + + bad: + free_cmd (cmd, 1); + cleanup (1); + return error; +} + +/* Ensure that the command line buffer can accommodate LEN bytes of space. */ +#define CHECK_CMDLINE_LEN(len) \ +{ \ + if (cmdline_alloc - cmdline_index < len) \ + { \ + char *ptr; \ + int alloc, i; \ + alloc = cmdline_alloc + len - (cmdline_alloc - cmdline_index) + 100; \ + ptr = boot_script_malloc (alloc); \ + if (! ptr) \ + { \ + error = BOOT_SCRIPT_NOMEM; \ + goto done; \ + } \ + memcpy (ptr, cmdline, cmdline_index); \ + for (i = 0; i < argc; ++i) \ + argv[i] = ptr + (argv[i] - cmdline); \ + boot_script_free (cmdline, cmdline_alloc); \ + cmdline = ptr; \ + cmdline_alloc = alloc; \ + } \ +} + +/* Execute commands previously parsed. */ +int +boot_script_exec (void) +{ + int cmd_index; + + for (cmd_index = 0; cmd_index < cmds_index; cmd_index++) + { + char **argv, *cmdline; + int i, argc, cmdline_alloc; + int cmdline_index, error, arg_index; + struct cmd *cmd = cmds[cmd_index]; + + /* Skip command if it doesn't have an associated task. */ + if (cmd->task == 0) + continue; + + /* Allocate a command line and copy command name. */ + cmdline_index = strlen (cmd->path) + 1; + cmdline_alloc = cmdline_index + 100; + cmdline = boot_script_malloc (cmdline_alloc); + if (! cmdline) + { + cleanup (1); + return BOOT_SCRIPT_NOMEM; + } + memcpy (cmdline, cmd->path, cmdline_index); + + /* Allocate argument vector. */ + argv = boot_script_malloc (sizeof (char *) * (cmd->args_index + 2)); + if (! argv) + { + boot_script_free (cmdline, cmdline_alloc); + cleanup (1); + return BOOT_SCRIPT_NOMEM; + } + argv[0] = cmdline; + argc = 1; + + /* Build arguments. */ + for (arg_index = 0; arg_index < cmd->args_index; arg_index++) + { + struct arg *arg = cmd->args[arg_index]; + + /* Copy argument text. */ + if (arg->text) + { + int len = strlen (arg->text); + + if (arg->type == VAL_NONE) + len++; + CHECK_CMDLINE_LEN (len); + memcpy (cmdline + cmdline_index, arg->text, len); + argv[argc++] = &cmdline[cmdline_index]; + cmdline_index += len; + } + + /* Add value of any symbol associated with this argument. */ + if (arg->type != VAL_NONE) + { + char *p, buf[50]; + int len; + mach_port_name_t name; + + if (arg->type == VAL_SYM) + { + struct sym *sym = (struct sym *) arg->val; + + /* Resolve symbol value. */ + while (sym->type == VAL_SYM) + sym = (struct sym *) sym->val; + if (sym->type == VAL_NONE) + { + error = BOOT_SCRIPT_UNDEF_SYM; + printf("bootstrap script missing symbol '%s'\n", sym->name); + goto done; + } + arg->type = sym->type; + arg->val = sym->val; + } + + /* Print argument value. */ + switch (arg->type) + { + case VAL_STR: + p = (char *) arg->val; + len = strlen (p); + break; + + case VAL_TASK: + case VAL_PORT: + if (arg->type == VAL_TASK) + /* Insert send right to task port. */ + error = boot_script_insert_task_port + (cmd, (task_t) arg->val, &name); + else + /* Insert send right. */ + error = boot_script_insert_right (cmd, + (mach_port_t) arg->val, + &name); + if (error) + goto done; + + i = name; + p = buf + sizeof (buf); + len = 0; + do + { + *--p = i % 10 + '0'; + len++; + } + while (i /= 10); + break; + + default: + error = BOOT_SCRIPT_BAD_TYPE; + goto done; + } + len++; + CHECK_CMDLINE_LEN (len); + memcpy (cmdline + cmdline_index, p, len - 1); + *(cmdline + cmdline_index + len - 1) = '\0'; + if (! arg->text) + argv[argc++] = &cmdline[cmdline_index]; + cmdline_index += len; + } + } + + /* Terminate argument vector. */ + argv[argc] = 0; + + /* Execute the command. */ + if (boot_script_exec_cmd (cmd->hook, cmd->task, cmd->path, + argc, argv, cmdline, cmdline_index)) + { + error = BOOT_SCRIPT_EXEC_ERROR; + goto done; + } + + error = 0; + + done: + boot_script_free (cmdline, cmdline_alloc); + boot_script_free (argv, sizeof (char *) * (cmd->args_index + 2)); + if (error) + { + cleanup (1); + return error; + } + } + + for (cmd_index = 0; cmd_index < cmds_index; cmd_index++) + { + int i; + struct cmd *cmd = cmds[cmd_index]; + + /* Execute functions that want to be run on exec. */ + for (i = 0; i < cmd->exec_funcs_index; i++) + { + struct sym *sym = cmd->exec_funcs[i]; + int error = ((*((int (*) (struct cmd *, int *)) sym->val)) + (cmd, 0)); + if (error) + { + cleanup (1); + return error; + } + } + } + + cleanup (0); + return 0; +} + +/* Create an entry for the variable NAME with TYPE and value VAL, + in the symbol table. */ +int +boot_script_set_variable (const char *name, int type, long val) +{ + struct sym *sym = sym_enter (name); + + if (sym) + { + sym->type = type; + sym->val = val; + } + return sym ? 0 : 1; +} + + +/* Define the function NAME, which will return type RET_TYPE. */ +int +boot_script_define_function (const char *name, int ret_type, + int (*func) (const struct cmd *cmd, int *val)) +{ + struct sym *sym = sym_enter (name); + + if (sym) + { + sym->type = VAL_FUNC; + sym->val = (long) func; + sym->ret_type = ret_type; + sym->run_on_exec = ret_type == VAL_NONE; + } + return sym ? 0 : 1; +} + + +/* Return a string describing ERR. */ +char * +boot_script_error_string (int err) +{ + switch (err) + { + case BOOT_SCRIPT_NOMEM: + return "no memory"; + + case BOOT_SCRIPT_SYNTAX_ERROR: + return "syntax error"; + + case BOOT_SCRIPT_INVALID_ASG: + return "invalid variable in assignment"; + + case BOOT_SCRIPT_MACH_ERROR: + return "mach error"; + + case BOOT_SCRIPT_UNDEF_SYM: + return "undefined symbol"; + + case BOOT_SCRIPT_EXEC_ERROR: + return "exec error"; + + case BOOT_SCRIPT_INVALID_SYM: + return "invalid variable in expression"; + + case BOOT_SCRIPT_BAD_TYPE: + return "invalid value type"; + } + return 0; +} + +#ifdef BOOT_SCRIPT_TEST +#include + +int +boot_script_exec_cmd (void *hook, + mach_port_t task, char *path, int argc, + char **argv, char *strings, int stringlen) +{ + int i; + + printf ("port = %d: ", (int) task); + for (i = 0; i < argc; i++) + printf ("%s ", argv[i]); + printf ("\n"); + return 0; +} + +void +main (int argc, char **argv) +{ + char buf[500], *p; + int len; + FILE *fp; + mach_port_name_t host_port, device_port; + + if (argc < 2) + { + fprintf (stderr, "Usage: %s