aboutsummaryrefslogtreecommitdiff
path: root/i386
diff options
context:
space:
mode:
Diffstat (limited to 'i386')
-rw-r--r--i386/Makefrag.am215
-rw-r--r--i386/Makefrag_x86.am84
-rw-r--r--i386/README-Drivers122
-rw-r--r--i386/configfrag.ac124
-rw-r--r--i386/i386/.gitignore1
-rw-r--r--i386/i386/_setjmp.S63
-rw-r--r--i386/i386/apic.c453
-rw-r--r--i386/i386/apic.h337
-rw-r--r--i386/i386/ast.h47
-rw-r--r--i386/i386/ast_check.c56
-rw-r--r--i386/i386/ast_types.h36
-rw-r--r--i386/i386/copy_user.h100
-rw-r--r--i386/i386/cpu.h110
-rw-r--r--i386/i386/cpu_number.h119
-rw-r--r--i386/i386/cpuboot.S245
-rw-r--r--i386/i386/cswitch.S139
-rw-r--r--i386/i386/db_disasm.c1437
-rw-r--r--i386/i386/db_interface.c865
-rw-r--r--i386/i386/db_interface.h149
-rw-r--r--i386/i386/db_machdep.h105
-rw-r--r--i386/i386/db_trace.c586
-rw-r--r--i386/i386/db_trace.h33
-rw-r--r--i386/i386/debug.h73
-rw-r--r--i386/i386/debug_i386.c178
-rw-r--r--i386/i386/debug_trace.S56
-rw-r--r--i386/i386/eflags.h35
-rw-r--r--i386/i386/fpu.c948
-rw-r--r--i386/i386/fpu.h250
-rw-r--r--i386/i386/gdt.c166
-rw-r--r--i386/i386/gdt.h121
-rw-r--r--i386/i386/hardclock.c81
-rw-r--r--i386/i386/hardclock.h28
-rw-r--r--i386/i386/i386asm.sym194
-rw-r--r--i386/i386/idt-gen.h47
-rw-r--r--i386/i386/idt.c87
-rw-r--r--i386/i386/idt_inittab.S140
-rw-r--r--i386/i386/io_perm.c329
-rw-r--r--i386/i386/io_perm.h63
-rw-r--r--i386/i386/ipl.h83
-rw-r--r--i386/i386/irq.c73
-rw-r--r--i386/i386/irq.h31
-rw-r--r--i386/i386/ktss.c92
-rw-r--r--i386/i386/ktss.h33
-rw-r--r--i386/i386/kttd_interface.c574
-rw-r--r--i386/i386/kttd_machdep.h59
-rw-r--r--i386/i386/ldt.c117
-rw-r--r--i386/i386/ldt.h77
-rw-r--r--i386/i386/lock.h132
-rw-r--r--i386/i386/locore.S1603
-rw-r--r--i386/i386/locore.h98
-rw-r--r--i386/i386/loose_ends.c49
-rw-r--r--i386/i386/loose_ends.h33
-rw-r--r--i386/i386/mach_i386.srv27
-rw-r--r--i386/i386/mach_param.h31
-rw-r--r--i386/i386/machine_routines.h38
-rw-r--r--i386/i386/machine_task.c80
-rw-r--r--i386/i386/machspl.h29
-rw-r--r--i386/i386/model_dep.h68
-rw-r--r--i386/i386/mp_desc.c357
-rw-r--r--i386/i386/mp_desc.h98
-rw-r--r--i386/i386/msr.h56
-rw-r--r--i386/i386/pcb.c958
-rw-r--r--i386/i386/pcb.h90
-rw-r--r--i386/i386/percpu.c33
-rw-r--r--i386/i386/percpu.h98
-rw-r--r--i386/i386/phys.c187
-rw-r--r--i386/i386/pic.c262
-rw-r--r--i386/i386/pic.h191
-rw-r--r--i386/i386/pio.h61
-rw-r--r--i386/i386/pit.c140
-rw-r--r--i386/i386/pit.h98
-rw-r--r--i386/i386/pmap.h27
-rw-r--r--i386/i386/proc_reg.h407
-rw-r--r--i386/i386/sched_param.h40
-rw-r--r--i386/i386/seg.h264
-rw-r--r--i386/i386/setjmp.h44
-rw-r--r--i386/i386/smp.c199
-rw-r--r--i386/i386/smp.h34
-rw-r--r--i386/i386/spl.S264
-rw-r--r--i386/i386/spl.h78
-rw-r--r--i386/i386/strings.c96
-rw-r--r--i386/i386/task.h61
-rw-r--r--i386/i386/thread.h276
-rw-r--r--i386/i386/time_stamp.h30
-rw-r--r--i386/i386/trap.c675
-rw-r--r--i386/i386/trap.h71
-rw-r--r--i386/i386/tss.h109
-rw-r--r--i386/i386/user_ldt.c451
-rw-r--r--i386/i386/user_ldt.h50
-rw-r--r--i386/i386/vm_param.h200
-rw-r--r--i386/i386/xen.h412
-rw-r--r--i386/i386/xpr.h32
-rw-r--r--i386/i386at/acpi_parse_apic.c650
-rw-r--r--i386/i386at/acpi_parse_apic.h201
-rw-r--r--i386/i386at/autoconf.c149
-rw-r--r--i386/i386at/autoconf.h43
-rw-r--r--i386/i386at/biosmem.c1070
-rw-r--r--i386/i386at/biosmem.h109
-rw-r--r--i386/i386at/boothdr.S179
-rw-r--r--i386/i386at/com.c900
-rw-r--r--i386/i386at/com.h86
-rw-r--r--i386/i386at/comreg.h139
-rw-r--r--i386/i386at/conf.c172
-rw-r--r--i386/i386at/cons_conf.c63
-rw-r--r--i386/i386at/cram.h86
-rw-r--r--i386/i386at/disk.h89
-rw-r--r--i386/i386at/elf.h61
-rw-r--r--i386/i386at/i8250.h134
-rw-r--r--i386/i386at/idt.h53
-rw-r--r--i386/i386at/immc.c134
-rw-r--r--i386/i386at/immc.h31
-rw-r--r--i386/i386at/int_init.c82
-rw-r--r--i386/i386at/int_init.h35
-rw-r--r--i386/i386at/interrupt.S142
-rw-r--r--i386/i386at/ioapic.c463
-rw-r--r--i386/i386at/kd.c3059
-rw-r--r--i386/i386at/kd.h744
-rw-r--r--i386/i386at/kd_event.c392
-rw-r--r--i386/i386at/kd_event.h62
-rw-r--r--i386/i386at/kd_mouse.c800
-rw-r--r--i386/i386at/kd_mouse.h72
-rw-r--r--i386/i386at/kd_queue.c109
-rw-r--r--i386/i386at/kd_queue.h86
-rw-r--r--i386/i386at/kdasm.S145
-rw-r--r--i386/i386at/kdsoft.h209
-rw-r--r--i386/i386at/lpr.c285
-rw-r--r--i386/i386at/lpr.h66
-rw-r--r--i386/i386at/mem.c42
-rw-r--r--i386/i386at/mem.h24
-rw-r--r--i386/i386at/model_dep.c674
-rw-r--r--i386/i386at/model_dep.h39
-rw-r--r--i386/i386at/pic_isa.c56
-rw-r--r--i386/i386at/rtc.c242
-rw-r--r--i386/i386at/rtc.h143
-rw-r--r--i386/include/mach/i386/asm.h146
-rw-r--r--i386/include/mach/i386/boolean.h37
-rw-r--r--i386/include/mach/i386/eflags.h53
-rw-r--r--i386/include/mach/i386/exception.h85
-rw-r--r--i386/include/mach/i386/exec/elf.h53
-rw-r--r--i386/include/mach/i386/fp_reg.h140
-rw-r--r--i386/include/mach/i386/ioccom.h32
-rw-r--r--i386/include/mach/i386/kern_return.h40
-rw-r--r--i386/include/mach/i386/mach_i386.defs113
-rw-r--r--i386/include/mach/i386/mach_i386_types.h57
-rwxr-xr-xi386/include/mach/i386/machine_types.defs107
-rw-r--r--i386/include/mach/i386/multiboot.h208
-rw-r--r--i386/include/mach/i386/syscall_sw.h39
-rw-r--r--i386/include/mach/i386/thread_status.h190
-rw-r--r--i386/include/mach/i386/trap.h60
-rw-r--r--i386/include/mach/i386/vm_param.h90
-rw-r--r--i386/include/mach/i386/vm_types.h173
-rw-r--r--i386/include/mach/sa/stdarg.h58
-rw-r--r--i386/intel/pmap.c3325
-rw-r--r--i386/intel/pmap.h574
-rw-r--r--i386/intel/read_fault.c178
-rw-r--r--i386/intel/read_fault.h35
-rw-r--r--i386/ldscript201
-rw-r--r--i386/linux/Makefrag.am25
-rw-r--r--i386/linux/dev/include/linux/autoconf.h284
-rw-r--r--i386/xen/Makefrag.am34
-rw-r--r--i386/xen/xen.c69
-rw-r--r--i386/xen/xen_boothdr.S208
-rw-r--r--i386/xen/xen_locore.S110
163 files changed, 37042 insertions, 0 deletions
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 <mach/machine/asm.h>
+
+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 <i386/apic.h>
+#include <i386/cpu.h>
+#include <i386at/idt.h>
+#include <string.h>
+#include <vm/vm_kern.h>
+#include <kern/printf.h>
+#include <kern/kalloc.h>
+
+/*
+ * 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 <stdint.h>
+
+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 <kern/ast.h>
+#include <kern/processor.h>
+#include <kern/smp.h>
+#include <machine/cpu_number.h>
+#include <machine/apic.h>
+
+/*
+ * 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 <stdint.h>
+#include <sys/types.h>
+
+#include <machine/locore.h>
+#include <mach/message.h>
+
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _X86_CPU_H
+#define _X86_CPU_H
+
+#include <kern/macros.h>
+
+/*
+ * 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 <kern/cpu_number.h>
+#include <i386/apic.h>
+#include <i386/percpu.h>
+
+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 <http://www.gnu.org/licenses/>.
+ */
+
+#if NCPUS > 1
+#include <mach/machine/asm.h>
+#include <i386/i386asm.h>
+#include <i386/proc_reg.h>
+#include <i386/apic.h>
+#include <i386/cpu_number.h>
+#include <i386/seg.h>
+#include <i386/gdt.h>
+
+#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 <mach/machine/asm.h>
+
+#include <i386/proc_reg.h>
+#include <i386/i386asm.h>
+#include <i386/cpu_number.h>
+#include <i386/gdt.h>
+
+/*
+ * 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 <mach/boolean.h>
+#include <machine/db_machdep.h>
+
+#include <ddb/db_access.h>
+#include <ddb/db_examine.h>
+#include <ddb/db_output.h>
+#include <ddb/db_sym.h>
+
+#include <kern/task.h>
+
+/*
+ * 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<<addrp->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("<bad instruction>");
+ break;
+ }
+ }
+
+ return loc;
+}
+
+/*
+ * Disassemble instruction at 'loc'. 'altfmt' specifies an
+ * (optional) alternate format. Return address of start of
+ * next instruction.
+ */
+db_addr_t
+db_disasm(
+ 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 <string.h>
+#include <sys/reboot.h>
+#include <vm/pmap.h>
+
+#include <i386/thread.h>
+#include <i386/db_machdep.h>
+#include <i386/seg.h>
+#include <i386/trap.h>
+#include <i386/setjmp.h>
+#include <i386/pmap.h>
+#include <i386/proc_reg.h>
+#include <i386/locore.h>
+#include <i386at/biosmem.h>
+#include "gdt.h"
+#include "trap.h"
+
+#include "vm_param.h"
+#include <vm/vm_map.h>
+#include <vm/vm_fault.h>
+#include <kern/cpu_number.h>
+#include <kern/printf.h>
+#include <kern/thread.h>
+#include <kern/task.h>
+#include <ddb/db_access.h>
+#include <ddb/db_command.h>
+#include <ddb/db_output.h>
+#include <ddb/db_run.h>
+#include <ddb/db_task_thread.h>
+#include <ddb/db_trap.h>
+#include <ddb/db_watch.h>
+#include <ddb/db_mp.h>
+#include <machine/db_interface.h>
+#include <machine/machspl.h>
+
+#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)&regs->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 <sys/types.h>
+#include <kern/task.h>
+#include <machine/thread.h>
+#include <ddb/db_watch.h>
+#include <ddb/db_variables.h>
+
+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 <mach/machine/vm_types.h>
+#include <mach/machine/vm_param.h>
+#include <mach/machine/eflags.h>
+#include <i386/thread.h> /* for thread_status */
+#include <i386/trap.h>
+
+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 <string.h>
+
+#include <mach/boolean.h>
+#include <vm/vm_map.h>
+#include <kern/thread.h>
+#include <kern/task.h>
+
+#include <machine/db_machdep.h>
+#include <machine/machspl.h>
+#include <machine/db_interface.h>
+#include <machine/db_trace.h>
+#include <machine/cpu_number.h>
+#include <i386at/model_dep.h>
+
+#include <ddb/db_access.h>
+#include <ddb/db_command.h>
+#include <ddb/db_output.h>
+#include <ddb/db_sym.h>
+#include <ddb/db_variables.h>
+#include <ddb/db_task_thread.h>
+
+#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 <kern/printf.h>
+
+#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 <kern/syscall_sw.h>
+
+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 <mach/machine/asm.h>
+#include <i386/xen.h>
+
+#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 <mach/machine/eflags.h>
+
+/* 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 <gareth@valinux.com>, May 2000
+ */
+
+/*
+ * Support for 80387 floating point or FP emulator.
+ */
+
+#include <string.h>
+
+#include <mach/exception.h>
+#include <mach/machine/thread_status.h>
+#include <mach/machine/fp_reg.h>
+
+#include <kern/debug.h>
+#include <machine/machspl.h> /* spls */
+#include <kern/printf.h>
+#include <kern/thread.h>
+#include <kern/slab.h>
+
+#include <i386/thread.h>
+#include <i386/fpu.h>
+#include <i386/pio.h>
+#include <i386/irq.h>
+#include <i386/locore.h>
+#include <i386/trap.h>
+#include "cpu_number.h"
+
+#if 0
+#include <i386/ipl.h>
+#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 <sys/types.h>
+#include <i386/proc_reg.h>
+#include <kern/thread.h>
+
+/*
+ * 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 <mach/machine/vm_types.h>
+#include <mach/xen.h>
+
+#include <kern/assert.h>
+#include <intel/pmap.h>
+#include <kern/cpu_number.h>
+#include <machine/percpu.h>
+
+#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 <mach/machine/eflags.h>
+
+#include <kern/mach_clock.h>
+#include <i386/thread.h>
+#include <i386/hardclock.h>
+
+#if defined(AT386) || defined(ATX86_64)
+#include <i386/ipl.h>
+#endif
+
+#ifdef LINUX_DEV
+#include <linux/dev/glue/glue.h>
+#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 <sys/reboot.h>
+
+#include <kern/thread.h>
+#include <kern/task.h>
+#include <kern/syscall_emulation.h>
+#include <i386/thread.h>
+#include <i386/pmap.h>
+#include <i386/vm_param.h>
+#include <i386/seg.h>
+#include <i386/tss.h>
+#include <i386at/idt.h>
+#include <i386/gdt.h>
+#include <i386/ldt.h>
+#include <i386/mp_desc.h>
+#include <i386/apic.h>
+#include <i386/xen.h>
+
+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 <mach/vm_param.h>
+
+#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 <i386/vm_param.h>
+#include <i386/seg.h>
+#include <i386at/idt.h>
+#include <i386/gdt.h>
+#include <i386/mp_desc.h>
+
+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 <mach/machine/asm.h>
+
+#include <i386/seg.h>
+#include <i386/i386asm.h>
+#include <i386/gdt.h>
+
+
+/* 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 <string.h>
+
+#include <mach/boolean.h>
+#include <mach/kern_return.h>
+
+#include <ipc/ipc_port.h>
+#include <ipc/ipc_space.h>
+
+#include <kern/slab.h>
+#include <kern/kalloc.h>
+#include <kern/lock.h>
+#include <kern/queue.h>
+#include <kern/thread.h>
+
+#include <device/dev_hdr.h>
+#include <device/device_emul.h>
+#include <device/device_port.h>
+
+#include <i386/i386/mach_i386.server.h>
+
+#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, &notify);
+ 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 <device/dev_hdr.h>
+#include <ipc/ipc_types.h>
+
+
+/* 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 <machine/machspl.h>
+/* 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 <i386/irq.h>
+#include <device/intr.h>
+#include <mach/kern_return.h>
+#include <kern/queue.h>
+#include <kern/assert.h>
+#include <machine/machspl.h>
+
+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 <i386/apic.h>
+#else
+# include <i386/pic.h>
+#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 <sys/types.h>
+#include <kern/printf.h>
+
+#include <mach/machine/eflags.h>
+
+#include <kern/thread.h>
+#include <kern/processor.h>
+#include <mach/thread_status.h>
+#include <mach/vm_param.h>
+#include <i386/seg.h>
+
+#include <ttd/ttd_types.h>
+#include <ttd/ttd_stub.h>
+#include <machine/kttd_machdep.h>
+
+/*
+ * 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)&regs->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 <mach/machine/eflags.h>
+#include <mach/machine/vm_types.h>
+#include <mach/xen.h>
+
+#include <intel/pmap.h>
+#include <kern/debug.h>
+
+#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 <i386/smp.h>
+
+/*
+ * 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 <mach/machine/asm.h>
+#include <mach/machine/eflags.h>
+#include <i386/proc_reg.h>
+#include <i386/trap.h>
+#include <i386/seg.h>
+#include <i386/gdt.h>
+#include <i386/ldt.h>
+#include <i386/i386asm.h>
+#include <i386/cpu_number.h>
+#include <i386/xen.h>
+
+#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 <sys/types.h>
+
+#include <kern/sched_prim.h>
+
+/*
+ * 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 <i386/i386/loose_ends.h>
+
+#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 <mach/std_types.h>
+
+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 <mach/machine/mach_i386.defs>
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 <kern/lock.h>
+#include <mach/mach_types.h>
+#include <kern/slab.h>
+#include <kern/task.h>
+#include <machine/task.h>
+
+#include <machine/io_perm.h>
+
+
+/* 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 <i386/spl.h>
+
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 <mach/std_types.h>
+
+/*
+ * 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 <kern/cpu_number.h>
+#include <kern/debug.h>
+#include <kern/printf.h>
+#include <kern/smp.h>
+#include <kern/startup.h>
+#include <kern/kmutex.h>
+#include <mach/machine.h>
+#include <mach/xen.h>
+#include <vm/vm_kern.h>
+
+#include <i386/mp_desc.h>
+#include <i386/lock.h>
+#include <i386/apic.h>
+#include <i386/locore.h>
+#include <i386/fpu.h>
+#include <i386/gdt.h>
+#include <i386at/idt.h>
+#include <i386at/int_init.h>
+#include <i386/cpu.h>
+#include <i386/smp.h>
+
+#include <i386at/model_dep.h>
+#include <machine/ktss.h>
+#include <machine/smp.h>
+#include <machine/tss.h>
+#include <machine/io_perm.h>
+#include <machine/vm_param.h>
+
+#include <i386at/acpi_parse_apic.h>
+#include <string.h>
+
+/*
+ * 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 <mach/kern_return.h>
+
+#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 <i386at/idt.h>
+#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 <stddef.h>
+#include <string.h>
+
+#include <mach/std_types.h>
+#include <mach/kern_return.h>
+#include <mach/thread_status.h>
+#include <mach/exec/exec.h>
+#include <mach/xen.h>
+
+#include "vm_param.h"
+#include <kern/counters.h>
+#include <kern/debug.h>
+#include <kern/thread.h>
+#include <kern/sched_prim.h>
+#include <kern/slab.h>
+#include <vm/vm_kern.h>
+#include <vm/pmap.h>
+
+#include <i386/thread.h>
+#include <i386/proc_reg.h>
+#include <i386/seg.h>
+#include <i386/user_ldt.h>
+#include <i386/db_interface.h>
+#include <i386/fpu.h>
+#include "eflags.h"
+#include "gdt.h"
+#include "ldt.h"
+#include "msr.h"
+#include "ktss.h"
+#include "pcb.h"
+
+#include <machine/tss.h>
+
+#if NCPUS > 1
+#include <i386/mp_desc.h>
+#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 <sys/types.h>
+#include <mach/exec/exec.h>
+#include <mach/thread_status.h>
+#include <machine/thread.h>
+#include <machine/io_perm.h>
+
+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 <http://www.gnu.org/licenses/>.
+ */
+#include <i386/smp.h>
+#include <i386/apic.h>
+#include <kern/cpu_number.h>
+#include <i386/percpu.h>
+
+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 <http://www.gnu.org/licenses/>.
+ */
+
+#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 <kern/processor.h>
+#include <mach/mach_types.h>
+
+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 <string.h>
+
+#include <mach/boolean.h>
+#include <mach/xen.h>
+#include <kern/task.h>
+#include <kern/thread.h>
+#include <vm/vm_map.h>
+#include "vm_param.h"
+#include <mach/vm_prot.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_page.h>
+
+#include <i386/pmap.h>
+#include <i386/model_dep.h>
+#include <mach/machine/vm_param.h>
+
+#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 <sys/types.h>
+#include <kern/printf.h>
+#include <i386/ipl.h>
+#include <i386/pic.h>
+#include <i386/machspl.h>
+#include <i386/pio.h>
+
+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 <kern/mach_clock.h>
+#include <i386/ipl.h>
+#include <machine/irq.h>
+#include <i386/pit.h>
+#include <i386/pio.h>
+#include <kern/cpu_number.h>
+
+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 <intel/pmap.h>
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 <i386/gdt.h>
+#include <i386/ldt.h>
+#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 <mach/inline.h>
+#include <mach/machine/vm_types.h>
+
+/*
+ * 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 <mach/inline.h>
+#include <mach/xen.h>
+
+
+/* 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 <string.h>
+#include <i386/apic.h>
+#include <i386/smp.h>
+#include <i386/cpu.h>
+#include <i386/pio.h>
+#include <i386/vm_param.h>
+#include <i386at/idt.h>
+#include <i386at/cram.h>
+#include <i386at/acpi_parse_apic.h>
+#include <kern/printf.h>
+#include <mach/machine.h>
+
+#include <kern/smp.h>
+
+/*
+ * 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 <mach/machine/vm_types.h>
+
+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 <mach/machine/asm.h>
+#include <i386/ipl.h>
+#include <i386/i386asm.h>
+#include <i386/xen.h>
+#include <i386/cpu_number.h>
+#include <i386/gdt.h>
+
+#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 <i386/ipl.h>
+
+#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 <stddef.h>
+#include <string.h>
+
+#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 <kern/kern_types.h>
+#include <kern/slab.h>
+
+/* 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 <mach/boolean.h>
+#include <mach/machine/vm_types.h>
+#include <mach/machine/fp_reg.h>
+#include <mach/machine/thread_status.h>
+
+#include <kern/lock.h>
+
+#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 <sys/types.h>
+#include <string.h>
+
+#include <mach/machine/eflags.h>
+#include <i386/trap.h>
+#include <i386/fpu.h>
+#include <i386/locore.h>
+#include <i386/model_dep.h>
+#include <intel/read_fault.h>
+#include <machine/machspl.h> /* for spl_t */
+#include <machine/db_interface.h>
+
+#include <mach/exception.h>
+#include <mach/kern_return.h>
+#include "vm_param.h"
+#include <mach/machine/thread_status.h>
+
+#include <vm/vm_fault.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_map.h>
+
+#include <kern/ast.h>
+#include <kern/debug.h>
+#include <kern/printf.h>
+#include <kern/thread.h>
+#include <kern/task.h>
+#include <kern/sched.h>
+#include <kern/sched_prim.h>
+#include <kern/exception.h>
+
+#if MACH_KDB
+#include <ddb/db_break.h>
+#include <ddb/db_run.h>
+#include <ddb/db_watch.h>
+#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 <mach/machine/trap.h>
+
+#ifndef __ASSEMBLER__
+#include <i386/thread.h>
+#include <mach/mach_types.h>
+
+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 <sys/types.h>
+#include <mach/inline.h>
+
+#include <machine/io_perm.h>
+
+/*
+ * 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 <string.h>
+
+#include <kern/kalloc.h>
+#include <kern/thread.h>
+
+#include <vm/vm_kern.h>
+
+#include <i386/pcb.h>
+#include <i386/seg.h>
+#include <i386/thread.h>
+#include <i386/user_ldt.h>
+#include <i386/i386/mach_i386.server.h>
+#include <stddef.h>
+#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 <i386/seg.h>
+
+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 <kern/macros.h>
+
+/* XXX use xu/vm_param.h */
+#include <mach/vm_param.h>
+#ifdef MACH_PV_PAGETABLES
+#include <xen/public/xen.h>
+#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 <kern/macros.h>
+#include <kern/printf.h>
+#include <mach/machine/vm_types.h>
+#include <mach/vm_param.h>
+#include <mach/inline.h>
+#include <mach/xen.h>
+#include <machine/vm_param.h>
+#include <intel/pmap.h>
+#include <kern/debug.h>
+#include <xen/public/xen.h>
+
+/* 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 <xen/public/memory.h>
+_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<nbentries; count+= PAGE_SIZE/8)
+ pmap_set_page_readonly(ldt+count*8);
+ hyp_mmuext_op(kvtolin(&op), 1, kvtolin(&count), DOMID_SELF);
+ if (!count)
+ panic("couldn't set LDT\n");
+}
+#define hyp_set_cr3(value) hyp_mmuext_op_mfn(MMUEXT_NEW_BASEPTR, pa_to_mfn(value))
+#define hyp_set_user_cr3(value) hyp_mmuext_op_mfn(MMUEXT_NEW_USER_BASEPTR, pa_to_mfn(value))
+static inline void hyp_invlpg(vm_offset_t lin) {
+ struct mmuext_op ops;
+ int n;
+ ops.cmd = MMUEXT_INVLPG_ALL;
+ ops.arg1.linear_addr = lin;
+ hyp_mmuext_op(kvtolin(&ops), 1, kvtolin(&n), DOMID_SELF);
+ if (n < 1)
+ panic("couldn't invlpg\n");
+}
+#endif
+
+#ifdef __i386__
+_hypcall2(long, set_timer_op, unsigned long, absolute_lo, unsigned long, absolute_hi);
+#define hyp_do_set_timer_op(absolute_nsec) ({ \
+ uint64_t __absolute = (absolute_nsec); \
+ hyp_set_timer_op(__absolute & 0xffffffffU, __absolute >> 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 <xen/public/event_channel.h>
+_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 <xen/public/sched.h>
+_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 <string.h> /* memcmp, memcpy... */
+
+#include <stdint.h> /* uint16_t, uint32_t... */
+
+#include <mach/machine.h> /* machine_slot */
+
+#include <kern/printf.h> /* printf */
+#include <kern/debug.h>
+#include <i386/vm_param.h> /* phystokv */
+#include <i386/apic.h> /* lapic, ioapic... */
+#include <i386at/acpi_parse_apic.h>
+#include <vm/vm_kern.h>
+
+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 <stdint.h>
+
+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 <kern/printf.h>
+#include <mach/std_types.h>
+#include <i386at/autoconf.h>
+#include <i386/irq.h>
+#include <i386/ipl.h>
+#ifdef APIC
+# include <i386/apic.h>
+#else
+# include <i386/pic.h>
+#endif
+#include <chips/busses.h>
+
+/* 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 <i386at/com.h>
+#endif /* NCOM */
+
+#if NLPR > 0
+extern struct bus_driver lprdriver;
+#include <i386at/lpr.h>
+#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 <mach/std_types.h>
+#include <chips/busses.h>
+
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+#include <inttypes.h>
+#include <i386/model_dep.h>
+#include <i386at/biosmem.h>
+#include <kern/assert.h>
+#include <kern/debug.h>
+#include <kern/macros.h>
+#include <kern/printf.h>
+#include <mach/vm_param.h>
+#include <mach/xen.h>
+#include <mach/machine/multiboot.h>
+#include <sys/types.h>
+#include <vm/vm_page.h>
+
+#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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _X86_BIOSMEM_H
+#define _X86_BIOSMEM_H
+
+#include <mach/machine/vm_types.h>
+#include <mach/machine/multiboot.h>
+
+/*
+ * 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 <mach/machine/asm.h>
+#include <i386/apic.h>
+#include <i386/seg.h>
+#include <i386/i386asm.h>
+
+ /*
+ * 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 <string.h>
+#include <util/atoi.h>
+
+#include <mach/std_types.h>
+#include <sys/types.h>
+#include <kern/printf.h>
+#include <kern/mach_clock.h>
+#include <device/conf.h>
+#include <device/device_types.h>
+#include <device/tty.h>
+#include <device/io_req.h>
+
+#include <i386/ipl.h>
+#include <i386/pio.h>
+#include <i386/machspl.h>
+#include <chips/busses.h>
+#include <i386at/autoconf.h>
+#include <i386at/com.h>
+#include <i386at/comreg.h>
+
+#include <device/cons.h>
+
+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<dev> 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 <unit> 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 <unit>.
+ * 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 <mach/std_types.h>
+#include <device/cons.h>
+#include <device/tty.h>
+#include <chips/busses.h>
+
+/*
+ * 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 <mach/machine/vm_types.h>
+#include <device/conf.h>
+#include <kern/mach_clock.h>
+#include <i386at/model_dep.h>
+
+#define timename "time"
+
+#ifndef MACH_HYP
+#include <i386at/kd.h>
+#define kdname "kd"
+
+#if NCOM > 0
+#include <i386at/com.h>
+#define comname "com"
+#endif /* NCOM > 0 */
+
+#if NLPR > 0
+#include <i386at/lpr.h>
+#define lprname "lpr"
+#endif /* NLPR > 0 */
+#endif /* MACH_HYP */
+
+#include <i386at/kd_event.h>
+#define kbdname "kbd"
+
+#ifndef MACH_HYP
+#include <i386at/kd_mouse.h>
+#define mousename "mouse"
+
+#include <i386at/mem.h>
+#define memname "mem"
+#endif /* MACH_HYP */
+
+#include <device/kmsg.h>
+#define kmsgname "kmsg"
+
+#ifdef MACH_HYP
+#include <xen/console.h>
+#define hypcnname "hyp"
+#endif /* MACH_HYP */
+
+#include <device/intr.h>
+#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 <sys/types.h>
+#include <device/cons.h>
+
+#ifdef MACH_HYP
+#include <xen/console.h>
+#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 <http://www.gnu.org/licenses/>.
+ */
+
+#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 <i386/idt-gen.h>
+
+#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 <device/cons.h>
+#include <mach/boolean.h>
+#include <i386/vm_param.h>
+#include <string.h>
+
+/* 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _IMMC_H_
+#define _IMMC_H_
+
+#include <sys/types.h>
+
+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 <i386at/idt.h>
+#include <i386at/int_init.h>
+#include <i386/gdt.h>
+#include <i386/mp_desc.h>
+#include <kern/printf.h>
+#ifdef APIC
+#include <i386/apic.h>
+#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 <mach/std_types.h>
+
+#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 <mach/machine/asm.h>
+
+#include <i386/ipl.h>
+#ifdef APIC
+# include <i386/apic.h>
+#else
+# include <i386/pic.h>
+#endif
+#include <i386/i386asm.h>
+
+#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 <sys/types.h>
+#include <i386/ipl.h>
+#include <machine/irq.h>
+#include <i386/fpu.h>
+#include <i386/hardclock.h>
+#include <i386at/kd.h>
+#include <i386at/idt.h>
+#include <i386/pio.h>
+#include <i386/pit.h>
+#include <i386/pic.h> /* only for macros */
+#include <i386/smp.h>
+#include <mach/machine.h>
+#include <kern/printf.h>
+#include <kern/timer.h>
+#include <kern/lock.h>
+
+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 <sys/types.h>
+#include <kern/debug.h>
+#include <kern/mach_clock.h>
+#include <kern/printf.h>
+#include <device/conf.h>
+#include <device/tty.h>
+#include <device/io_req.h>
+#include <device/buf.h>
+#include <vm/vm_kern.h>
+#include <i386/db_interface.h>
+#include <i386/locore.h>
+#include <i386/loose_ends.h>
+#include <i386/vm_param.h>
+#include <i386/machspl.h>
+#include <i386/pio.h>
+#include <i386at/cram.h>
+#include <i386at/kd.h>
+#include <i386at/kd_event.h>
+#include <i386at/kd_mouse.h>
+#include <i386at/kdsoft.h>
+#include <device/cons.h>
+#include <util/atoi.h>
+
+#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 <device/input.h>
+#include <mach/boolean.h>
+#include <sys/types.h>
+#include <device/cons.h>
+#include <device/io_req.h>
+#include <device/buf.h>
+#include <device/input.h>
+#include <device/tty.h>
+#include <i386at/kdsoft.h>
+
+/*
+ * 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 <i386/machspl.h>
+#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 <ddb/db_input.h>
+#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 <mach/boolean.h>
+#include <sys/types.h>
+#include <kern/printf.h>
+#include <string.h>
+
+#include <device/ds_routines.h>
+#include <device/device_types.h>
+#include <device/io_req.h>
+#include <i386/machspl.h>
+#include <i386/pio.h>
+#include <i386at/kd.h>
+#include <i386at/kd_queue.h>
+#ifdef APIC
+# include <i386/apic.h>
+#else
+# include <i386/pic.h>
+#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 <sys/types.h>
+#include <device/io_req.h>
+#include <i386at/kd.h>
+
+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 <mach/boolean.h>
+#include <sys/types.h>
+#include <kern/printf.h>
+#include <device/ds_routines.h>
+#include <device/device_types.h>
+#include <device/io_req.h>
+#include <device/subrs.h>
+#include <i386/ipl.h>
+#include <i386/irq.h>
+#include <i386/pio.h>
+#include <chips/busses.h>
+#include <i386at/com.h>
+#include <i386at/kd.h>
+#include <i386at/kd_queue.h>
+#include <i386at/i8250.h>
+
+#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 <sys/types.h>
+
+#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 <i386at/kd_queue.h>
+
+/*
+ * 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 <mach/std_types.h>
+#include <i386at/kd.h>
+
+#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 <mach/machine/asm.h>
+
+/*
+ * 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 <mach/std_types.h>
+#include <sys/types.h>
+#include <kern/printf.h>
+#include <kern/mach_clock.h>
+#include <device/conf.h>
+#include <device/device_types.h>
+#include <device/tty.h>
+#include <device/io_req.h>
+
+#include <i386/ipl.h>
+#include <i386/pio.h>
+#include <chips/busses.h>
+#include <i386at/autoconf.h>
+#include <i386at/lpr.h>
+
+/*
+ * 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 <device/io_req.h>
+#include <i386/model_dep.h>
+#include <i386at/biosmem.h>
+#include <i386at/mem.h>
+
+/* 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 <inttypes.h>
+#include <string.h>
+
+#include <device/cons.h>
+
+#include <mach/vm_param.h>
+#include <mach/vm_prot.h>
+#include <mach/machine.h>
+#include <mach/machine/multiboot.h>
+#include <mach/xen.h>
+
+#include <kern/assert.h>
+#include <kern/cpu_number.h>
+#include <kern/debug.h>
+#include <kern/mach_clock.h>
+#include <kern/macros.h>
+#include <kern/printf.h>
+#include <kern/startup.h>
+#include <kern/smp.h>
+#include <sys/types.h>
+#include <vm/vm_page.h>
+#include <i386/fpu.h>
+#include <i386/gdt.h>
+#include <i386/ktss.h>
+#include <i386/ldt.h>
+#include <i386/machspl.h>
+#include <i386/mp_desc.h>
+#include <i386/pit.h>
+#include <i386/pmap.h>
+#include <i386/proc_reg.h>
+#include <i386/vm_param.h>
+#include <i386/locore.h>
+#include <i386/model_dep.h>
+#include <i386/smp.h>
+#include <i386/seg.h>
+#include <i386at/acpi_parse_apic.h>
+#include <i386at/autoconf.h>
+#include <i386at/biosmem.h>
+#include <i386at/elf.h>
+#include <i386at/idt.h>
+#include <i386at/int_init.h>
+#include <i386at/kd.h>
+#include <i386at/rtc.h>
+#include <i386at/model_dep.h>
+#include <machine/irq.h>
+
+#ifdef MACH_XEN
+#include <xen/console.h>
+#include <xen/store.h>
+#include <xen/evt.h>
+#include <xen/xen.h>
+#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 <ddb/db_sym.h>
+#include <i386/db_interface.h>
+
+/* 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 <mach/vm_prot.h>
+#include <vm/pmap.h>
+#include <mach/time_value.h>
+
+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 <i386/vm_param.h>
+#include <mach/vm_prot.h>
+
+/*
+ * 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 <sys/types.h>
+#include <i386/ipl.h>
+#include <i386/pic.h>
+#include <i386/fpu.h>
+#include <i386/hardclock.h>
+#include <i386at/kd.h>
+
+/* 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 <sys/types.h>
+#include <kern/mach_clock.h>
+#include <kern/printf.h>
+#include <i386/machspl.h>
+#include <i386/pio.h>
+#include <i386at/rtc.h>
+
+/* 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 <mach/std_types.defs>
+#include <mach/mach_types.defs>
+
+#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 <mach/machine/mach_i386_types.h>;
+
+#if KERNEL_SERVER
+simport <machine/io_perm.h>;
+#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 <mach/machine/vm_types.h>
+
+/* 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 <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Versions used by the biosmem module.
+ */
+
+#include <kern/macros.h>
+
+/*
+ * 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 <mach/machine/asm.h>
+
+#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 <mach/machine/fp_reg.h>
+/*
+ * 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 <mach/i386/fp_reg.h>.
+ *
+ * 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 <mach/machine/vm_types.h>
+
+#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 <stdint.h>
+
+#ifdef MACH_KERNEL
+#include <kern/assert.h>
+#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 <string.h>
+
+#include <mach/machine/vm_types.h>
+
+#include <mach/boolean.h>
+#include <kern/debug.h>
+#include <kern/printf.h>
+#include <kern/thread.h>
+#include <kern/slab.h>
+
+#include <kern/lock.h>
+
+#include <vm/pmap.h>
+#include <vm/vm_map.h>
+#include <vm/vm_kern.h>
+#include <i386/vm_param.h>
+#include <mach/vm_prot.h>
+#include <vm/vm_object.h>
+#include <vm/vm_page.h>
+#include <vm/vm_user.h>
+
+#include <mach/machine/vm_param.h>
+#include <mach/xen.h>
+#include <machine/thread.h>
+#include <i386/cpu_number.h>
+#include <i386/proc_reg.h>
+#include <i386/locore.h>
+#include <i386/model_dep.h>
+#include <i386/spl.h>
+#include <i386at/biosmem.h>
+#include <i386at/model_dep.h>
+
+#if NCPUS > 1
+#include <i386/mp_desc.h>
+#endif
+
+#include <ddb/db_output.h>
+#include <machine/db_machdep.h>
+
+#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 <kern/lock.h>
+#include <mach/machine/vm_param.h>
+#include <mach/vm_statistics.h>
+#include <mach/kern_return.h>
+#include <mach/vm_prot.h>
+#include <i386/proc_reg.h>
+
+/*
+ * 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 <vm/vm_fault.h>
+#include <mach/kern_return.h>
+#include <vm/vm_map.h>
+#include <vm/vm_object.h>
+#include <vm/vm_page.h>
+#include <vm/pmap.h>
+
+#include <kern/macros.h>
+
+#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 <mach/std_types.h>
+
+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 <kern/printf.h>
+#include <kern/debug.h>
+#include <kern/mach_clock.h>
+
+#include <mach/machine/eflags.h>
+#include <machine/thread.h>
+#include <machine/ipl.h>
+#include <machine/model_dep.h>
+
+#include <xen/xen.h>
+
+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 <xen/public/elfnote.h>
+
+.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 <mach/machine/asm.h>
+
+#include <i386/i386/i386asm.h>
+
+ .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 <mach/machine/asm.h>
+
+#include <i386/i386asm.h>
+#include <i386/cpu_number.h>
+#include <i386/xen.h>
+
+ .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