diff options
Diffstat (limited to 'i386')
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)®s->uesp; /* kernel stack pointer */ + ddb_regs.ss = KERNEL_DS; + } + + cnpollc(TRUE); + db_task_trap(type, code, (regs->cs & 0x3) != 0); + cnpollc(FALSE); + + regs->eip = ddb_regs.eip; + regs->efl = ddb_regs.efl; + regs->eax = ddb_regs.eax; + regs->ecx = ddb_regs.ecx; + regs->edx = ddb_regs.edx; + regs->ebx = ddb_regs.ebx; + if ((regs->cs & 0x3) != KERNEL_RING) { + /* + * user mode - saved esp and ss valid + */ + regs->uesp = ddb_regs.uesp; /* user stack pointer */ + regs->ss = ddb_regs.ss & 0xffff; /* user stack segment */ + } + regs->ebp = ddb_regs.ebp; + regs->esi = ddb_regs.esi; + regs->edi = ddb_regs.edi; + regs->cs = ddb_regs.cs & 0xffff; +#if !defined(__x86_64__) || defined(USER32) + regs->es = ddb_regs.es & 0xffff; + regs->ds = ddb_regs.ds & 0xffff; + regs->fs = ddb_regs.fs & 0xffff; + regs->gs = ddb_regs.gs & 0xffff; +#endif + if ((type == T_INT3) && + (db_get_task_value(regs->eip, BKPT_SIZE, FALSE, TASK_NULL) + == BKPT_INST)) + regs->eip += BKPT_SIZE; + } +#if NCPUS > 1 + db_leave(); +#endif /* NCPUS > 1 */ + + splx(s); + return 1; +} + +/* + * Enter KDB through a keyboard trap. + * We show the registers as of the keyboard interrupt + * instead of those at its call to KDB. + */ +struct int_regs { +#ifdef __i386__ + long edi; + long esi; +#endif + long ebp; + long ebx; + struct i386_interrupt_state *is; +}; + +void +kdb_kentry( + struct int_regs *int_regs) +{ + struct i386_interrupt_state *is = int_regs->is; + spl_t s = splhigh(); + +#if NCPUS > 1 + if (db_enter()) +#endif /* NCPUS > 1 */ + { + if ((is->cs & 0x3) != KERNEL_RING) { + ddb_regs.uesp = *(uintptr_t *)(is+1); + ddb_regs.ss = *(int *)((uintptr_t *)(is+1)+1); + } + else { + ddb_regs.ss = KERNEL_DS; + ddb_regs.uesp= (uintptr_t)(is+1); + } + ddb_regs.efl = is->efl; + ddb_regs.cs = is->cs; + ddb_regs.eip = is->eip; + ddb_regs.eax = is->eax; + ddb_regs.ecx = is->ecx; + ddb_regs.edx = is->edx; + ddb_regs.ebx = int_regs->ebx; + ddb_regs.ebp = int_regs->ebp; +#ifdef __i386__ + ddb_regs.esi = int_regs->esi; + ddb_regs.edi = int_regs->edi; +#endif +#ifdef __x86_64__ + ddb_regs.esi = is->rsi; + ddb_regs.edi = is->rdi; +#endif +#if !defined(__x86_64__) || defined(USER32) + ddb_regs.ds = is->ds; + ddb_regs.es = is->es; + ddb_regs.fs = is->fs; + ddb_regs.gs = is->gs; +#endif + cnpollc(TRUE); + db_task_trap(-1, 0, (ddb_regs.cs & 0x3) != 0); + cnpollc(FALSE); + + if ((ddb_regs.cs & 0x3) != KERNEL_RING) { + ((int *)(is+1))[0] = ddb_regs.uesp; + ((int *)(is+1))[1] = ddb_regs.ss & 0xffff; + } + is->efl = ddb_regs.efl; + is->cs = ddb_regs.cs & 0xffff; + is->eip = ddb_regs.eip; + is->eax = ddb_regs.eax; + is->ecx = ddb_regs.ecx; + is->edx = ddb_regs.edx; + int_regs->ebx = ddb_regs.ebx; + int_regs->ebp = ddb_regs.ebp; +#ifdef __i386__ + int_regs->esi = ddb_regs.esi; + int_regs->edi = ddb_regs.edi; +#endif +#ifdef __x86_64__ + is->rsi = ddb_regs.esi; + is->rdi = ddb_regs.edi; +#endif +#if !defined(__x86_64__) || defined(USER32) + is->ds = ddb_regs.ds & 0xffff; + is->es = ddb_regs.es & 0xffff; + is->fs = ddb_regs.fs & 0xffff; + is->gs = ddb_regs.gs & 0xffff; +#endif + } +#if NCPUS > 1 + db_leave(); +#endif /* NCPUS > 1 */ + + (void) splx(s); +} + +boolean_t db_no_vm_fault = TRUE; + +static int +db_user_to_phys_address( + const task_t task, + vm_offset_t addr, + phys_addr_t *paddr, + int flag) +{ + pt_entry_t *ptp; + boolean_t faulted = FALSE; + + retry: + ptp = pmap_pte(task->map->pmap, addr); + if (ptp == PT_ENTRY_NULL || (*ptp & INTEL_PTE_VALID) == 0) { + if (!faulted && !db_no_vm_fault) { + kern_return_t err; + + faulted = TRUE; + err = vm_fault( task->map, + trunc_page(addr), + VM_PROT_READ, + FALSE, FALSE, 0); + if (err == KERN_SUCCESS) + goto retry; + } + if (flag) { + db_printf("\nno memory is assigned to address %08x\n", addr); + } + return(-1); + } + + *paddr = pte_to_pa(*ptp) + (addr & (INTEL_PGBYTES-1)); + return(0); +} + +int +db_user_to_kernel_address( + const task_t task, + vm_offset_t addr, + vm_offset_t *kaddr, + int flag) +{ + phys_addr_t paddr; + + if (db_user_to_phys_address(task, addr, &paddr, flag) < 0) + return(-1); + + if (paddr >= biosmem_directmap_end()) { + db_printf("\naddr %016llx is stored in highmem at physical %016llx, accessing it is not supported yet\n", (unsigned long long) addr, (unsigned long long) paddr); + return(-1); + } + + *kaddr = phystokv(paddr); + return(0); +} + +/* + * Read bytes from kernel address space for debugger. + */ + +boolean_t +db_read_bytes( + vm_offset_t addr, + int size, + char *data, + task_t task) +{ + char *src; + int n; + phys_addr_t phys_addr; + + src = (char *)addr; + if ((addr >= VM_MIN_KERNEL_ADDRESS && addr < VM_MAX_KERNEL_ADDRESS) || task == TASK_NULL) { + if (task == TASK_NULL) + task = db_current_task(); + while (--size >= 0) { + if (addr < VM_MIN_KERNEL_ADDRESS && task == TASK_NULL) { + db_printf("\nbad address %x\n", addr); + return FALSE; + } + addr++; + *data++ = *src++; + } + return TRUE; + } + while (size > 0) { + if (db_user_to_phys_address(task, addr, &phys_addr, 1) < 0) + return FALSE; + n = intel_trunc_page(addr+INTEL_PGBYTES) - addr; + if (n > size) + n = size; + size -= n; + addr += n; + copy_from_phys(phys_addr, (vm_offset_t) data, n); + data += n; + } + return TRUE; +} + +/* + * Write bytes to kernel address space for debugger. + */ +void +db_write_bytes( + vm_offset_t addr, + int size, + char *data, + task_t task) +{ + char *dst; + + pt_entry_t *ptep0 = 0; + pt_entry_t oldmap0 = 0; + vm_offset_t addr1; + pt_entry_t *ptep1 = 0; + pt_entry_t oldmap1 = 0; + extern char etext; + + if ((addr < VM_MIN_KERNEL_ADDRESS) ^ + ((addr + size) <= VM_MIN_KERNEL_ADDRESS)) { + db_error("\ncannot write data into mixed space\n"); + /* NOTREACHED */ + } + if (addr < VM_MIN_KERNEL_ADDRESS) { + if (task) { + db_write_bytes_user_space(addr, size, data, task); + return; + } else if (db_current_task() == TASK_NULL) { + db_printf("\nbad address %x\n", addr); + db_error(0); + /* NOTREACHED */ + } + } + + if (addr >= VM_MIN_KERNEL_ADDRESS && + addr <= (vm_offset_t)&etext) + { + ptep0 = pmap_pte(kernel_pmap, addr); + oldmap0 = *ptep0; + *ptep0 |= INTEL_PTE_WRITE; + + addr1 = i386_trunc_page(addr + size - 1); + if (i386_trunc_page(addr) != addr1) { + /* data crosses a page boundary */ + + ptep1 = pmap_pte(kernel_pmap, addr1); + oldmap1 = *ptep1; + *ptep1 |= INTEL_PTE_WRITE; + } + if (CPU_HAS_FEATURE(CPU_FEATURE_PGE)) + set_cr4(get_cr4() & ~CR4_PGE); + flush_tlb(); + } + + dst = (char *)addr; + + while (--size >= 0) + *dst++ = *data++; + + if (ptep0) { + *ptep0 = oldmap0; + if (ptep1) { + *ptep1 = oldmap1; + } + flush_tlb(); + if (CPU_HAS_FEATURE(CPU_FEATURE_PGE)) + set_cr4(get_cr4() | CR4_PGE); + } +} + +void +db_write_bytes_user_space( + vm_offset_t addr, + int size, + char *data, + task_t task) +{ + int n; + phys_addr_t phys_addr; + + while (size > 0) { + if (db_user_to_phys_address(task, addr, &phys_addr, 1) < 0) + return; + n = intel_trunc_page(addr+INTEL_PGBYTES) - addr; + if (n > size) + n = size; + size -= n; + addr += n; + copy_to_phys((vm_offset_t) data, phys_addr, n); + } +} + +boolean_t +db_check_access( + vm_offset_t addr, + int size, + task_t task) +{ + int n; + phys_addr_t phys_addr; + + if (addr >= VM_MIN_KERNEL_ADDRESS) { + if (kernel_task == TASK_NULL) + return TRUE; + task = kernel_task; + } else if (task == TASK_NULL) { + if (current_thread() == THREAD_NULL) + return FALSE; + task = current_thread()->task; + } + while (size > 0) { + if (db_user_to_phys_address(task, addr, &phys_addr, 0) < 0) + return FALSE; + n = intel_trunc_page(addr+INTEL_PGBYTES) - addr; + if (n > size) + n = size; + size -= n; + addr += n; + } + return TRUE; +} + +boolean_t +db_phys_eq( + task_t task1, + vm_offset_t addr1, + const task_t task2, + vm_offset_t addr2) +{ + phys_addr_t phys_addr1, phys_addr2; + + if (addr1 >= VM_MIN_KERNEL_ADDRESS || addr2 >= VM_MIN_KERNEL_ADDRESS) + return FALSE; + if ((addr1 & (INTEL_PGBYTES-1)) != (addr2 & (INTEL_PGBYTES-1))) + return FALSE; + if (task1 == TASK_NULL) { + if (current_thread() == THREAD_NULL) + return FALSE; + task1 = current_thread()->task; + } + if (db_user_to_phys_address(task1, addr1, &phys_addr1, 0) < 0 + || db_user_to_phys_address(task2, addr2, &phys_addr2, 0) < 0) + return FALSE; + return(phys_addr1 == phys_addr2); +} + +#define DB_USER_STACK_ADDR (VM_MIN_KERNEL_ADDRESS) +#define DB_NAME_SEARCH_LIMIT (DB_USER_STACK_ADDR-(INTEL_PGBYTES*3)) + +#define GNU + +#ifndef GNU +static boolean_t +db_search_null( + const task_t task, + vm_offset_t *svaddr, + vm_offset_t evaddr, + vm_offset_t *skaddr, + int flag) +{ + unsigned vaddr; + unsigned *kaddr; + + kaddr = (unsigned *)*skaddr; + for (vaddr = *svaddr; vaddr > evaddr; ) { + if (vaddr % INTEL_PGBYTES == 0) { + vaddr -= sizeof(unsigned); + if (db_user_to_kernel_address(task, vaddr, skaddr, 0) < 0) + return FALSE; + kaddr = (vm_offset_t *)*skaddr; + } else { + vaddr -= sizeof(unsigned); + kaddr--; + } + if ((*kaddr == 0) ^ (flag == 0)) { + *svaddr = vaddr; + *skaddr = (unsigned)kaddr; + return TRUE; + } + } + return FALSE; +} +#endif /* GNU */ + +#ifdef GNU +static boolean_t +looks_like_command( + const task_t task, + char* kaddr) +{ + char *c; + + assert(!((vm_offset_t) kaddr & (INTEL_PGBYTES-1))); + + /* + * Must be the environment. + */ + if (!memcmp(kaddr, "PATH=", 5) || !memcmp(kaddr, "TERM=", 5) || !memcmp(kaddr, "SHELL=", 6) || !memcmp(kaddr, "LOCAL_PART=", 11) || !memcmp(kaddr, "LC_ALL=", 7)) + return FALSE; + + /* + * This is purely heuristical but works quite nicely. + * We know that it should look like words separated by \0, and + * eventually only \0s. + */ + c = kaddr; + while (c < kaddr + INTEL_PGBYTES) { + if (!*c) { + if (c == kaddr) + /* Starts by \0. */ + return FALSE; + break; + } + while (c < kaddr + INTEL_PGBYTES && *c) + c++; + if (c < kaddr + INTEL_PGBYTES) + c++; /* Skip \0 */ + } + /* + * Check that the remainder is just \0s. + */ + while (c < kaddr + INTEL_PGBYTES) + if (*c++) + return FALSE; + + return TRUE; +} +#endif /* GNU */ + +void +db_task_name( + const task_t task) +{ + char *p; + int n; + vm_offset_t vaddr, kaddr; + unsigned sp; + + if (task->name[0]) { + db_printf("%s", task->name); + return; + } + +#ifdef GNU + /* + * GNU Hurd-specific heuristics. + */ + + /* Heuristical address first. */ + vaddr = 0x1026000; + if (db_user_to_kernel_address(task, vaddr, &kaddr, 0) >= 0 && + looks_like_command(task, (char*) kaddr)) + goto ok; + + /* Try to catch SP of the main thread. */ + thread_t thread; + + task_lock(task); + thread = (thread_t) queue_first(&task->thread_list); + if (!thread) { + task_unlock(task); + db_printf(DB_NULL_TASK_NAME); + return; + } + sp = thread->pcb->iss.uesp; + task_unlock(task); + + vaddr = (sp & ~(INTEL_PGBYTES - 1)) + INTEL_PGBYTES; + while (1) { + if (db_user_to_kernel_address(task, vaddr, &kaddr, 0) < 0) + return; + if (looks_like_command(task, (char*) kaddr)) + break; + vaddr += INTEL_PGBYTES; + } +#else /* GNU */ + vaddr = DB_USER_STACK_ADDR; + kaddr = 0; + + /* + * skip nulls at the end + */ + if (!db_search_null(task, &vaddr, DB_NAME_SEARCH_LIMIT, &kaddr, 0)) { + db_printf(DB_NULL_TASK_NAME); + return; + } + /* + * search start of args + */ + if (!db_search_null(task, &vaddr, DB_NAME_SEARCH_LIMIT, &kaddr, 1)) { + db_printf(DB_NULL_TASK_NAME); + return; + } +#endif /* GNU */ + +ok: + n = DB_TASK_NAME_LEN-1; +#ifdef GNU + p = (char *)kaddr; + for (; n > 0; vaddr++, p++, n--) { +#else /* GNU */ + p = (char *)kaddr + sizeof(unsigned); + for (vaddr += sizeof(int); vaddr < DB_USER_STACK_ADDR && n > 0; + vaddr++, p++, n--) { +#endif /* GNU */ + if (vaddr % INTEL_PGBYTES == 0) { + (void)db_user_to_kernel_address(task, vaddr, &kaddr, 0); + p = (char*)kaddr; + } + db_printf("%c", (*p < ' ' || *p > '~')? ' ': *p); + } + while (n-- >= 0) /* compare with >= 0 for one more space */ + db_printf(" "); +} + +#endif /* MACH_KDB */ diff --git a/i386/i386/db_interface.h b/i386/i386/db_interface.h new file mode 100644 index 0000000..69a277a --- /dev/null +++ b/i386/i386/db_interface.h @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2007 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Barry deFreese. + */ + +#ifndef _I386_DB_INTERFACE_H_ +#define _I386_DB_INTERFACE_H_ + +#include <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, ¬ify); + assert(notify == IP_NULL); + + *new = io_perm; + + if (CONTAINS_PCI_CFG(from, to)) + taken_pci_cfg = TRUE; + + return KERN_SUCCESS; +} + +/* Modify the I/O permissions for TARGET_TASK. If ENABLE is TRUE, the + permission to access the I/O ports specified by IO_PERM is granted, + otherwise it is withdrawn. + + The function returns KERN_INVALID_ARGUMENT if TARGET_TASK is not a valid + task or IO_PERM not a valid I/O permission port. + + The function is exported. */ +kern_return_t +i386_io_perm_modify (task_t target_task, io_perm_t io_perm, boolean_t enable) +{ + io_port_t from, to; + unsigned char *iopb; + io_port_t iopb_size; + + if (target_task == TASK_NULL || io_perm == IO_PERM_NULL) + return KERN_INVALID_ARGUMENT; + + from = io_perm->from; + to = io_perm->to; + + simple_lock (&target_task->machine.iopb_lock); + iopb = target_task->machine.iopb; + iopb_size = target_task->machine.iopb_size; + + if (!enable && !iopb_size) + { + simple_unlock (&target_task->machine.iopb_lock); + return KERN_SUCCESS; + } + + if (!iopb) + { + simple_unlock (&target_task->machine.iopb_lock); + iopb = (unsigned char *) kmem_cache_alloc (&machine_task_iopb_cache); + simple_lock (&target_task->machine.iopb_lock); + if (target_task->machine.iopb) + { + if (iopb) + kmem_cache_free (&machine_task_iopb_cache, (vm_offset_t) iopb); + iopb = target_task->machine.iopb; + iopb_size = target_task->machine.iopb_size; + } + else if (iopb) + { + target_task->machine.iopb = iopb; + io_bitmap_init (iopb); + } + else + { + simple_unlock (&target_task->machine.iopb_lock); + return KERN_RESOURCE_SHORTAGE; + } + } + + if (enable) + { + io_bitmap_set (iopb, from, to); + if ((to >> 3) + 1 > iopb_size) + target_task->machine.iopb_size = (to >> 3) + 1; + } + else + { + if ((from >> 3) + 1 > iopb_size) + { + simple_unlock (&target_task->machine.iopb_lock); + return KERN_SUCCESS; + } + + io_bitmap_clear (iopb, from, to); + while (iopb_size > 0 && iopb[iopb_size - 1] == 0xff) + iopb_size--; + target_task->machine.iopb_size = iopb_size; + } + +#if NCPUS>1 +#warning SMP support missing (notify all CPUs running threads in that of the I/O bitmap change). +#endif + if (target_task == current_task()) + update_ktss_iopb (iopb, target_task->machine.iopb_size); + + simple_unlock (&target_task->machine.iopb_lock); + return KERN_SUCCESS; +} + +/* We are some sort of Mach device... */ +static struct device_emulation_ops io_perm_device_emulation_ops = +{ + /* ... in order to be easily able to receive a ``no senders'' notification + which we then use to deallocate ourselves. */ + .no_senders = no_senders +}; diff --git a/i386/i386/io_perm.h b/i386/i386/io_perm.h new file mode 100644 index 0000000..b97cf97 --- /dev/null +++ b/i386/i386/io_perm.h @@ -0,0 +1,63 @@ +/* Data types for I/O permission bitmap objects. + + Copyright (C) 2002, 2007 Free Software Foundation, Inc. + + Written by Marcus Brinkmann. Glued into GNU Mach by Thomas Schwinge. + + This file is part of GNU Mach. + + GNU Mach is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any later + version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _I386_IO_PERM_H_ +#define _I386_IO_PERM_H_ + +#include <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)®s->uesp; /* kernel stack pointer */ + kttd_regs.ss = KERNEL_DS; + } + + /* + * If this was not entered via an interrupt (type != -1) + * then we've entered via a bpt, single, etc. and must + * set the globals. + * + * Setup the kttd globals for entry.... + */ + if (type != -1) { + kttd_current_request = NULL; + kttd_current_length = 0; + kttd_current_kmsg = NULL; + kttd_run_status = FULL_STOP; + }else{ + /* + * We know that we can only get here if we did a kttd_intr + * since it's the way that we are called with type -1 (via + * the trampoline), so we don't have to worry about entering + * from Cntl-Alt-D like the mips does. + */ + /* + * Perform sanity check! + */ + if ((kttd_current_request == NULL) || + (kttd_current_length == 0) || + (kttd_current_kmsg == NULL) || + (kttd_run_status != ONE_STOP)) { + + printf("kttd_trap: INSANITY!!!\n"); + } + } + + kttd_task_trap(type, code, (regs->cs & 0x3) != 0); + + regs->eip = kttd_regs.eip; + regs->efl = kttd_regs.efl; + regs->eax = kttd_regs.eax; + regs->ecx = kttd_regs.ecx; + regs->edx = kttd_regs.edx; + regs->ebx = kttd_regs.ebx; + if ((regs->cs & 0x3) != KERNEL_RING) { + /* + * user mode - saved esp and ss valid + */ + regs->uesp = kttd_regs.uesp; /* user stack pointer */ + regs->ss = kttd_regs.ss & 0xffff; /* user stack segment */ + } + regs->ebp = kttd_regs.ebp; + regs->esi = kttd_regs.esi; + regs->edi = kttd_regs.edi; + regs->es = kttd_regs.es & 0xffff; + regs->cs = kttd_regs.cs & 0xffff; + regs->ds = kttd_regs.ds & 0xffff; + regs->fs = kttd_regs.fs & 0xffff; + regs->gs = kttd_regs.gs & 0xffff; + + if (--kttd_active < MIN_KTTD_ACTIVE) + printf("ttd_trap: kttd_active < 0\n"); + + if (kttd_debug) { + printf("Leaving kttd_trap, kttd_active = %d\n", kttd_active); + } + + /* + * Only reset this if we entered kttd_trap via an async trampoline. + */ + if (type == -1) { + if (kttd_run_status == RUNNING) + printf("kttd_trap: $$$$$ run_status already RUNNING! $$$$$\n"); + kttd_run_status = RUNNING; + } + + /* Is this right? XXX */ + kttd_run_status = RUNNING; + + (void) splx(s); + + /* + * Return true, that yes we handled the trap. + */ + return TRUE; +} + +/* + * Enter KTTD through a network packet trap. + * We show the registers as of the network interrupt + * instead of those at its call to KDB. + */ +struct int_regs { + int edi; + int esi; + int ebp; + int ebx; + struct i386_interrupt_state *is; +}; + +void +kttd_netentry(struct int_regs *int_regs) +{ + struct i386_interrupt_state *is = int_regs->is; + int s; + + if (kttd_debug) + printf("kttd_NETENTRY before slphigh()\n"); + + s = splhigh(); + + if (kttd_debug) + printf("kttd_NETENTRY after slphigh()\n"); + + if ((is->cs & 0x3) != KERNEL_RING) { + /* + * Interrupted from User Space + */ + kttd_regs.uesp = ((int *)(is+1))[0]; + kttd_regs.ss = ((int *)(is+1))[1]; + } + else { + /* + * Interrupted from Kernel Space + */ + kttd_regs.ss = KERNEL_DS; + kttd_regs.uesp= (int)(is+1); + } + kttd_regs.efl = is->efl; + kttd_regs.cs = is->cs; + kttd_regs.eip = is->eip; + kttd_regs.eax = is->eax; + kttd_regs.ecx = is->ecx; + kttd_regs.edx = is->edx; + kttd_regs.ebx = int_regs->ebx; + kttd_regs.ebp = int_regs->ebp; + kttd_regs.esi = int_regs->esi; + kttd_regs.edi = int_regs->edi; + kttd_regs.ds = is->ds; + kttd_regs.es = is->es; + kttd_regs.fs = is->fs; + kttd_regs.gs = is->gs; + + kttd_active++; + kttd_task_trap(-1, 0, (kttd_regs.cs & 0x3) != 0); + kttd_active--; + + if ((kttd_regs.cs & 0x3) != KERNEL_RING) { + ((int *)(is+1))[0] = kttd_regs.uesp; + ((int *)(is+1))[1] = kttd_regs.ss & 0xffff; + } + is->efl = kttd_regs.efl; + is->cs = kttd_regs.cs & 0xffff; + is->eip = kttd_regs.eip; + is->eax = kttd_regs.eax; + is->ecx = kttd_regs.ecx; + is->edx = kttd_regs.edx; + int_regs->ebx = kttd_regs.ebx; + int_regs->ebp = kttd_regs.ebp; + int_regs->esi = kttd_regs.esi; + int_regs->edi = kttd_regs.edi; + is->ds = kttd_regs.ds & 0xffff; + is->es = kttd_regs.es & 0xffff; + is->fs = kttd_regs.fs & 0xffff; + is->gs = kttd_regs.gs & 0xffff; + + if (kttd_run_status == RUNNING) + printf("kttd_netentry: %%%%% run_status already RUNNING! %%%%%\n"); + kttd_run_status = RUNNING; + + (void) splx(s); +} + +#endif /* MACH_TTD */ diff --git a/i386/i386/kttd_machdep.h b/i386/i386/kttd_machdep.h new file mode 100644 index 0000000..8ac7de1 --- /dev/null +++ b/i386/i386/kttd_machdep.h @@ -0,0 +1,59 @@ +/* + * Mach Operating System + * Copyright (c) 1992 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +#ifndef _KTTD_MACHDEP_H_ +#define _KTTD_MACHDEP_H_ + +#define MAX_KTTD_ACTIVE 2 +#define MIN_KTTD_ACTIVE 0 + +/* + * Register state for gdb + */ +struct i386_gdb_register_state { + int eax; + int ecx; + int edx; + int ebx; + int esp; /* 4 */ + int ebp; /* 5 */ + int esi; + int edi; + int eip; /* 8 */ + int efl; /* 9 */ + int cs; + int ss; + int ds; + int es; + int fs; + int gs; +}; + +typedef struct i386_gdb_register_state ttd_machine_state; + +typedef unsigned long ttd_saved_inst; + +#endif /* _KTTD_MACHDEP_H_ */ diff --git a/i386/i386/ldt.c b/i386/i386/ldt.c new file mode 100644 index 0000000..5db3642 --- /dev/null +++ b/i386/i386/ldt.c @@ -0,0 +1,117 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * "Local" descriptor table. At the moment, all tasks use the + * same LDT. + */ +#include <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 |