diff options
author | Thomas Bushnell <thomas@gnu.org> | 1997-02-25 21:28:37 +0000 |
---|---|---|
committer | Thomas Bushnell <thomas@gnu.org> | 1997-02-25 21:28:37 +0000 |
commit | f07a4c844da9f0ecae5bbee1ab94be56505f26f7 (patch) | |
tree | 12b07c7e578fc1a5f53dbfde2632408491ff2a70 /i386/i386 |
Initial source
Diffstat (limited to 'i386/i386')
74 files changed, 15193 insertions, 0 deletions
diff --git a/i386/i386/ast.h b/i386/i386/ast.h new file mode 100644 index 00000000..7afaa41a --- /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 00000000..faa3b8ed --- /dev/null +++ b/i386/i386/ast_check.c @@ -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. + */ + +#include <cpus.h> + +#if NCPUS > 1 + +/* + * Handle signalling ASTs on other processors. + * + * Initial i386 implementation does nothing. + */ + +#include <kern/processor.h> + +/* + * Initialize for remote invocation of ast_check. + */ +init_ast_check(processor) + processor_t processor; +{ +#ifdef lint + processor++; +#endif lint +} + +/* + * Cause remote invocation of ast_check. Caller is at splsched(). + */ +cause_ast_check(processor) + processor_t processor; +{ +#ifdef lint + processor++; +#endif lint +} + +#endif /* NCPUS > 1 */ diff --git a/i386/i386/ast_types.h b/i386/i386/ast_types.h new file mode 100644 index 00000000..89e31825 --- /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/cpu_number.h b/i386/i386/cpu_number.h new file mode 100644 index 00000000..e60ac771 --- /dev/null +++ b/i386/i386/cpu_number.h @@ -0,0 +1,49 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * Machine-dependent definitions for cpu identification. + * + */ +#ifndef _I386_CPU_NUMBER_H_ +#define _I386_CPU_NUMBER_H_ + +#if NCPUS > 1 + +/* More-specific code must define cpu_number() and CPU_NUMBER. */ +#define CX(addr, reg) addr(,reg,4) + +#else /* NCPUS == 1 */ + +#define CPU_NUMBER(reg) +#define CX(addr,reg) addr + +#endif /* NCPUS == 1 */ + +#ifndef ASSEMBLER +#include "kern/cpu_number.h" +#endif + +#endif /* _I386_CPU_NUMBER_H_ */ diff --git a/i386/i386/cswitch.S b/i386/i386/cswitch.S new file mode 100644 index 00000000..8187980f --- /dev/null +++ b/i386/i386/cswitch.S @@ -0,0 +1,142 @@ +/* + * 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 <cpus.h> +#include <platforms.h> + +#include <mach/machine/asm.h> + +#include "proc_reg.h" +#include "i386asm.h" +#include "cpu_number.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,CX(EXT(active_stacks),%eax) /* 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) + CPU_NUMBER(%edx) + movl CX(EXT(active_stacks),%edx),%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 */ + + movl %esi,CX(EXT(active_threads),%edx) /* new thread is active */ + movl %ecx,CX(EXT(active_stacks),%edx) /* 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) + CPU_NUMBER(%edx) + movl EXT(active_stacks)(,%edx,4),%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 */ + + movl _interrupt_stack(,%edx,4),%ecx /* point to its interrupt stack */ + lea 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 00000000..dfb85e2c --- /dev/null +++ b/i386/i386/db_disasm.c @@ -0,0 +1,1423 @@ +/* + * 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. + */ +#include "mach_kdb.h" +#if MACH_KDB + +/* + * Instruction disassembler. + */ +#include <mach/boolean.h> +#include <machine/db_machdep.h> + +#include <ddb/db_access.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' */ + +struct inst { + char * i_name; /* name */ + short i_has_modrm; /* has regmodrm byte */ + short i_size; /* operand size */ + int i_mode; /* addressing modes */ + char * i_extra; /* pointer to extra opcode table */ +}; + +#define op1(x) (x) +#define op2(x,y) ((x)|((y)<<8)) +#define op3(x,y,z) ((x)|((y)<<8)|((z)<<16)) + +struct finst { + char * f_name; /* name for memory instruction */ + int f_size; /* size for memory instruction */ + int f_rrmode; /* mode for rr instruction */ + char * f_rrname; /* name for rr instruction + (or pointer to table) */ +}; + +char * db_Grp6[] = { + "sldt", + "str", + "lldt", + "ltr", + "verr", + "verw", + "", + "" +}; + +char * db_Grp7[] = { + "sgdt", + "sidt", + "lgdt", + "lidt", + "smsw", + "", + "lmsw", + "invlpg" +}; + +char * db_Grp8[] = { + "", + "", + "", + "", + "bt", + "bts", + "btr", + "btc" +}; + +struct inst db_inst_0f0x[] = { +/*00*/ { "", TRUE, NONE, op1(Ew), (char *)db_Grp6 }, +/*01*/ { "", TRUE, NONE, op1(Ew), (char *)db_Grp7 }, +/*02*/ { "lar", TRUE, LONG, op2(E,R), 0 }, +/*03*/ { "lsl", TRUE, LONG, op2(E,R), 0 }, +/*04*/ { "", FALSE, NONE, 0, 0 }, +/*05*/ { "", FALSE, NONE, 0, 0 }, +/*06*/ { "clts", FALSE, NONE, 0, 0 }, +/*07*/ { "", FALSE, NONE, 0, 0 }, + +/*08*/ { "invd", FALSE, NONE, 0, 0 }, +/*09*/ { "wbinvd",FALSE, NONE, 0, 0 }, +/*0a*/ { "", FALSE, NONE, 0, 0 }, +/*0b*/ { "", FALSE, NONE, 0, 0 }, +/*0c*/ { "", FALSE, NONE, 0, 0 }, +/*0d*/ { "", FALSE, NONE, 0, 0 }, +/*0e*/ { "", FALSE, NONE, 0, 0 }, +/*0f*/ { "", FALSE, NONE, 0, 0 }, +}; + +struct inst db_inst_0f2x[] = { +/*20*/ { "mov", TRUE, LONG, op2(CR,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", TRUE, NONE, 0, 0 }, +/*d5*/ { "aad", TRUE, NONE, 0, 0 }, +/*d6*/ { "", FALSE, NONE, 0, 0 }, +/*d7*/ { "xlat", FALSE, BYTE, op1(BX), 0 }, + +/*d8*/ { "", TRUE, NONE, 0, (char *)db_Esc8 }, +/*d9*/ { "", TRUE, NONE, 0, (char *)db_Esc9 }, +/*da*/ { "", TRUE, NONE, 0, (char *)db_Esca }, +/*db*/ { "", TRUE, NONE, 0, (char *)db_Escb }, +/*dc*/ { "", TRUE, NONE, 0, (char *)db_Escc }, +/*dd*/ { "", TRUE, NONE, 0, (char *)db_Escd }, +/*de*/ { "", TRUE, NONE, 0, (char *)db_Esce }, +/*df*/ { "", TRUE, NONE, 0, (char *)db_Escf }, + +/*e0*/ { "loopne",FALSE, NONE, op1(Db), 0 }, +/*e1*/ { "loope", FALSE, NONE, op1(Db), 0 }, +/*e2*/ { "loop", FALSE, NONE, op1(Db), 0 }, +/*e3*/ { "jcxz", FALSE, SDEP, op1(Db), "jecxz" }, +/*e4*/ { "in", FALSE, BYTE, op2(Ib, A), 0 }, +/*e5*/ { "in", FALSE, LONG, op2(Ib, A) , 0 }, +/*e6*/ { "out", FALSE, BYTE, op2(A, Ib), 0 }, +/*e7*/ { "out", FALSE, LONG, op2(A, Ib) , 0 }, + +/*e8*/ { "call", FALSE, NONE, op1(Dl), 0 }, +/*e9*/ { "jmp", FALSE, NONE, op1(Dl), 0 }, +/*ea*/ { "ljmp", FALSE, NONE, op1(OS), 0 }, +/*eb*/ { "jmp", FALSE, NONE, op1(Db), 0 }, +/*ec*/ { "in", FALSE, BYTE, op2(DX, A), 0 }, +/*ed*/ { "in", FALSE, LONG, op2(DX, A) , 0 }, +/*ee*/ { "out", FALSE, BYTE, op2(A, DX), 0 }, +/*ef*/ { "out", FALSE, LONG, op2(A, DX) , 0 }, + +/*f0*/ { "", FALSE, NONE, 0, 0 }, +/*f1*/ { "", FALSE, NONE, 0, 0 }, +/*f2*/ { "", FALSE, NONE, 0, 0 }, +/*f3*/ { "", FALSE, NONE, 0, 0 }, +/*f4*/ { "hlt", FALSE, NONE, 0, 0 }, +/*f5*/ { "cmc", FALSE, NONE, 0, 0 }, +/*f6*/ { "", TRUE, BYTE, 0, (char *)db_Grp3 }, +/*f7*/ { "", TRUE, LONG, 0, (char *)db_Grp3 }, + +/*f8*/ { "clc", FALSE, NONE, 0, 0 }, +/*f9*/ { "stc", FALSE, NONE, 0, 0 }, +/*fa*/ { "cli", FALSE, NONE, 0, 0 }, +/*fb*/ { "sti", FALSE, NONE, 0, 0 }, +/*fc*/ { "cld", FALSE, NONE, 0, 0 }, +/*fd*/ { "std", FALSE, NONE, 0, 0 }, +/*fe*/ { "", TRUE, NONE, 0, (char *)db_Grp4 }, +/*ff*/ { "", TRUE, NONE, 0, (char *)db_Grp5 }, +}; + +struct inst db_bad_inst = + { "???", FALSE, NONE, 0, 0 } +; + +#define f_mod(byte) ((byte)>>6) +#define f_reg(byte) (((byte)>>3)&0x7) +#define f_rm(byte) ((byte)&0x7) + +#define sib_ss(byte) ((byte)>>6) +#define sib_index(byte) (((byte)>>3)&0x7) +#define sib_base(byte) ((byte)&0x7) + +struct i_addr { + int is_reg; /* if reg, reg number is in 'disp' */ + int disp; + char * base; + char * index; + int ss; +}; + +char * db_index_reg_16[8] = { + "%bx,%si", + "%bx,%di", + "%bp,%si", + "%bp,%di", + "%si", + "%di", + "%bp", + "%bx" +}; + +char * db_reg[3][8] = { + { "%al", "%cl", "%dl", "%bl", "%ah", "%ch", "%dh", "%bh" }, + { "%ax", "%cx", "%dx", "%bx", "%sp", "%bp", "%si", "%di" }, + { "%eax", "%ecx", "%edx", "%ebx", "%esp", "%ebp", "%esi", "%edi" } +}; + +char * db_seg_reg[8] = { + "%es", "%cs", "%ss", "%ds", "%fs", "%gs", "", "" +}; + +/* + * lengths for size attributes + */ +int db_lengths[] = { + 1, /* BYTE */ + 2, /* WORD */ + 4, /* LONG */ + 8, /* QUAD */ + 4, /* SNGL */ + 8, /* DBLR */ + 10, /* EXTR */ +}; + +#define get_value_inc(result, loc, size, is_signed, task) \ + result = db_get_task_value((loc), (size), (is_signed), (task)); \ + (loc) += (size); + +/* + * Read address at location and return updated location. + */ +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; +} + +void +db_print_address( + char * seg, + int size, + 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. + */ +db_addr_t +db_disasm_esc( + db_addr_t loc, + int inst, + int short_addr, + int size, + 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; + + 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 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 00000000..30b0b0f3 --- /dev/null +++ b/i386/i386/db_interface.c @@ -0,0 +1,558 @@ +/* + * 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 "mach_kdb.h" +#if MACH_KDB + +#include <cpus.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 "gdt.h" +#include "trap.h" + +#include "vm_param.h" +#include <vm/vm_map.h> +#include <kern/cpu_number.h> +#include <kern/thread.h> +#include <kern/task.h> +#include <ddb/db_task_thread.h> +#include <machine/machspl.h> + +struct i386_saved_state *i386_last_saved_statep; +struct i386_saved_state i386_nested_saved_state; +unsigned i386_last_kdb_sp; + +extern thread_t db_default_thread; + +/* + * Print trap reason. + */ +kdbprinttrap(type, code) + int type, 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, + register struct i386_saved_state *regs) +{ + spl_t s; + + s = splhigh(); + saved_ipl[cpu_number()] = s; + + switch (type) { + case T_DEBUG: /* single_step */ + { + extern int dr_addr[]; + int addr; + int status = dr6(); + + if (status & 0xf) { /* hmm hdw break */ + addr = status & 0x8 ? dr_addr[3] : + status & 0x4 ? dr_addr[2] : + status & 0x2 ? dr_addr[1] : + dr_addr[0]; + 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 = (unsigned) &type; + + /* XXX Should switch to ddb`s own stack here. */ + + ddb_regs = *regs; + if ((regs->cs & 0x3) == 0) { + /* + * Kernel mode - esp and ss not saved + */ + ddb_regs.uesp = (int)®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) { + /* + * 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->es = ddb_regs.es & 0xffff; + regs->cs = ddb_regs.cs & 0xffff; + regs->ds = ddb_regs.ds & 0xffff; + regs->fs = ddb_regs.fs & 0xffff; + regs->gs = ddb_regs.gs & 0xffff; + + 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 { + int gs; + int fs; + int edi; + int esi; + int ebp; + int 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) { + ddb_regs.uesp = ((int *)(is+1))[0]; + ddb_regs.ss = ((int *)(is+1))[1]; + } + else { + ddb_regs.ss = KERNEL_DS; + ddb_regs.uesp= (int)(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; + ddb_regs.esi = int_regs->esi; + ddb_regs.edi = int_regs->edi; + ddb_regs.ds = is->ds; + ddb_regs.es = is->es; + ddb_regs.fs = int_regs->fs; + ddb_regs.gs = int_regs->gs; + + cnpollc(TRUE); + db_task_trap(-1, 0, (ddb_regs.cs & 0x3) != 0); + cnpollc(FALSE); + + if (ddb_regs.cs & 0x3) { + ((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; + int_regs->esi = ddb_regs.esi; + int_regs->edi = ddb_regs.edi; + is->ds = ddb_regs.ds & 0xffff; + is->es = ddb_regs.es & 0xffff; + int_regs->fs = ddb_regs.fs & 0xffff; + int_regs->gs = ddb_regs.gs & 0xffff; + } +#if NCPUS > 1 + db_leave(); +#endif /* NCPUS > 1 */ + + (void) splx(s); +} + +boolean_t db_no_vm_fault = TRUE; + +int +db_user_to_kernel_address( + task_t task, + vm_offset_t addr, + unsigned *kaddr, + int flag) +{ + register 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); + db_error(0); + /* NOTREACHED */ + } + return(-1); + } + *kaddr = (unsigned)ptetokv(*ptp) + (addr & (INTEL_PGBYTES-1)); + return(0); +} + +/* + * Read bytes from kernel address space for debugger. + */ + +void +db_read_bytes( + vm_offset_t addr, + register int size, + register char *data, + task_t task) +{ + register char *src; + register int n; + unsigned kern_addr; + + src = (char *)addr; + if (addr >= VM_MIN_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); + db_error(0); + /* NOTREACHED */ + } + *data++ = *src++; + } + return; + } + while (size > 0) { + if (db_user_to_kernel_address(task, addr, &kern_addr, 1) < 0) + return; + src = (char *)kern_addr; + n = intel_trunc_page(addr+INTEL_PGBYTES) - addr; + if (n > size) + n = size; + size -= n; + addr += n; + while (--n >= 0) + *data++ = *src++; + } +} + +/* + * Write bytes to kernel address space for debugger. + */ +void +db_write_bytes( + vm_offset_t addr, + register int size, + register char *data, + task_t task) +{ + register char *dst; + + register pt_entry_t *ptep0 = 0; + pt_entry_t oldmap0 = 0; + vm_offset_t addr1; + register pt_entry_t *ptep1 = 0; + pt_entry_t oldmap1 = 0; + extern char etext; + void db_write_bytes_user_space(); + + 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; + } + flush_tlb(); + } + + dst = (char *)addr; + + while (--size >= 0) + *dst++ = *data++; + + if (ptep0) { + *ptep0 = oldmap0; + if (ptep1) { + *ptep1 = oldmap1; + } + flush_tlb(); + } +} + +void +db_write_bytes_user_space( + vm_offset_t addr, + register int size, + register char *data, + task_t task) +{ + register char *dst; + register int n; + unsigned kern_addr; + + while (size > 0) { + if (db_user_to_kernel_address(task, addr, &kern_addr, 1) < 0) + return; + dst = (char *)kern_addr; + n = intel_trunc_page(addr+INTEL_PGBYTES) - addr; + if (n > size) + n = size; + size -= n; + addr += n; + while (--n >= 0) + *dst++ = *data++; + } +} + +boolean_t +db_check_access( + vm_offset_t addr, + register int size, + task_t task) +{ + register n; + vm_offset_t kern_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_kernel_address(task, addr, &kern_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, + task_t task2, + vm_offset_t addr2) +{ + vm_offset_t kern_addr1, kern_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_kernel_address(task1, addr1, &kern_addr1, 0) < 0 + || db_user_to_kernel_address(task2, addr2, &kern_addr2, 0) < 0) + return FALSE; + return(kern_addr1 == kern_addr2); +} + +#define DB_USER_STACK_ADDR (VM_MIN_KERNEL_ADDRESS) +#define DB_NAME_SEARCH_LIMIT (DB_USER_STACK_ADDR-(INTEL_PGBYTES*3)) + +static boolean_t +db_search_null( + task_t task, + vm_offset_t *svaddr, + vm_offset_t evaddr, + vm_offset_t *skaddr, + int flag) +{ + register unsigned vaddr; + register unsigned *kaddr; + + kaddr = (unsigned *)*skaddr; + for (vaddr = *svaddr; vaddr > evaddr; vaddr -= sizeof(unsigned)) { + 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; +} + +void +db_task_name( + task_t task) +{ + register char *p; + register n; + unsigned vaddr, kaddr; + + 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; + } + + n = DB_TASK_NAME_LEN-1; + p = (char *)kaddr + sizeof(unsigned); + for (vaddr += sizeof(int); vaddr < DB_USER_STACK_ADDR && n > 0; + vaddr++, p++, n--) { + 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_machdep.h b/i386/i386/db_machdep.h new file mode 100644 index 00000000..ee5853a0 --- /dev/null +++ b/i386/i386/db_machdep.h @@ -0,0 +1,110 @@ +/* + * 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 int db_expr_t; /* expression - signed */ + +typedef struct i386_saved_state db_regs_t; +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)) + +boolean_t db_check_access(/* vm_offset_t, int, task_t */); +boolean_t db_phys_eq(/* task_t, vm_offset_t, task_t, vm_offset_t */); + +/* macros for printing OS server dependent task name */ + +#define DB_TASK_NAME(task) db_task_name(task) +#define DB_TASK_NAME_TITLE "COMMAND " +#define DB_TASK_NAME_LEN 23 +#define DB_NULL_TASK_NAME "? " + +void db_task_name(/* task_t */); + +/* macro for checking if a thread has used floating-point */ + +#define db_thread_fp_used(thread) ((thread)->pcb->ims.ifps != 0) + +/* 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 00000000..358d2b10 --- /dev/null +++ b/i386/i386/db_trace.c @@ -0,0 +1,674 @@ +/* + * 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 "mach_kdb.h" +#if MACH_KDB + +#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 <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" + +db_i386_reg_value( + struct db_variable *vp, + db_expr_t *valuep, + int flag, + struct db_var_aux_param *ap); /* forward */ + +/* + * Machine register set. + */ +struct db_variable db_regs[] = { + { "cs", (int *)&ddb_regs.cs, db_i386_reg_value }, + { "ds", (int *)&ddb_regs.ds, db_i386_reg_value }, + { "es", (int *)&ddb_regs.es, db_i386_reg_value }, + { "fs", (int *)&ddb_regs.fs, db_i386_reg_value }, + { "gs", (int *)&ddb_regs.gs, db_i386_reg_value }, + { "ss", (int *)&ddb_regs.ss, db_i386_reg_value }, + { "eax",(int *)&ddb_regs.eax, db_i386_reg_value }, + { "ecx",(int *)&ddb_regs.ecx, db_i386_reg_value }, + { "edx",(int *)&ddb_regs.edx, db_i386_reg_value }, + { "ebx",(int *)&ddb_regs.ebx, db_i386_reg_value }, + { "esp",(int *)&ddb_regs.uesp,db_i386_reg_value }, + { "ebp",(int *)&ddb_regs.ebp, db_i386_reg_value }, + { "esi",(int *)&ddb_regs.esi, db_i386_reg_value }, + { "edi",(int *)&ddb_regs.edi, db_i386_reg_value }, + { "eip",(int *)&ddb_regs.eip, db_i386_reg_value }, + { "efl",(int *)&ddb_regs.efl, db_i386_reg_value }, +}; +struct db_variable *db_eregs = db_regs + sizeof(db_regs)/sizeof(db_regs[0]); + +/* + * Stack trace. + */ +#define INKERNEL(va) (((vm_offset_t)(va)) >= VM_MIN_KERNEL_ADDRESS) + +struct i386_frame { + struct i386_frame *f_frame; + int f_retaddr; + int f_arg0; +}; + +#define TRAP 1 +#define INTERRUPT 2 +#define SYSCALL 3 + +db_addr_t db_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; + int offset; +} i386_kregs[] = { + { "ebx", (int)(&((struct i386_kernel_state *)0)->k_ebx) }, + { "esp", (int)(&((struct i386_kernel_state *)0)->k_esp) }, + { "ebp", (int)(&((struct i386_kernel_state *)0)->k_ebp) }, + { "edi", (int)(&((struct i386_kernel_state *)0)->k_edi) }, + { "esi", (int)(&((struct i386_kernel_state *)0)->k_esi) }, + { "eip", (int)(&((struct i386_kernel_state *)0)->k_eip) }, + { 0 }, +}; + +int * +db_lookup_i386_kreg( + char *name, + int *kregp) +{ + register struct i386_kregs *kp; + + for (kp = i386_kregs; kp->name; kp++) { + if (strcmp(name, kp->name) == 0) + return (int *)((int)kregp + kp->offset); + } + return 0; +} + +db_i386_reg_value( + struct db_variable *vp, + db_expr_t *valuep, + int flag, + db_var_aux_param_t ap) +{ + int *dp = 0; + db_expr_t null_reg = 0; + register thread_t thread = ap->thread; + extern unsigned int_stack_high; + + 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 (ddb_regs.ebp < int_stack_high) + 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, + (int *)(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 == (int *) &ddb_regs.eip) { + dp = (int *)(&thread->swap_func); + } else { + dp = &null_reg; + } + } + } + if (dp == 0) { + if (thread->pcb == 0) + db_error("no pcb\n"); + dp = (int *)((int)(&thread->pcb->iss) + + ((int)vp->valuep - (int)&ddb_regs)); + } + if (flag == DB_VAR_SET) + *dp = *valuep; + else + *valuep = *dp; +} + +void +db_find_trace_symbols(void) +{ + db_expr_t value; + if (db_value_of_name("_user_trap", &value)) + db_user_trap_symbol_value = (db_addr_t) value; + if (db_value_of_name("_kernel_trap", &value)) + db_kernel_trap_symbol_value = (db_addr_t) value; + if (db_value_of_name("_interrupt", &value)) + db_interrupt_symbol_value = (db_addr_t) value; + if (db_value_of_name("_return_to_iret", &value)) + db_return_to_iret_symbol_value = (db_addr_t) value; + if (db_value_of_name("_syscall", &value)) + db_syscall_symbol_value = (db_addr_t) value; + db_trace_symbols_found = TRUE; +} + +/* + * Figure out how many arguments were passed into the frame at "fp". + */ +int db_numargs_default = 5; + +int +db_numargs( + struct i386_frame *fp, + task_t task) +{ + int *argp; + int inst; + int args; + extern char etext[]; + + argp = (int *)db_get_task_value((int)&fp->f_retaddr, 4, FALSE, task); + if (argp < (int *)VM_MIN_KERNEL_ADDRESS || argp > (int *)etext) + args = db_numargs_default; + else if (!DB_CHECK_ACCESS((int)argp, 4, task)) + args = db_numargs_default; + else { + inst = db_get_task_value((int)argp, 4, 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; +} + +struct interrupt_frame { + struct i386_frame *if_frame; /* point to next frame */ + int if_retaddr; /* return address to _interrupt */ + int if_unit; /* unit number */ + spl_t if_spl; /* saved spl */ + int if_iretaddr; /* _return_to_{iret,iret_i} */ + int if_edx; /* old sp(iret) or saved edx(iret_i) */ + int if_ecx; /* saved ecx(iret_i) */ + int if_eax; /* saved eax(iret_i) */ + int if_eip; /* saved eip(iret_i) */ + int if_cs; /* saved cs(iret_i) */ + int 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. + */ +void +db_nextframe( + struct i386_frame **lfp, /* in/out */ + struct i386_frame **fp, /* in/out */ + db_addr_t *ip, /* out */ + int frame_type, /* in */ + 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((int)&((*fp)->f_arg0),4,FALSE,task); + db_printf(">>>>> %s (%d) at ", + trap_name(saved_regs->trapno), saved_regs->trapno); + db_task_printsym(saved_regs->eip, DB_STGY_PROC, task); + db_printf(" <<<<<\n"); + *fp = (struct i386_frame *)saved_regs->ebp; + *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; + 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; + *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((int)&(*fp)->f_retaddr, 4, FALSE, task); + *lfp = *fp; + *fp = (struct i386_frame *) + db_get_task_value((int)&(*fp)->f_frame, 4, FALSE, task); + break; + } +} + +void +db_i386_stack_trace( + thread_t th, + struct i386_frame *frame, + db_addr_t callpc, + db_expr_t count, + int flags); /* forward */ + +#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, + char *modif) +{ + boolean_t trace_thread = FALSE; + struct i386_frame *frame; + db_addr_t callpc; + int flags = 0; + thread_t th; + + { + register char *cp = modif; + register 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; + 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((db_addr_t)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; + 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) { + register struct i386_saved_state *iss = &th->pcb->iss; + + db_printf("Continuation "); + db_task_printsym((db_expr_t)th->swap_func, + DB_STGY_PROC, + th->task); + db_printf("\n"); + + frame = (struct i386_frame *) (iss->ebp); + callpc = (db_addr_t) (iss->eip); + } else { + register struct i386_kernel_state *iks; + iks = STACK_IKS(th->kernel_stack); + frame = (struct i386_frame *) (iks->k_ebp); + callpc = (db_addr_t) (iks->k_eip); + } + } + } else { + frame = (struct i386_frame *)addr; + th = (db_default_thread)? db_default_thread: current_thread(); + callpc = (db_addr_t)db_get_task_value((int)&frame->f_retaddr, 4, + FALSE, + (th == THREAD_NULL) ? TASK_NULL : th->task); + } + + db_i386_stack_trace( th, frame, callpc, count, flags ); +} + + +void +db_i386_stack_trace( + thread_t th, + struct i386_frame *frame, + db_addr_t callpc, + db_expr_t count, + int flags) +{ + task_t task; + boolean_t kernel_only; + int *argp; + int user_frame = 0; + struct i386_frame *lastframe; + int frame_type; + char *filename; + int linenum; + extern unsigned int 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((unsigned)callpc) && !INKERNEL((unsigned)frame)) { + db_printf(">>>>> user space <<<<<\n"); + user_frame++; + } + + lastframe = 0; + while (count-- && frame != 0) { + register int narg; + char * name; + db_expr_t offset; + + if (INKERNEL((unsigned)callpc) && user_frame == 0) { + db_addr_t call_func = 0; + + db_symbol_values(0, db_search_task_symbol(callpc, + DB_STGY_XTRN, (db_addr_t *)&offset, + TASK_NULL), + &name, (db_expr_t *)&call_func); + if (call_func == db_user_trap_symbol_value || + call_func == db_kernel_trap_symbol_value) { + frame_type = TRAP; + narg = 1; + } else if (call_func == db_interrupt_symbol_value) { + frame_type = INTERRUPT; + goto next_frame; + } else if (call_func == db_syscall_symbol_value) { + frame_type = SYSCALL; + goto next_frame; + } else { + frame_type = 0; + narg = db_numargs(frame, task); + } + } else if (INKERNEL((unsigned)callpc) ^ INKERNEL((unsigned)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); + + argp = &frame->f_arg0; + while (narg > 0) { + db_printf("%x", db_get_task_value((int)argp,4,FALSE,task)); + argp++; + if (--narg != 0) + db_printf(","); + } + if (narg < 0) + db_printf("..."); + db_printf(")"); + if (offset) { + db_printf("+%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, &callpc, frame_type, th); + + if (frame == 0) { + /* end of chain */ + break; + } + if (!INKERNEL(lastframe) || + (!INKERNEL((unsigned)callpc) && !INKERNEL((unsigned)frame))) + user_frame++; + if (user_frame == 1) { + db_printf(">>>>> user space <<<<<\n"); + if (kernel_only) + break; + } + if (frame <= lastframe) { + if (INKERNEL(lastframe) && !INKERNEL(frame)) + continue; + db_printf("Bad frame pointer: 0x%x\n", frame); + break; + } + } +} + +#define CTHREADS_SUPPORT 1 + +#if CTHREADS_SUPPORT + +thread_t +db_find_kthread( + vm_offset_t ustack_base, + vm_size_t ustack_top, + task_t task) +{ + thread_t thread; + + queue_iterate(&task->thread_list, thread, thread_t, thread_list) { + vm_offset_t usp = thread->pcb->iss.uesp/*ebp works*/; + if (usp >= ustack_base && usp < ustack_top) + return thread; + } + return THREAD_NULL; +} + +static void db_cproc_state( + int state, + char s[4]) +{ + if (state == 0) { + *s++ = 'R'; + } else { + if (state & 1) *s++ = 'S'; + if (state & 2) *s++ = 'B'; + if (state & 4) *s++ = 'C'; + } + *s = 0; +} + +/* offsets in a cproc structure */ +int db_cproc_next_offset = 0 * 4; +int db_cproc_incarnation_offset = 1 * 4; +int db_cproc_list_offset = 2 * 4; +int db_cproc_wait_offset = 3 * 4; +int db_cproc_context_offset = 5 * 4; +int db_cproc_state_offset = 7 * 4; +int db_cproc_stack_base_offset = 10 * 4 + sizeof(mach_msg_header_t); +int db_cproc_stack_size_offset = 11 * 4 + sizeof(mach_msg_header_t); + +/* offsets in a cproc_switch context structure */ +int db_cprocsw_framep_offset = 3 * 4; +int db_cprocsw_pc_offset = 4 * 4; + +#include <machine/setjmp.h> + +extern jmp_buf_t *db_recover; + +void db_trace_cproc( + vm_offset_t cproc, + thread_t thread) +{ + jmp_buf_t db_jmpbuf; + jmp_buf_t *prev = db_recover; + task_t task; + db_addr_t pc, fp; + + task = (thread == THREAD_NULL)? TASK_NULL: thread->task; + + if (!_setjmp(db_recover = &db_jmpbuf)) { + char pstate[4]; + unsigned int s, w, n, c, cth; + + s = db_get_task_value(cproc + db_cproc_state_offset, 4, FALSE, task); + w = db_get_task_value(cproc + db_cproc_wait_offset, 4, FALSE, task); + n = db_get_task_value(cproc + db_cproc_next_offset, 4, FALSE, task); + c = db_get_task_value(cproc + db_cproc_context_offset, 4, FALSE, task); + cth = db_get_task_value(cproc + db_cproc_incarnation_offset, 4, FALSE, task); + + db_cproc_state(s, pstate); + + db_printf("CThread %x (cproc %x) %s", cth, cproc, pstate); + if (w) db_printf(" awaits %x", w); + if (n) db_printf(" next %x", n); + db_printf("\n"); + + if ((s != 0) && (c != 0)) { + pc = db_get_task_value(c + db_cprocsw_pc_offset, 4, FALSE, task); + fp = c + db_cprocsw_framep_offset; + } else { + db_addr_t sb; + vm_size_t ss; + + sb = db_get_task_value(cproc + db_cproc_stack_base_offset, sizeof(db_expr_t), FALSE, task); + ss = db_get_task_value(cproc + db_cproc_stack_size_offset, sizeof(db_expr_t), FALSE, task); + db_printf(" Stack base: %x\n", sb); + /* + * Lessee now.. + */ + thread = db_find_kthread(sb, sb+ss, task); + if (thread != THREAD_NULL) { + pc = thread->pcb->iss.eip; + fp = thread->pcb->iss.ebp; + } else + fp = -1; + } + + if (fp != -1) + db_i386_stack_trace(thread, (struct i386_frame*)fp, pc, + -1, F_USER_TRACE); + } + + db_recover = prev; +} + +void db_all_cprocs( + task_t task, + db_expr_t cproc_list) +{ + jmp_buf_t db_jmpbuf; + jmp_buf_t *prev = db_recover; + thread_t thread; + db_expr_t cproc, next; + + + if (task != TASK_NULL) { + thread = (thread_t) queue_first(&task->thread_list); + } else + thread = current_thread(); + + if (cproc_list != 0) + next = cproc_list; + else + if (!db_value_of_name("unix::cproc_list", &next)) { + db_printf("No cprocs.\n"); + return; + } + + + while (next) { + if (_setjmp(db_recover = &db_jmpbuf)) + break; + + cproc = db_get_task_value(next, 4, FALSE, TASK_NULL); + if (cproc == 0) break; + next = cproc + db_cproc_list_offset; + + db_trace_cproc(cproc, thread); + } + + db_recover = prev; +} + +#endif /* CTHREADS_SUPPORT */ + +#endif MACH_KDB diff --git a/i386/i386/debug.h b/i386/i386/debug.h new file mode 100644 index 00000000..99108b69 --- /dev/null +++ b/i386/i386/debug.h @@ -0,0 +1,72 @@ +/* + * 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_ + + +#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 + +/* Dump a saved state. + Probably a good idea to have this around + even when DEBUG isn't turned on. */ +void dump_ss(struct i386_saved_state *st); + +#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 00000000..c8cd5635 --- /dev/null +++ b/i386/i386/debug_i386.c @@ -0,0 +1,147 @@ +/* + * 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 "thread.h" +#include "trap.h" +#include "debug.h" + +void dump_ss(struct i386_saved_state *st) +{ + printf("Dump of i386_saved_state %08x:\n", st); + printf("EAX %08x EBX %08x ECX %08x EDX %08x\n", + st->eax, st->ebx, st->ecx, st->edx); + printf("ESI %08x EDI %08x EBP %08x ESP %08x\n", + st->esi, st->edi, st->ebp, st->uesp); + printf("CS %04x SS %04x DS %04x ES %04x FS %04x GS %04x\n", + st->cs & 0xffff, st->ss & 0xffff, + st->ds & 0xffff, st->es & 0xffff, + st->fs & 0xffff, st->gs & 0xffff); + printf("v86: DS %04x ES %04x FS %04x GS %04x\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 %08x EFLAGS %08x\n", st->eip, st->efl); + printf("trapno %d: %s, error %08x\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() +{ + 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() +{ + 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 "syscall_sw.h" + +int syscall_trace = 0; + +int +syscall_trace_print(int syscallvec, ...) +{ + int syscallnum = syscallvec >> 4; + int i; + + printf("syscall -%d:", syscallnum); + for (i = 0; i < mach_trap_table[syscallnum].mach_trap_arg_count; i++) + printf(" %08x", (&syscallvec)[1+i]); + printf("\n"); + + return syscallvec; +} + +#endif DEBUG + diff --git a/i386/i386/debug_trace.S b/i386/i386/debug_trace.S new file mode 100644 index 00000000..a263bcfd --- /dev/null +++ b/i386/i386/debug_trace.S @@ -0,0 +1,55 @@ +/* + * 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 "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 00000000..26ce3d91 --- /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/fpe.b b/i386/i386/fpe.b new file mode 100755 index 00000000..10a8e65d --- /dev/null +++ b/i386/i386/fpe.b @@ -0,0 +1,478 @@ + +begin 644 fpe.o +M!P$``+12`````````````#````````````````````!@'@8/H`^HB>7I:@D` +M`(GL#ZD/H0<?8<\`````5E>+?"00#[1W/(M',#T`````#X27````/0$```!U +M"(/&(.F(````/0(```!U!8/&).M\/0,```!U!8/&*.MP/04```!U!8/&+.MD +M/1(```!T)3T4````="0]%0```'0C/18```!T(CT7````=$&+=QADK6:)1P!D +MK6:)1P1DK6:)1PADK6:)1PQDK8E'$&2MB4<49*V)1QB#Q@1DK8E'(&2MB4<D +M9*V)1RADK8E'+&2MB4<P9*UFB4<T9*V)1SB)=SQFC&=`7U[+```````````` +M`,#__P````````"`_W\`````````````___________^?T(Q``!",0``V!(` +M`+(2``!*!```2@0``)4&```K!0``2@0``$H$``"5!@``*P4``&4&``!E!@`` +M"@<``%,&```*!0``"@4``(,&``#?!```2@0``$H$``!^!@``*P4``$H$``!* +M!```?@8``"L%``!E!@``908```H'``!3!@``"@4```H%``!L!@``WP0``$H$ +M``"S!@``\08``"L%``"S!@``LP8``"$'``"A!@``\08``"$'``#Q!@``PP8` +M``H%``"A!@``PP8``-\$``!*!```VP8``+,&```K!0``LP8``"$'``"S!@`` +MH08``/$&``#Q!@``(0<``,,&```*!0``VP8``*$&``#?!```ON@```#&1<3_ +MQD7&`HU]O!X.'Q8'N0(```#SI8E-N(L&9HE%R!_#'@'N%A\6!XU]N+D%```` +M\Z4?PV6`#00````(9?8%``````AU/X%MR`!@``"!?<C_?P``?2^`O7#_____ +M#X6D````98`-!````""`O6______#X6/````98`-!0````+I@@```&6`#00` +M```@Z/(C```\#'0<BD7$Z*`C``!U$F6`#04````"OO(```#I4?___\=%R/Y_ +M``#'1;@`````QT6\_____\=%P/____^`O73___\%?#"`O73___\(?R>R`V4B +M%0$```"`^@)_&700QT6\`````,=%P`#____K!X%EO`#X___&1<8`PV7V!0`` +M```0=2!E@`T$````$(%%R`!@``"#?<@`#X\G____@6W(`&```+^X____N`$` +M``#HDT(``.@@(P``L/^_N/___^@J(@``9H.]</___P!T)&6`#00````P@+UO +M_____W4398`-!0````+V1<.`=`7&1<8`PS'`B47(O[S____H?DP``,9%Q@9U +M!,9%Q@'#@WP]$`!U+,=$/1`!````]D0]"X!U'0^]3#T(=!>+1#T$@^D?]]D/ +MI40]"--D/00I3#T0PXM$/00/O<B#Z1_WV=/@B40]",=$/00`````@\$@Z]QT +M'>A5(P``9?8%``````$/A!H!``#H[_W__^F:````98`E!0```/V*18X*1:(/ +MA00!```/MIUT____@.L%9L'C`B[_DQ`!``"_N/___^@G(@``L`#H-B$``+^X +M____Z&I!``"+1<@]_G\```^/MP```(/X``^,M@```.B7_O__(<!U%+^\____ +MZ)-+```/A9T```#&1<8!@+UP_____W4998`-!````""`O6______=0AE@`T% +M`````K^X____OGS____I+P$``&6`#00````"9?8%``````)T5;^`____Z,G^ +M__^_E/___^B__O__Z4#___]E@`T$`````F7V!0`````"="J_@/___^B>_O__ +MZ1____]E@`T$`````F7V!0`````"=`GKO.@6_?__ZXK#Z.;]___K@J@0="9E +M@`T$`````67V!0`````!=.*_@/___^@J&```OGS____IE@```(!]C@)T#(!] +MH@(/A9````#K#/9%BT!T#(!]H@)U&/9%GT!U$F6`#00````!9?8%``````%T +MF+Y\____@'V.`G0'OY3____K28!]H@)T![^`____ZSR*18R*9:!0@&6+?X!E +MGW_&18P`QD6@`,:%=/___P96Z%$K``!>6(A%C+^`____@'W$`'0(B&6@OY3_ +M__^`3#T+P.B1%```Z14B``"*7:**?8[VQP1T`K<#]L,$=`*S`X'C`P,``,#G +M`@#[,/_!XP*X^T````*%=/____;D`<,N_Z,@`0``PV6`#00````"9?8%```` +M``)T[;Z`____ZRYE@`T$`````F7V!0`````"=-3V5:#K$F6`#00````"9?8% +M``````)TO;Z4____Z*C[___K9&6`#00````"9?8%``````)TG[[\````Z'#[ +M___&1<8!ZS-E@`T$`````F7V!0`````"#X1Y____ZQ9E@`T$````!&7V!0`` +M```$#X1A____Z-C[___&1<0`BF6,.F6@=`/V5<3IQOW__XIEH("]=/___P9U +M`O;4.F6,#X1$____98`-!`````%E]@4``````0^$&____^CD^O__Z8_]__\` +M``!B#@``GPX``+T.``!2#P``DPX``"00``#Q#@``1Q```)<.``"G#@``%0\` +M`.</``";#@``I0X``"X/```0$```'PT``"T-```V#0``0@T``",-```H#0`` +M.@T``#$-``#^#0```@X```8.```*#@``#@X``!4.```A#@``)0X``"D.```Q +M#@``.0X``$$.``!)#@``2@X``%(.``!:#@``?1H``$`J```1'P``<C(``#LH +M```7!```%P0``!<$```7!```@2D``!8=``!3'0``.R@``/8>``!A*0``5QX` +M`!,Z```3.@``VS\``"A*```3.@``_!4``.$?``!<%```M2,``+D=```[*``` +MMB@``)<I``#0*0``KRD``/03``#T$P``]!,``/03``#T$P``]!,``/03``"7 +M*```DR@``*4I``!R,@``Z1T``"),``#'30``Q$X``.$?``!R,@``!0<#,`8Q +M"#(`A`$SFXF<G0`9C#,"#82$*2J$A!\@(2(C)"6$$A`3%`\N)B<6$14M%Q@K +M+`"$`3,``#,SC(RHCHR$A(0`A`$SBX2*GAH9`3,O-(2$&ADS,YZ$A(0```<' +M!0$%`0<'!P<'!P<'!P`%`04"!0-$1```0$````P,#`P,#`P`1&A'F$=D``!D +M:$1'1)1$1`,'!04'!0<%``<!`0```0$%!P,#!P4'!0$'```#`P```0`'!P4! +M!0$'!P<'!P<'!P('!04(!`4%!0<"`@4%"`2![)@```!FC-!FCMAFCL!F+HL5 +M&````&:.Z@\"T/?"``!``'40@>3__P``@>7__P``LP'K`K,`B%T"]T4X```" +M`'40QD4#`6:)1?J-13R)11SK,L9%`P"+13R)11QFBT5`9HE%^F:+141FB44( +M9HM%2&:)10QFBT5,9HE%!&:+15!FB44`_+DD````C;UH____,<#SJ_RR!XC1 +M9KL"`H!]`P%U(,5U,,9%^`'&1?D!#P)%-*D``$``=1S&1?@`QD7Y`.L2#[=U +M-,'F!`-U,,9%^`#&1?D`9HL&1B2'>?B`Q$#0T-#`9KX`.&8AQF;![@MFOQX` +M9B''9M'G#A\/O_8/O_\N_Y=$!P``@.0'9HF%=/___V:)E7;___]FB8UY____ +M9HFU?/___V:)O7[___^(O7C___^(G7O___^`?0,!=1+%=3")M6[___]FC)UR +M____ZQN+=3")M6[___\/MTTT9HF-<O___\'A!`'.%A]F,<DQVV:+50QFBP9& +M(,!X0#QG=0:`=?D!Z^X\9G4&@'7X`>OD9DEFBU4$/&1TVF:+50`\97329HM5 +M"#PF=,IFBU4T/"YTPF:+5?H\-G2ZZ[1&B./V1?D!#X4)`@``#[<&4&:+10QF +MCMA8@/O`#X.(````T.-R$7@,@.,.@/L,=!-F,<!.3F:8@^,.T>,N_Y.$!P`` +M1D:`?0,!=2=FB95L____B85H____]H5T____@'5)969EB148````9:,4```` +MZS@/M]+!X@0!T&:,TF:)E6S___^)A6C____VA73___^`=1=E9J,4````B<+! +MZA#!X@QE98D5&````(!]`P%U!8EU_.L,#[=5-,'B!"G6B77\OG;___^_@/__ +M_\=%J`````#'1:P`````QT6P`````,=%M`````#H-`T``&90OGG___^_E/__ +M_^@C#0``9EMF"=B*G73____0TW,5BHUT____@/F;=W&`^8YT;(#YBG1G966* +M#0````#VT8#A/V4B#00```!T!,T0Z^;VA73___^`=42`?0,!=1E0Q(5N____ +M9:,,````969EC`40````6.LE4`^WA7+____!X`0#A6[___]E9J,,````P>@0 +MP>`,9:,0````6`^_V]'#9@G`+O^3Y`<``&5EB@4`````]M`D?V4B!00```!T +M(+``BHUT____]L&`=`6`^9MW&&9E@0T$````@("P`NL*9F6!)00```!_?XM- +M_(E-,")%.70"S1#I[_+__V8#12!F`T44PV8#11##9@-%$&8#12##9@-%%&8# +M11AGXP?#9@-%$.OR9HM5^L,Q_X#\P',+@.0'@/P$=0.*/D:+!AX/H5!FBT4, +M9H[86(#[P`^#:_[__X/&!-#C<AAX$(#C#D>`^PH/A/#]__\QP$Z#[@,/OL"` +MXP^`^PAT$8/C#M'C+O^3I`<``.G._?__B/LA_W00@.<'@/\%=0ADBP:#Q@3K +M$V:)WX/C!\'C`B[_DZ0'``!FB?N(WX#G.(#_(`^$E_W__XC9P,$"@.$#T.N# +MXQPN_Y/$!P``Z7[]__\#12S#`T4HPP-%),,#12##`T4<9^,(PP-%&&?C`<-F +MBU7ZPP-%%,,#11##BWTLT^<!^,.+?2C3YP'XPXM])-/G`?C#BWT@T^<!^,/# +MBWT8T^<!^,.+?133YP'XPXM]$-/G`?C#L0"U`(J&I`@``&:)UV:Z!0%FB=8\ +M,70-/#)T#3PP=02P`[<!P[`&ZP*P"(;?9H?*P[$#Z\VQ`>O)L0+KQ6:Y!03K +MP;,!BH:D"```9HG79KH%`6:Y!01FB<[KN8J&K`@``&;1Y@^_]F:+E@`)``!F +MO@``9HG//`!T##PS=`T\`70-9HG^PV:^!0/#MP&P`<.*AM@(``"*ED@)``"* +MME`)``!F4HJ6.`D``(JV0`D``&9>Z[^*AN@(``!FT>8/O_9FBY98"0``9KX! +M`.NFBH;8"```BI9P"0``BK90"0``9E**EF@)``"*MD`)``!F7NN"]L0@=$YF +MO@`?9B'&9L'N"`^_]HJ&&`D``-#`N00```#`P`)FOP8`9B''#[__9O^W$`D` +M`.+JBH:X"```/!!T"#P1=`0\%'4"MP%F7V9>9EEF6L-FB<^*AK0(```\`'00 +M/!ET%3PS="`\`70@9HG^PV:Z!01FO@4#PV:Z!0%FN04$9HG.9HG7P[`!MP%F +MN@4!9KX%!,.*AO`(```\-'4$MP&P+SPO=0]FN04$9HG79HG69KH%`<,\&G6B +MMP#KGHJ&^`@``#P:=0*S`3R>=>BU!.OD@/PI=16S`;<!L"]FN04$9HG79HG6 +M9KH%`<.PA&:)SF:)S\-FO@`'9B'&9L'N"`^_]HJ&X`@``.OC9K@!`,,``!PD +M.40<)#Q$OKRZN0$"`@(!`@(#O[C____HYC\``+^`____L0'H834``.C]%0`` +M@/H#=`:*38.(3;H/MMJ_=!```"X/M@P[B(UH____C76$-O]V!/9%`@%T!0^W +MU.L"B>))=0%!-O\V@^X$XOB)R+]P$```+HH,.XB-;O___U#B_8G3B<>*C6C_ +M__]1-HL3.U6<=0NX_____S:+4_SK%C:+0_SW=9R)1;")5:SK$HM%L$B+5:R) +M1;`#59QR%HE5K(M%L/=EF#M5K'((=^$V.T/X=]LQP(E%M(J%;O___XG!P>`" +M]]B)QHM$-:#W9;`V*00S<P%"BT6T-BD$,W,!0HE5M(/&!.+@-BD4,W,D_TVP +MBHUN____B<C!X`+WV(G&^(M$-:`V$00S@\8$XO,V$0PSBT6PB40]P%E)=`N# +MZP2#[P3I1/___XG(B<:*C6[____0X68V"T0S_H/N`N+U",0(9;J)SHJ-;O__ +M_S:+1#/\B40UB(/N!.+RBHUH____`HUN____@/D"=0%!P>$"`<S#:&00``#K +M!6AH$```O[C____H73X``+^4____L0'HV#,``.AT%```#[;:7RZ*!#N(A6[_ +M__\N#[:#;!```(G'BT6`BUV$BU6(#ZS8`@^LTP+!Z@+K#='@T=/1TG(0@$P] +M`B`K190;79@;59SK!>@W````Z#P```#^C6[___]UUB'2>`>`3#T"(.L%Z!H` +M``")18")782)58@)V`G0#Z3"$&8)T`C$B&6ZPP-%E!-=F!-5G,/19;C15;S1 +M5<##BT60*T6D!?\_``")1<B*18PR1:"(1<3HJOW__[^X____Z>H^``"+19`# +M1:0M_C\``(E%R(I%C#)%H(A%Q.@*````O[C____IQ#X``!Z_K/___Q8?C5P] +M`.A9/0``O[C____H43T``+D(````OY3____H#ST``'4$`<\!R[Z`____BT0U +M!`M$-0!U!`'.`<M7BWP]`#')BT0U`/?G`0,1T8/^@'0%B4L$ZR:+1#4$]^<! +MR(/2`#')`4,$$=&+1#4(]^<!R(/2``%#"(/2`(E3#%^#PP2#QP2#_Z!UL[^L +M____Z+L\``!T!(!-N@$?PP```````````(#_/P!`_HH;S4MXFM0`0`#`N_`7 +M7"D[JKC_/P#`-,)H(:+:#\D`0`"@F/?/^X2:()K]/P#@JWG/T?<7<K'^/P`` +M`````````````-#KN`8```#VXP4L$@``B<;'1;@`````9BZ+!F:)1;HNBT8" +MB46\+HM&!HE%P"X/MT8*B47(,<")1<2R`[^X____Z&P1``"_N/___^B@,0`` +M98`E!0```/TQP(M%J)^_N/___^E#!@``=!3H$!,``&7V!0`````!=#WI*`8` +M`&6`)04```#]BD6./`!T8#P&=$`\$G4BOX#____H!0D``&6`#00````!9?8% +M``````%T!>GP!0``PSP"=`7IY@4``/9%BT!T!>G;!0``@$V+0.O.98`-!``` +M``)E]@4``````G339L=%D`$`QD6.`+@^0```.460?5N_@/___U?H"#$``%^R +M`[``Z*80``"`O7#_____=1EE@`T$````(("];_____]U"&6`#04````",<"_ +MA/___^@).P``=`^_@/___^BH/```Z5L%``")19#&18X!Z4\%``"_N/___^@3 +M.P``OY3____H"SL``('A_P```/EJ09SK%U&<L0'HTSL``)V<$,D(3;NQ`NC. +M.P``BD6+),"(19:Q`NC,.P``L,"*9;N=<@K!X!#H3````.L8]M3!X!`!192+ +M1;SWT!%%F(M%P/?0$46<6>*MT-D(3;IFBT6Z_L3!X!#H'````+^8____,<#H +M6CH```I%EW0$@$VZ0+$(Z5H[``"^N/___[^4____Z;X)``!T$.AP$0``9?8% +M``````%U4<-E@"4%````_8I%CCP`=0B`?8P`=27K3SP&=0B`?8P`=1GK,CP! +M="D\$G0//`)U!>ER_O__@'V,`'06Z.L0``!TOK^X____Z#\'``#IBP```.GC +M_O__Z,(0``!TH[^`____Z%#M__^!;9#_/P``]T60`0```'0%_TV0ZPZQ`3#` +MOX#____H6"\``(M%D-'X!?\_``")1<CHHO[__S'`B47$O[C____HV`\``.CI +M#@``O[C____H'2\``("]</____]U&66`#00````@@+UO_____W4(98`-!0`` +M``*_N/___^FL`P```,2=:/___S'`B40]!(A$/0@FBP.)1#T)P?@7B&0]#"7_ +M````QT6H`````'5$BD0]"V8+1#T)=0K&1#T.`8E$/1##QD0]#@;&1:K_9?8% +M``````)U`</'1#T0@3\```^]3#T(@^D?]]G39#T(*4P]$,,\_W4RQT0]$/]_ +M``#W1#T(____?\9$/0X*=!KV1#T+0'4.QD6H_V7V!0`````!=`7&1#T.`L/& +M1#T.``6`/P``B40]$(!,/0N`PQ[%G6C____&1#T$`(L#B40]!8M#!!^)1#T) +MP?@49IF(5#T,)?\'``#'1:@`````=6B+1#T$"T0]"'4%Z3W____&1#T.!L9% +MJO]E]@4``````G4!P\=$/1`$/```#[U,/0AT%XM$/02#Z1_WV0^E1#T(TV0] +M!"E,/1##BT0]!`^]R(/I'_?9T^")1#T(QT0]!`````"#P2#KW&8]_P=U.\=$ +M/1#_?P``@60]"/___P^+1#T("T0]!,9$/0X*=#GV1#T+"'4.QD6H_V7V!0`` +M```!="G&1#T.`NL.!0`\``")1#T0QD0]#@"+1#T$#Z1$/0@#P60]!`.`3#T+ +M@,,>Q;5H____%@>#QP17C7P]`+D"````\Z5FBP9?'V:9B%0]""7_?P``B40] +M#,=%J`````!T+_9$/0>`=02R$NL(9CW_?W0)L@"(5#T*,<##L@J+1#T$)?__ +M_W\+1#T`=.BR`NODBT0]!`M$/0"R`738L@;KU'(9``"#&0``FAD``+`9``"C +M&0``]!8``*X7```H'```-QP``(H<``"K&```*!P``"@<``"X&0``@'PU``=T +M(C';B5P]`(I<-0'!XP(N_Z,?&0``BEPU`,'C`B[_DS,9```QP,/H/0T``.@4 +M`@``="&)WAX/J!_I&/___^@F#0``9D#KY>@=#0```H5U____Z]CHXP,``&9` +MPQ[%M6C___\6!XU]EKD%````\V:E'V:!?8K__W4+OX#____HN0,``,.*19]F +MF(AEC+^`____Z'8V``"Q$E&Q!.A4-P``@V6?#^A6-P``OH#____H..C__[$" +MZ#$W``"+1;AF,<`!18"+1;P1182+1<`118@QP(I%GV8!18*P`!%%A!%%B%GB +MM;^`____Z`HV``!U"(E%D,9%C@'#QT603D```+^`____Z'XW``#&18X`P[\: +M``!,&P``LQH``+\:``!3&P``=!#H[PP``&7V!0`````!=0'#GW4(98`E!0`` +M`/V_@/___[Y\____#[9<-0'!XP(N_Z-I&@``Z`T,``!F0.F?````G@^%@0`` +M`.C/````=!KHL@P``&7V!0`````!#X2Y````Z+,"``#K8(M%J`G`=%D(P'0= +M98`-!`````%E]@4``````0^$D0```(!,/0M`ZS@(Y'0498`-!`````1E]@4` +M````!'1TZR!E@`T$`````F7V!0`````"=&"`O7;___\!=P7&1#T.`.A+#0`` +MZ'0+``#K"^AM"P```H5U____@^`'B<&*1#T.Z/D,``"-=#T$OQP````!S\'A +M`@'/N0(````/J`<>%A_SI8HF9B4`@&8+1@0?969EB0?#Z"8+``!F2"0'Z"L+ +M```\`\.-=8"-?93H[@X``(!]C@!U%L=%I/\_``"+19`M_S\``+^`____ZVZ` +M?8X&=23&1:K_9?8%``````)T(+^4____Z-3G__^+1:3'1:3_/P``Z\J`?8X! +M=`7&18P`P\9%J?]E]@4`````!'3QQT60_W\``,9%BX#'18S_``H`P\2=:/__ +M_V8FBP,/O\#K"<2=:/___R:+`XG",<")1#T`B40]!`G0=1*)1#T(B$0]#,9$ +M/0X!B40]$,.9B%0]#'D"]]@/O<B#Z1_WV=/@B40]"+@>0```*<B)1#T0QD0] +M#@##'L6U:/___Q8'5XU\/02Y`@```/.E7Q^+1#T(F8A4/0Q"=13W5#T$@T0] +M!`'W5#T(@U0]"`#K$`M$/01U"HE$/1#&1#T.`</'1#T0/D````^]3#T(=!>+ +M1#T$@^D?]]D/I40]"--D/00I3#T0PXM$/00/O<B#Z1_WV=/@B40]",=$/00` +M````@\$@Z]SHQ@P``.@%````Z3P,``"Q!>B9"0``'@^H'_;A9KDH`&8IP;X< +M````5I@!Q@'&#[?)\V:E9HG!7O-FI1_#Z%X+``"Q!>AF"0``'O;AN2@```!F +M*<&_'````%>8`<<!Q\6U:/___X/&#H!]^`%U`X/&#O-FI6:)P5_S9J4?Z54+ +M``##,<")1#T$QT0]"````,#&1#T,_\=$/1#_?P``QD0]#@+#=`_HLPD``&7V +M!0`````!=.YE@R4%````_>C#_/__,<"?OY3___^^?O___^F[_/__=!)E@`T% +M````066`)04```#YZTME@"4%````N(I%CCP2=#P\`G0@/`IT%#P&=0IE@`T% +M````1.LF]D6+@'0(98`-!0````30R',(98`-!0```$#0R',(98`-!0````'V +M18P!=`AE@`T%`````L-T(>@5"0``9?8%``````%T1K^4____Z";___\,_Y_I +M8/___^@;_?__=`7I4?___V6`)04```#]QT6H`````(I%CCP2=1]E]@4````` +M`74)98`-!`````'#OX#____HW_[__^L8/`)U+?9%BT!U$F7V!0`````!=->` +M38M`QD6H_XUU@(U]E.C""P``Z+/[___IZ_[__^BZ_/__Z=3^__]T$.AV"``` +M9?8%``````%U!</&18P`Z8'[__]T$.A;"```9?8%``````%U!,/V58SI9_O_ +M_P!0L0'H>S$``+$!Z&LQ``"^E/___[^X____9L=$-0```+D#````N``````; +M1#4`B40]`$9&1D9'1T='XNGK%%'HZS$``+Z4____]H5H____`70%OKC___^_ +M@/___^@F````T)5H____6>+4]H5H____`74/OI3___^_@/___^@%````Z:@Q +M``"+1#4`9C'``40]`(M$-0011#T$BT0U"!%$/0C#`>\![AX6'Q8'N0,```#S +MI1_#GV6`)04```#Y9L>%:/___P``GG0*Z'@'``#IY@$``(I%C@I%H@^%S@$` +M`(M%D"M%I+^`____?&.#^#]^#F6`#04````$@\@@@^`_*4600.CW_O__,<"_ +MA/___^CV+P``=1EE]@4%````!`^$&@$``(B%:/___^D/`0``OX#____H?#$` +M`&7V!04````$=`S&A6C___\`Z;@```"?@+UT____+@^%J@```)YU![$!Z+PP +M``#_3:3_=8S&1:``QD6,`,:%=/___P:+19`[1:2^@/___WX%OI3___^_X/__ +M_^@*____Z'@0``"_N/___^A_+P``CT6,OX#___]U%/:%:/___P%T3?Z%:/__ +M__95C.M"ON#___^`?<0`=`SHS?[__[^`____ZRN+19`[1:1T"K^4____Z+3^ +M___^A6C_____1:2*18R(1:#H$Q```+^X____@WP]$`%]88G^Z`7A__]E_S4` +M````98`-`0````/&A7#___\`Z-7A__]ECP4`````O[C____K,8E%D,9%C@&` +MO73___\N=1S&18P`9?8%`0````1T#F7V!0$````(=03&18S_OX#___^^?/__ +M_^C^^/__98`E!0```+R*A6C____`X`9S"&6`#04````!T.!S"&6`#04```!` +MT.!S"&6`#04````"PZ@0="9E@`T$`````67V!0`````!=.F_@/___^B>^___ +MOGS____I"N3__XI%HHIECH#\`G0&/`)U*>L*]D6+0'0*/`)U&/9%GT!U$F6` +M#00````!9?8%``````%THNERX___@+UT____&`^$K0```(#\"G20/`%TC(#\ +M!G5C98`-!`````)E]@4``````@^$;/___[^`____/`IU/?9%BX!T$,=%D`$` +M``#&18X`Z6[___]E]@4`````$`^%8/___^@EX?__@460`&```&6`#00````0 +MZ4?___]0Z`OA__]8/`9U(F6`#00````"9?8%``````(/A`7___^_E/___U#H +MY>#__UB`_`$/A*[^__\\"@^$IO[__^D6_?__@/P*=6B`?8P`=2X\`0^$U?[_ +M_SP&=1)E@`T$`````F7V!0`````"=%N*1:"(18R_@/___^G_````/`H/A*?^ +M__\\!G4298`-!`````)E]@4``````G0MOY3____H$2T``(E%I,9%H@'IRP`` +M`(#\!G4398`-!`````)E]@4``````G4!PSPo````98`-!`````)E]@4` +M`````G3E@/P`=0R_E/___^@@X/__ZR]E]@4`````$'5YZ90```!T"NBW`P`` +MZ27^__]E@"4%````_8I%C@I%H@^%!?[__V7_-0````!E@`T!````#.B+"P`` +M98\%`````'0(@'V,`'0,ZQB+18@]/>```'X'N#W@``#K##T"(/__?06X`B#_ +M_P%%I(%]I/Y_``!_%8-]I`%\(+^4____OGS____I;/;__[Z4____Z`7>___H +M$][__^LLOI3____H]-W__V7_-0````!E@`T!`````\:%</___P#HQ-[__V6/ +M!0````"_N/___^NRATP]!B')>0+VUH'A____?PM,/0)F"TP]`'0"]M(PY/9$ +M/0H!=!'VU,=$/0(`````9L=$/0```+D"`0``PX9,/0@@R7D"]M:`X7\+3#T$ +M"TP]`'0"]M(PY/9$/0D!=`+VU,=$/00`````QT0]``````"Y`P$``,.'3#T$ +M(<EY`O;6@>'___]_"TP]`'0"]M(PY/9$/0@!=`+VU,=$/0``````N00!``## +MBTP]!/;%!'0"]M:!X?\#```+3#T`=`+VTC#D]D0]!0AT`O;4QT0]``````!F +M@60]!`#XN0<(``##ATP]`"')>0+VUH'A____?W0"]M(PY/9$/00!=`+VU+D( +M`0``P[DD``#S)```)"4``%TE``!\)```"B8```(F````)@``,28``##V#[_R +MP>8"9C'2,<DN_Y:")0``9B'2=1L\_XC0=0>(A7'____#9HF5</___XB%;___ +M_\,\_W4(B(5Q____ZPEFQX5P_____P!0Z+4````/MMA8+O^CEB4``/;7.'P] +M#'4SZR>(UPCB//]U&XJ=</___V:!^_\`=0XZI6____]U!F9"=`;K#F9"=`JP +M`(B%;____^LHQH5O_____XCJ,.V[#````"G+`=\PP`!4/0!'21!$/0!'XOD8 +MP(/O#,.(Q.@_````/`1U!R#D=`LPP,,\"'7Y(.1T]0S_P^@D````/`1T]#P( +M=/#KX[(#92(5`0```,-E@"4!````_&4(!0$```##966*!0$````D#,-E@"4! +M````\V4(!0$```##966*!04````D.,#H`\,/ML")P='AB<O!XP(!RX/#'&5F +MH0@```!FT^@D`SP"=2QE9F6+0PAF)?]_=0.P!L-E98M#!*D```"`=0.P$L,E +M____?V4+`[`*=`*P`L-E]@4`````$,/HB@```&7V!0`````"P^AS````9?8% +M``````'#9?8%``````C#9?8%`0```!##9?8%!````"##9?8%!`````'#9?8% +M!`````+#98`-!````$%E@"4%````_<-E@`T$````066`#04````"PV6`#00` +M```@PV6`#00````0PV6`#00````(PV6`#00````!PV6`#00````"PV6`#00` +M```$9?8%``````3#98`-!0````'#98`-!0```$##98`-!0````+#98`-!0`` +M``3#98`E!0```/[#98`E!0```+_#98`E!0```/W#98`E!0```+C#98`E!0`` +M`/O#98`E!````-_#Z`O___]U!^@H____=7J*A7C____H!@```(J%>____SP! +M=#DT`'4TZ&S^__\"A77___\D!XC!L`/0X69ETPT(````98`E"````/PD`V4( +M!0@```!F9=,%"````,/H-_[__^C.____L0CK`K$X966*!04````D.&4P!04` +M````R"0X90@%!0```,,>#Z@'Q;5H____OP````"Y!P```(!]^`%U#?.E'V6` +M#0````!`ZP]FI4='XOH?98`-`````$"Y"````#';N`H```#WX[X<`````<:X +M`P```&4C!0@```"#^`-T0F5F98M&"&8E_W]T&67V1@>`=02R`NL<9CW_?W0$ +ML@#K$K("ZPYE98M&!&4+!K(!=`*R`F6`)0@```#\90@5"````&9EP0T(```` +M`D/BD\-FN'\#96:C`````&:X__]E9J,(````9D!E9J,$````P\2=:/___V8F +MBP-F#4``96:C`````,/H10```&6`#0````!_PV5EQ@4$`````,.*A7K___\\ +M!&5FH00```!T"\2=:/___V8FB0/#9HE%+,-E9J$`````9B5_'V8-0!#KWQX/ +MJ!]F98$E`````'\?9F6!#0````!`$+X`````Q+UH____N0<```"`??@!=03S +MI1_#9J5&1N+Z'\,`2RP``,\M```++P``Q"\``(DP``"R*@``"R\```LO``#F +M*@``=`_H+/W__V7V!0`````!=&!E@"4%````_8"]??___P1U"N@S\/__Z<'] +M__\/MIU\____P>,"@_L(?2*`?8X2=1QE@`T$`````67V!0`````!=!Z_@/__ +M_^C[\O__+O^C'"H``!X6'Q8'N04```#SI1_#Q+UH____'A8?C76$N0(```#S +MI1^*98QF)0"`9@M%D&8FB0?I4_W__P```````$!V.FL+WF7_-0````!E@`T! +M`````X!]C@%U!>D?`0``@7V0_W\``'4P98`-!`````%E]@4``````74%Z2,! +M``#'18(`````QT6&````@&;'18K__^GO````@'V.!G4+QT60`0```,9%C@"X +M/D```#E%D'VUOX#___]7Z)\:``!?L@.P`.@]^O__,<"_A/___^C")```=0F) +M19#&18X!ZWV+5#T$@?JSMN`-#X=Y____BT0]`'(+/0``9*</@VC____'1#T$ +M`````,=$/0``````@^\"NP#*FCOW\U"Y!````+MD````B=`QTO?SDM0*P.0$ +M"."(1#T`1^+K6`G`B%0]`'094C'2N0H```#W\9)9B,2(R&H`N04```#KTH"] +M</____]U&66`#00````@@+UO_____W4(98`-!0````**98R`Y("(98MEQ#T4 +M````'A8?C76"N04```#S9J4?Z.K[__]ECP4`````PV@M*```Q+UH____OH$_ +M``"R`(I%CCP`=$0\!G0V/`)U'/9%BT!U%F6`#00````!9?8%``````%T&8!- +MBT"+38F!X?__?X`/MD60P>`7"<@FB0?#OX#____H'-?__[]^0```Z$(```#$ +MO6C___]T+7@DBT6))?___X"I____`'01)?__?X`/MDV0@,&`P>$7"<@FB0?# +MN'___W_K!;A_``"`"D6,P<@(Z^A75E*P`+^`____Z*/X___HW!@``%I>7SEU +MD'Q1#X2(````.7V0#XZ)````98`-!`````AE]@4`````"'4#6%C#98`-!``` +M`"!E98H%`0```"0,M`@XQ'@4BD6,Z!#Y__^<>`AE@`T%`````IW#9?8%```` +M`!!U"F6`#00````0Z[R_@/___XGP5U+H>Q@``%I?L/_H&OC__V:#O7#___\` +M=`AE@`T$````,/9%BX!U!&;_39!F@[UP____`'0998`-!````""`O6______ +M=0AE@`T%`````C'`0,-H+2@``+X!/```L@**18X\`'1#/`9T-3P!=0AFQT60 +M`#SK4CP"=1WV18M`=1=E@`T$`````67V!0`````!=0'#@$V+0&;'19#_0^LI +MOX#____HG]7__[_^0P``Z,7^__]T?7AHBD6%9IB_AO___^CZ(0``=#NQ`^AD +M(@``Q+UH____'A8?C76%N0,```#S9J4?9HM%D&8M`#QFP>`$@&6+#PI%BX!E +MC(`*98QF)HD'PS'`Q+UH____)HD'9B:)1P2*98R`Y(!F)HE'!L.X_____^C< +M____9KCO?PIEC.OGZ,S___]F)H%/!O!_P[D.0```*TV0>$6#^1%V!;D1```` +MOX#___\PP.@4%P``OX#___^R!+``Z+WV__\\`'4=9H%]B@"`<@AW$X!]C/]U +M#;``.D6,=`9F]UV*,,##@'V.`'0&@'V.!G4]Z)____]U-F:#O7#___\`=!EE +M@`T$````(("];_____]U"&6`#04````"9HM%BL2]:/___V8FB0?IV?C__V8Q +MP(!]C@%TZ&6`#00````!9K@`@&7V!0`````!==+#N1Y````K39!X18/Y(78% +MN2$```"_@/___S#`Z%L6``"_@/___[(!L`#H!/;__SP`=1V!?8@```"`<@AW +M$H!]C/]U#+``.D6,=`7W78@PP,.`?8X`=`:`?8X&=3OHG____W4T9H.]</__ +M_P!T&66`#00````@@+UO_____W4(98`-!0````*+18C$O6C___\FB0?I(OC_ +M_S'`@'V.`73J98`-!`````&X````@&7V!0`````!==/#N3Y````K39!X4X/Y +M078%N4$```"_@/___S#`Z*05``"_@/___[(#L`#H3?7__SP`=2N!?8@```"` +M<@YW((-]A`!U&H!]C/]U%+``.D6,=`TQP/==A!M%B(E%B##`PX!]C@!T!H!] +MC@9U0NB1____=3MF@[UP____`'0998`-!````""`O6______=0AE@`T%```` +M`HM%A(M5B,2]:/___R:)!R:)5P3I5O?__S'`F8!]C@%TY66`#00````!N@`` +M`(!E]@4``````77.PXM%@&8QP`-%E(E%N(M%A!-%F(E%O(M%B!-%G(E%P+`` +MT-##BT6`@664``#__RM%E(E%N(M%A!M%F(E%O(M%B!M%G(E%P+``T-##ON]% +M``#K!;Y/,@``BTV0*TVD>"F#^4-V!;E#````L`"_E/___U;_UEZ+19")1<B` +M?8X!=#/V18N`=2WK'??9@_E#=@6Y0P```%&P`+^`_____]:+1:2)1<A9@'VB +M`70(L`#V19^`=`*P_X"]=/___P9U`_95H(IEC#IEH'4HZ#/___^_N/___^@5 +M%```BD6,B$7$]D7#@'4,O[C____H;AX``'5=PU"*18R(1<3H)____[^X____ +M/`"X`````'0<]U0]`(-$/0`!]U0]!!%$/03W5#T($40]"/95Q.@P'@``=1KH +M@/3__S0$9DAFF(AEQ%@\_W469L=%R```PU@\_W4*O[C____HE!\``,/H8AX` +M`(M$/0`E____'PG"=`B!3#T`````((%D/0````#@PW0598`E!0```/UE@`T$ +M````0>DH`0``QT6H`````&6`)04```#Y@+UT____*745QD6@`,9%H@&_E/__ +M_^BU'0``B46DBD6."D6B='8\`71RJ!`/A=\```"*18Z*9:(\`G0'@/P"=2WK +M#_9%BT`/A,0```"`_`)U"O9%GT`/A+4```"`O73___\O#X3)````Z:,```"` +M?8X&=1'&1:K_@WV0`'4'QT60`0```(!]H@9U$<9%JO^#?:0`=0?'1:0!```` +MQH5T____!N@"_O__O[C____H"1T```^$A````&6`)04```"^]D7$`70(98`- +M!0````&#?:@`=07IO/3__X!]J`!T&V7V!0`````!=`7II_3__\.`?:H`=07I +MF_3__V6`#00````"9?8%``````)TXNF$]/__98`-!`````''1:C_````ZQ"` +M?8X&=`:`?:(&=03&1:K_98`-!0````7K"&6`)04```#^98`-!0```$#K@``` +M@`````````````"```````````````````#U'YCLP+OP%UPI.ZJX```````` +M``````$```````````#``(1DWODS\P2U````C?01!5^L?XH```````````"` +M`/________^_`*"?A];[.1K`E0!@\E?<:%["TZ0`(/T\M-[/T0"N`(#FLYA( +MUK<?LP#`B3GL=ZR;UK4`@%-,D18NM#RW`(#\BT)XMX7RMP#@5KICU6LC3K@` +MX#$FJU/X'WRX`&#]H*(?NBF3N`#`5QB^RGNQGK@`H'!$_@T5=J2X`&"J&YO2 +MCUBGN`!@E.F:OMC)J+@`@#-R)1>`@JFX`,`TPF@AHMH/R0"`17O:#2LX8^T` +M8!7K!F3)K]OZ`,`R;GMAU=2M_@#`-D[O9[G=JO\`@$(EL4O=K>K_`."[U93; +MW:KZ_P``:+G4W:VJ_O\`P$NYW=VJJO__`*!+W=VMJNK__P"@V]W=JJKZ__\` +MX-W=K:JJ_O__`.#=W:JJJO___P#@W:VJJNK___\`X-VJJJKZ____`."MJJJJ +M_O___P#,.0``LSD``+`Y``"G.0``%CD``*1!``"J00``03H``#0\```)/``` +M!SL```D\``!`/```"3P``/,[```T/```-#P``$$Z```>/```XCH``!X\``#S +M.P``LSH``$$Z``#W/```P#P```<[``!;/```)#L``-\\``!;/```KSP``"0[ +M```#/0``F3P``.(Z```-/0``#3T``+,Z``!!.@``.ST``#L]```'.P``63T` +M`#L]```D.P``0ST``*\\```D.P```ST``)D\``#B.@``)3T``"4]``"S.@`` +M9?\U`````&6!#0```````P``98`-!````"#&1<0`9L>%;/___P``BT6D*T60 +MB46H@_@/?C^#^#]_!>G0````Z!#<___H*`\``(M%R(/X``^/!0(```^,`P(` +M`+^\____Z'09```/A?,!``#&1<8!Z>H!``#HS1D``.C1&0``Z#KZ__]FT:5L +M____@'W#`'54Z$8;``"_@/___^B7&0``Z/;Y___H1QL``+[H,P``Z&X;``#H +M,1L``(%EE````/_H"QL``&;_A6S___^`?<,`=!.Y`0```.AM&0``@664```` +M_^L%Z!0:``#_1:B#?:@/=HRY!P```.@#&@``@66`````X,=%J`\```#HX1D` +M`.@&V?__Z-<:``"Y"0```.@N&0``@66```#@_^BZ&@``Z!(9``"!990``.#_ +M@66X````X.B4&@``Z$[Y___HD1H``.AY&@``Z&T:``"Y'@```.CL&```QT6` +M`````+D>````Z(D9``#H)=O__[^X____Z+$8``#&1</`Z%L:``"!990```#@ +M@'WW`+`!N0<```!U![``N0@```!0O^[____H4!D``.@#&@``Z%S8__^!9;@` +M``#@6-#HN0<```!S!;D&````Z&88``#H$AH``&;1K6S___]S&;@*````]V6H +M!>(T``")QN@F&@``Z/`9``!F@[UL____`'06N0$```#H/1@``(%E@```X/__ +M3:CKOH!]BP"Y"````'0(N0<```#_3:CHQQ@``,=%D/X_``"+1:@I19"_@/__ +M_^@5&0``Z$09``"+19")1<C&1<8`98\%`````,/'1#T``````,=$/00````` +MQT0]"`````#'1#T,```!`,=$/1``````P\=$/0``````QT0]!#7":"''1#T( +MHMH/R<=$/0P`````QT0]$/\_``##BE6,BG6@QD6,`,9%H`"(\X/C"(M%I#M% +MD'\W?"N+18@[19Q_+7PABT6$.T68?R-\%[^X____Z)S_____3<@)VP^$LP`` +M`.LX4^@7$@``6X/+!%-2Z._\__]:6PG;=2&+1<B#^`!_3WP&@'W&`71G4NBG +MR?__6F6`#00````0ZWF_@/___^A-____+O^C@C4``,:%=/___P7K"O]%D,:% +M=/___P92C7V4C76XZ-[P___H=_?__UIE_S4`````98$-```````#``!2Z.L+ +M``!:98\%`````&:#O7#___\`=!EE@`T$````(("];_____]U"&6`#04````" +MB%7$PW07Z%GM__]E]@4``````74!P^CVQ___ZWAE@"4%````_0^V78X*7:(/ +MA=X````/MIUT____@.L4=%"`^_QT/(%]D/\_```/C\4```!\)H!]C``/A+D` +M``"!?8@```"`#X6L````@WV$``^%H@```.G4`0``NP@```#K#X!]C``/A8P` +M``"[!````"[_DY(U``"_N/___[Y\____Z5O+__]E@`T$`````F7V!0`````" +M#X1;____OX#____H\<C__[^4____Z.?(___I7____V6`#00````"9?8%```` +M``(/A"S___^_@/___^C"R/__Z3K___]E@`T$`````F7V!0`````"#X0'____ +MZ[3VPQ!T&V6`#00````!9?8%``````$/A.K^___IYO[__X!]C@)T"(!]H@)U +M+^L,]D6+0'0,@'VB`G4<]D6?0'4698`-!`````%E]@4``````0^$K_[__^DT +MRO__BGV.#[:%=/___RP4=$$\_'0I@7V0_S\``'P9#X3&_O__@7V0_W\``'6` +M@'V,``^%=O___[B`````ZQ2`?8P`=`F`_P$/A6#___^X0````(I=HO;'!'0" +MMP/VPP1T`K,#@>,#`P``P.<"`/LP_\'C`@'#+O^CGC4``&6`#00````"9?8% +M``````(/A!O^__^`?:``=#>_N/___^C4_/___T7(ZS)E@`T$`````F7V!0`` +M```"#X3P_?__O[C____HK_S__^L0@'V@`'7)O[C____H=/S__XI5C(A5Q.E) +M_O__98`-!`````1E]@4`````!`^$L_W__\=%B````(#'18P```H`QT60_W\` +M`+^`____BE6@]M*(5#T,Z1#^__]E@`T$`````F7V!0`````"#X1U_?__OX#_ +M__^*5:"(5#T,Z>G]__^!?9#_/P``?SI\-8%]B````(!W+X-]A`!U*>E%_O__ +M98`-!`````1E]@4`````!`^$+_W__^L)@7V0_S\``'T#]E6@OY3____IG/W_ +M_V6`#00````"9?8%``````(/A`']___KVV6`#00````"9?8%``````(/A.G\ +M__^*58PP5:#KP&6`#00````"9?8%``````(/A,O\__^*5:`P58R_@/___^E` +M_?__6_]T-0#_=#4$_W0U"/]T-0S_=#40_^-;CT0U$(]$-0R/1#4(CT0U!(]$ +M-0#_X\=$-0``````QT0U!`````#'1#4(````@,=$-0P`````QT0U$/\_``## +M9L>%;/___P``N/\_```K19"#^#]_48E%D(/X#W=^Z,H2``"X"@```/=ED`5" +M-```B<;HH10``&;1I6S___^`?<,`=1/H110``(%E@```X/]F_X5L_____T60 +M@WV0#W<PZ#83``#KO,9%H`#'1:3_/P``O@8T``#H;10``.@-$P``Z&S4___H +MA`<``.DN`0``QT60#P```.C[$@``Z)P3``"Q'NA'$@``@V6``+$>Z.H2``#H +MAM3__^C1$P``@66`````X+[\,P``Z!$4``"+39"#P0KH!!(``(%EN```X/_H +MB!,``.@#$@``Z)@3``#H9_+__XM-D$'HX!$``(%EN```X/_H?A,``+X&-``` +MZ+83``#H51,``.AJ$P``Z&P2``#HD='__[D(````Z*P1``"!9;@``.#_9M&M +M;/___W,DZ$@3``#H/!,``(M-D.B3$0``@664``#@_V:!39Z``.C3\?__9H.] +M;/___P!T$>AG$0``@66X``#@__]-D.NXL0B`?<,`=`6Q!_]-D.CU$0``N/\_ +M```K19")1<C'1<0`````]D7#@'4*L0'HUA$``/]-R.C5$@``Z.T2``##]E6, +MZ#G^__^-=;B-?93H$NO__[Z`____Z/O]___&18S_QH5T____!KZX____Z+C] +M___HC/'__^@4!@``C76XC7V4Z-[J__^^@/___^BP_?__Z-W2___H]04``,/H +M]P8``'4%Z;/:__^!?9#_/P``?_)\&H%]B````(!WYX-]A`!UX8!]C`!TV_]- +MD.O698`-!````"!E_S4`````98$-```````#``!E@24`````__/__X%]D/X_ +M``!T"(!]C`!U6>L5@7V(````@'=5@WV$`'5/@'V,`'5"Z&3]__^+3<B#^0!^ +M!L9%Q@#K#>C#PO__98`-!````!"`O6______=0AE@`T%`````F6/!0````"_ +MN/___^D&VO__Z.'^___KO(!]C`!U?[Z4____Z.?\___&A73___\&Z(;P___H +M#@4``(UUN(U]@.C8Z?__Z*[^__^-=;B-?8#HR.G__[Z4____Z+'\___&A73_ +M__\%Z%#P___HV`0``/]%R(UUN(U]@.B?Z?__OI3____HB/S__\:%=/___P;H +M)_#__^BO!```Z5+___^^E/___^AH_/__QH5T____!>@'\/__Z(\$``"-=;B- +M?8#H6>G__^AP_/__C76XC7V`Z$GI__^^E/___^@R_/__QH5T____!>C1[___ +MZ%D$``"-=;B-?8#H(^G___]-D+Z4____Z`G\___&A73___\&Z*CO___H,`0` +M`.G3_O__QD7:`.L$QD7:`67_-0````!E@0T```````,``+_B____Z"40``"* +M1:*(1>*+1:2)A6C___^*1:"(1<[&1=@`@'W:`'4HZ+\.``"^)#0``.B@$``` +M@'W#`'5<Z5P!``#H[@\``,9%V/_IBP$``(M%D(E%R(!]C`!UYNCE#P``N?\_ +M```K39"#^4AV!;E(````Z&T.``!FBTV6@>'_'P``"<IT!F:!398`((!-GX"! +M990```#@ZSOH^`X``.B2#P``N0$```#HV0X``+^Z____Z+H-``!T.,=%R/X_ +M``"_N/___^A7#P``Z'</``#HN0\``/^U:/___^C8S?__CX5H____@66X```` +MX.G4````Z/38__^_@O___^AQ#0``=1?&18X!BD7.,$6,C76`C7VXZ,SG___K +M;X%EX````/_H0`\``(N%:/___XE%I(I%SHA%H.C>S___Z-`"``"+1<@]_G\` +M`'](@_@`?$I_$K^\____Z!T-``!U/,9%Q@'K!,9%Q@!F@[UP____`'0998`- +M!````""`O6______=0AE@`T%`````F6/!0````##Z`2____K\>C5O___Z^K_ +M19#HH`X``+X0-```Z!@/``"Y!P```.C,#0``QD78_\=%R/\_``"_NO___^BB +M#```=`J_N/___^A&#@``Z*8.``!FQX5L____``"X_S\``"M%R(E%J(/X#P^' +MD````.B+#@``N0<````#3:CHWPP``.C/#```Z![M__^`?<,`=!F!9;@``.#_ +MQD7#`.A7#@``9L>%;/___P$`9M&E;/___^CE#0``BTVHZ*(,``"!38@``(`` +MZ-_L__^`?<,`=!>!9;@``.#_QD7#`.@8#@``9O^%;/___[D!````Z!`-``#_ +M1:B#?:@/=K#H``T``&6`#00````@Z(\-``#HGL[__XM-J(/Y2'8%N4@```#H +M*PP``(%EN````.#HR0T``.B8[/__Z+@-``"+3:B#^4AV!;E(````Z!4,``"! +M98````#@Z&$-``"^&C0``.C9#0``Z)D-``#H60T``(M-J$&#^4AV!;E(```` +MZ-D+``"!990```#PZ$+L__^!9;@```#@Z#4-``#H70T``/^U:/___^B#R___ +MCX5H____@66X````X.A'#0``OBXT``#HC@T``+D'````Z((+``#_3:AFT:UL +M____<R*+=:A.N`H```#WYHG&@<9,-```Z&H-``#H"@T``.BOZ___9H.];/__ +M_P!T"NA#"P``_TVHZ\&X!T```"M%J(E%R(%EN```X/^_N/___^C:"@``=`J_ +MN/___^A=#```@'W:`'4XZ!O6___HL@P``(M%R(E%I,:%=/___P6*1=B(1:#H +MG.O__^B<#```BD7$B$6,BT7(B460Z?3\__^*1=B(1<3KXL9%N@&R`[^X____ +ML`#HR=___[^X____M`2H`74!P_]$/1"Q`>C""@``(=)T!8!,/0(!PXG!AT0] +M$"G!@_E#?@6Y0P```##`Z]MT*NA8X?__98`E!0```/ME]@4``````74"6,._ +M@/___[Y^____6)_I9-3__V6`)04```#[Z$W5__]T!#'`Z]S'1:@`````98`E +M!0```/V*18X\`79M/`9R#G0_OX#____H']?__^L*]D6+0'7.@$V+0&7V!0`` +M```!=0IE@`T$`````>N4C76`C7V4Z`#D___&1:C_6.D<U___9?8%``````)U +M#66`#00````"Z6C____&1:K_OX#____H\;S__X/(`<-T'^B5X/__98`E!0`` +M`/ME]@4``````74"6,-8Z:+3__]E@"4%````^8I%CCP!?FL\!GPJ=$:`O73_ +M__\2=1,\$G0/@'V,`'32QT60_S\``.O)OX#____H9=;__^L*]D6+0'6W@$V+ +M0&6`#00````!9?8%``````%TG^N?98`-!`````)E]@4``````G2+OX#____H +M5;S__X/(`<,QP(J%=/___XIEC(E%K,9%C`!FQX5H____``"+19`M_C\``'Q5 +M@_@_?@IE@`T%````!%C#QT68-<)H(<=%G*+:#\G'1:``````QT6D_C\``"E% +MD$#H8-?__XM%A`M%B'4/B46`B460QT6.`0```.L2OX#____H]0D``&6`#00` +M```@]H5H____!'0$9O=5K?:%:/___P)T!O95K_95KO:%:/___P%U`</V5:^` +M?8X!=2>`?:P3=2'&18N`QD6.`,=%D/\_``"^E/___^A4]?__@\0$Z2(#``!E +M@`T$````(,=%E`````#'19@UPF@AQT6<HMH/R<=%H`````#'1:3^/P``QD6, +M_\:%=/___P7HO.C__V7_-0````!E@24`````__/__^@R_?__98\%`````(UU +MN(U]@.CUX?__PV;'A6S___\``(E%D(/X$']8_TV0Z.0'``"X"@```/=ED`7B +M-```B<;HNPD``&;1I6S___^`?<,`=1/H7PD``(%E@```X/]F_X5L_____T60 +M@WV0#W<'Z%`(``#KO.A%"```QT6``````.C?"```Z-O)__^+39#1X8/!".AM +M!P``@66X````_^@+"0``OD(T``#H0PD``.C;"```Z%0'``#HV`@``+$)Z%,' +M``#HV`@``.B2Y___Z#`'``#HT`@``/]-D&;1K6S___]S-NBL"```Z(T(``#H +M;>?__^BP"```BTV0T>'H#`<``(%EE```X/_H=>?__^B."```@67@````_V:# +MO6S___\`=!BQ`;_L____Z.P&``"!9>P``.#__TV0ZY^Q"(!]]P!T!;$'_TV0 +MO^[____H=P<``.@J"```N/\_```K19")19"_@/___^C=!P``Z!P(``#H/P<` +M`,=%I/\_``#'1:``````OY3____HNP<``,/HY_O__W4/OI3____H7O/__^F, +MT___Z##]__^`?8X!=2^`?:__=>#'18``````QT6$`````,=%B````(#'18P` +M``H`QT60_W\``,9%J?_KM[C_/P``*T60@_@_?GP]_S\``'Q`BD6MB$6,98`- +M!````!!E]@4`````$'4)@460`&```.N"OX#___^X`0```.A"^___QT60```` +M`,9%C@;I8____[Z4____Z,'R__^`?:__#X6-````9HM%K8A%C(AEH&7_-0`` +M``!E@0T```````,``/]UK.LP9?\U`````&6!#0```````P``_W6LZ)7]__^/ +M1:QFBT6MB$6,B&6@_W6L@'VO_W4'Z&4```#K!>AC````@+UO_____W4(98`- +M!0````*-=;B-?8#H2]___[Z4____Z#3R__^/1:QECP4`````BD6M,D6NB$6, +MZ5?2__^^@/___^CD\?__C764C7V`Z!3?__^^E/___^CF\?__P^C;____9?\U +M`````&6!#0```````P``Z/O&___H$_K__V6/!0````##OKC____HF_'__^C- +M____C76XC7V`Z,;>__^^E/___^B8\?__98$-```````/``#&A73___\%Z$/E +M___HR_G__XUUN(U]E.B5WO__OH#____H?O'___]%D.A_____P^BP^O__=1K& +M1:PKBD6,B$6MC76`C7VXZ&;>___IG0$``.@K^___@'V.`74+@'VO_W7?Z7T! +M``"`?:__#X38`0``N/\_```K19"#^!Y\5'\1@7V(```0C7=)<K:#?80`=+`] +M_S\``'RI98`-!````!!E]@4`````$'4)@460`&```.N.OX#___^X`0```.A* +M^?__QT60`````,9%C@;I;_____]UK&7_-0````!E@24`````__/__V6!#0`` +M`````P``0.C&^___OH#____H=?#__[Z4____Z&OP___HF/[__[Z4____Z'/P +M__^^@/___^AI\/__Z*C^__]ECP4`````CT6L98`E!0```/V`O6______#X6E +M````98`-!0````+IF````(UU@(U]E.A1W?__Z(#%___H<OC__XUUN(U]@.@\ +MW?__OI3____H)?#__\:%=/___P7HQ./__^A,^/__C76XC7V4Z!;=__^^@/__ +M_^C_[____T60Z`#^__^-=;B-?93H^=S__[Z`____Z.+O___&A73___\&Z('C +M___H"?C__\/H"_G__W4MQD6L+,9%K@"^N/___^BW[___@'VL+'0%BD6MZP.* +M1:Z(1<2_N/___^FDS/__Z'/Y__^`?8X!=2N`?:__=<K'1;@`````QT6\```` +M`,=%P`````#'1<0```$`QT7(`````.NZ@'VO_P^$*/[__[C_/P``*T60@_@^ +M?!5_C(%]B````(!W"H-]A``/A'G_____=:QE_S4`````98$E`````/_S__]E +M@0T```````,``$#H+?K__^@3_?__C76XC7V`Z!'<___HL/[__V6/!0````"/ +M1:QE@"4%````_8"];_____\/A2G___]E@`T%`````ND<____Z$OW__]U+#'` +MBF6,B46LOI3____HNN[__X!]K_]U!>B3_/__9HM%K8A%C(AEH.G3SO__Z'?X +M__^`?8X!=-&X_S\``"M%D(/X/GQ2?P^!?8@```"`=T>#?80`=4$]_S\``'RL +M98`-!````!!E]@4`````$'4)@460`&```.N1OX#___^X`0```.BM]O__QT60 +M`````,9%C@;I<O____]UK&7_-0````!E@24`````__/__V6!#0```````P`` +M@_@>?!]_$8%]B```$(UW%'(&@WV$`'4,,=N^@/___^C$[?__G$#H!/G__YUT +M%+Z`____Z+#M__^^E/___^BF[?__G.C2^___G704OI3____HJNW__[Z`____ +MZ*#M__^^N/___^A_[?__=`><Z-+[__^=OH#____H@^W__W0*OKC____H8.W_ +M_^@Z_?__OH#____H:.W__XUUN(U]E.B!VO__98\%`````(]%K.FK_O__9HM$ +M/0AF"T0]!F8+1#T$9@M$/0)F"T0]`'4!PV:X___#BT0]``M$/00+1#T(=0'# +MN/_____#,<")1#T`B40]!(E$/0C#9C'`9HE$/0AFB40]!F:)1#T$9HE$/0)F +MB40]`,.+3:C0X>L9L0&_N/___^L0L0B_E/___^L'L0B_@/___[``,=+0R`^^ +MP%"U`H#Y(')9@/E@<B%T`V8)P@M4/0`+5#T$"U0]"(C@B40]`(E$/02)1#T( +M6,,>%A\6!P'OB?Z(RX/A8,'I!8C("Q:#Q@3B^6:Y`P`HP8C/\Z6(P8C@\ZN# +M[PPI[Q]FB=E85U"+1#T`,=L/K<,)VHM$/00/K40]`(/'!/[-=?!8B.`/K40] +M`%_XP[^Z____ZQ2Q"+^6____ZPNQ".L"L0&_@O___X#Y4'X"L5"(R"1X=#)1 +M5QYFF&;!Z`-F2+D)`````<\/O\`IP0'OB?XIQDX6'Q8'_?.DB`^)P8G^3_.D +M_!]?68/A!W0;9M%D/0!FT50]`F;15#T$9M%4/09FT50]".+EP_9$/0N`=1A7 +MN0$```"#QP+HS____U__3#T0]D0]"X##Z-O___]T^<._@O___[Z6____ZV*_ +MNO___^L/O^+____H!0```+^6____OH+____K1;^6____ZP6_@O___[[N____ +MZS*_EO___^L%OX+___^^XO___^L?O^+____K$[_N____ZPR_EO___^L%OX+_ +M__^^NO___P'N'A8?%@<![[D%````\V:E'\.^\C,``.@*````O[C____I9?/_ +M_^@E````Z6_>__^_@O___^@;````Z8/>__^_EO___^OOZ`4```#I5<#__[^6 +M____'@[KJ@``!`````4`````````#P````4````8````(`````4````<```` +M+0````4```"T4@``-@```%]F<&5?<W1A<G0`7V9P95]R96=?<V5G;65N=`!? +59G!E7W)E8V]V97(`7V9P95]E;F0` +` +end diff --git a/i386/i386/fpe.b_elf b/i386/i386/fpe.b_elf new file mode 100644 index 00000000..c04619e1 --- /dev/null +++ b/i386/i386/fpe.b_elf @@ -0,0 +1,576 @@ +begin 775 fpe.o +M?T5,1@$!`0````````````(``P`!`````````#0```"08P```````#0`(``! +M`"@`!P`$``$`````$`````````````"T4@``M%(```<`````$``````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`&`>!@^@#ZB)Y>EJ"0``B>P/J0^A!Q]ASP````!65XM\)!`/M'<\BT<P/0`` +M```/A)<````]`0```'4(@\8@Z8@````]`@```'4%@\8DZWP]`P```'4%@\8H +MZW`]!0```'4%@\8LZV0]$@```'0E/10```!T)#T5````=",]%@```'0B/1<` +M``!T08MW&&2M9HE'`&2M9HE'!&2M9HE'"&2M9HE'#&2MB4<09*V)1Q1DK8E' +M&(/&!&2MB4<@9*V)1R1DK8E'*&2MB4<L9*V)1S!DK6:)1S1DK8E'.(EW/&:, +M9T!?7LL`````````````P/__`````````(#_?P````````````#_________ +M__Y_0C$``$(Q``#8$@``LA(``$H$``!*!```E08``"L%``!*!```2@0``)4& +M```K!0``908``&4&```*!P``4P8```H%```*!0``@P8``-\$``!*!```2@0` +M`'X&```K!0``2@0``$H$``!^!@``*P4``&4&``!E!@``"@<``%,&```*!0`` +M"@4``&P&``#?!```2@0``+,&``#Q!@``*P4``+,&``"S!@``(0<``*$&``#Q +M!@``(0<``/$&``##!@``"@4``*$&``##!@``WP0``$H$``#;!@``LP8``"L% +M``"S!@``(0<``+,&``"A!@``\08``/$&```A!P``PP8```H%``#;!@``H08` +M`-\$``"^Z````,9%Q/_&1<8"C7V\'@X?%@>Y`@```/.EB4VXBP9FB47('\,> +M`>X6'Q8'C7VXN04```#SI1_#98`-!`````AE]@4`````"'4_@6W(`&```(%] +MR/]_``!]+X"]</____\/A:0```!E@`T$````(("];_____\/A8\```!E@`T% +M`````NF"````98`-!````"#H\B,``#P,=!R*1<3HH",``'4298`-!0````*^ +M\@```.E1____QT7(_G\``,=%N`````#'1;S_____QT7`_____X"]=/___P5\ +M,("]=/___PA_)[(#92(5`0```(#Z`G\9=!#'1;P`````QT7``/___^L'@66\ +M`/C__\9%Q@##9?8%`````!!U(&6`#00````0@47(`&```(-]R``/CR?___^! +M;<@`8```O[C___^X`0```.B30@``Z"`C``"P_[^X____Z"HB``!F@[UP____ +M`'0D98`-!````#"`O6______=1-E@`T%`````O9%PX!T!<9%Q@##,<")1<B_ +MO/___^A^3```QD7&!G4$QD7&`<.#?#T0`'4LQT0]$`$```#V1#T+@'4=#[U, +M/0AT%XM$/02#Z1_WV0^E1#T(TV0]!"E,/1##BT0]!`^]R(/I'_?9T^")1#T( +MQT0]!`````"#P2#KW'0=Z%4C``!E]@4``````0^$&@$``.CO_?__Z9H```!E +M@"4%````_8I%C@I%H@^%!`$```^VG73___^`ZP5FP>,"+O^3$`$``+^X____ +MZ"<B``"P`.@V(0``O[C____H:D$``(M%R#W^?P``#X^W````@_@`#XRV```` +MZ)?^__\AP'44O[S____HDTL```^%G0```,9%Q@&`O7#_____=1EE@`T$```` +M(("];_____]U"&6`#04````"O[C___^^?/___^DO`0``98`-!`````)E]@4` +M`````G15OX#____HR?[__[^4____Z+_^___I0/___V6`#00````"9?8%```` +M``)T*K^`____Z)[^___I'____V6`#00````"9?8%``````)T">N\Z!;]___K +MBL/HYOW__^N"J!!T)F6`#00````!9?8%``````%TXK^`____Z"H8``"^?/__ +M_^F6````@'V.`G0,@'VB`@^%D````.L,]D6+0'0,@'VB`G48]D6?0'4298`- +M!`````%E]@4``````728OGS___^`?8X"=`>_E/___^M)@'VB`G0'OX#____K +M/(I%C(IEH%"`98M_@&6??\9%C`#&1:``QH5T____!E;H42L``%Y8B$6,OX#_ +M__^`?<0`=`B(9:"_E/___X!,/0O`Z)$4``#I%2(``(I=HHI]CO;'!'0"MP/V +MPP1T`K,#@>,#`P``P.<"`/LP_\'C`KC[0````H5T____]N0!PR[_HR`!``## +M98`-!`````)E]@4``````G3MOH#____K+F6`#00````"9?8%``````)TU/95 +MH.L298`-!`````)E]@4``````G2]OI3____HJ/O__^MD98`-!`````)E]@4` +M`````G2?OOP```#H</O__\9%Q@'K,V6`#00````"9?8%``````(/A'G____K +M%F6`#00````$9?8%``````0/A&'____HV/O__\9%Q`"*98PZ9:!T`_95Q.G& +M_?__BF6@@+UT____!G4"]M0Z98P/A$3___]E@`T$`````67V!0`````!#X0; +M____Z.3Z___IC_W__P```&(.``"?#@``O0X``%(/``"3#@``)!```/$.``!' +M$```EPX``*<.```5#P``YP\``)L.``"E#@``+@\``!`0```?#0``+0T``#8- +M``!"#0``(PT``"@-```Z#0``,0T``/X-```"#@``!@X```H.```.#@``%0X` +M`"$.```E#@``*0X``#$.```Y#@``00X``$D.``!*#@``4@X``%H.``!]&@`` +M0"H``!$?``!R,@``.R@``!<$```7!```%P0``!<$``"!*0``%AT``%,=```[ +M*```]AX``&$I``!7'@``$SH``!,Z``#;/P``*$H``!,Z``#\%0``X1\``%P4 +M``"U(P``N1T``#LH``"V*```ERD``-`I``"O*0``]!,``/03``#T$P``]!,` +M`/03``#T$P``]!,``)<H``"3*```I2D``'(R``#I'0``(DP``,=-``#$3@`` +MX1\``'(R```%!P,P!C$(,@"$`3.;B9R=`!F,,P(-A(0I*H2$'R`A(B,D)802 +M$!,4#RXF)Q81%2T7&"LL`(0!,P``,S.,C*B.C(2$A`"$`3.+A(J>&AD!,R\T +MA(0:&3,SGH2$A```!P<%`04!!P<'!P<'!P<'``4!!0(%`T1$``!`0```#`P, +M#`P,#`!$:$>81V0``&1H1$=$E$1$`P<%!0<%!P4`!P$!```!`04'`P,'!0<% +M`0<```,#```!``<'!0$%`0<'!P<'!P<'`@<%!0@$!04%!P("!04(!('LF``` +M`&:,T&:.V&:.P&8NBQ48````9H[J#P+0]\(``$``=1"!Y/__``"!Y?__``"S +M`>L"LP"(70+W13@```(`=1#&10,!9HE%^HU%/(E%'.LRQD4#`(M%/(E%'&:+ +M14!FB47Z9HM%1&:)10AFBT5(9HE%#&:+14QFB44$9HM%4&:)10#\N20```"- +MO6C___\QP/.K_+('B-%FNP("@'T#`74@Q74PQD7X`<9%^0$/`D4TJ0``0`!U +M',9%^`#&1?D`ZQ(/MW4TP>8$`W4PQD7X`,9%^0!FBP9&)(=Y^(#$0-#0T,!F +MO@`X9B'&9L'N"V:_'@!F(<=FT><.'P^_]@^__R[_ET0'``"`Y`=FB85T____ +M9HF5=O___V:)C7G___]FB;5\____9HF]?O___XB]>/___XB=>____X!]`P%U +M$L5U,(FU;O___V:,G7+____K&XMU,(FU;O___P^W331FB8UR____P>$$`<X6 +M'V8QR3';9HM5#&:+!D8@P'A`/&=U!H!U^0'K[CQF=0:`=?@!Z^1F26:+500\ +M9'3:9HM5`#QE=-)FBU4(/"9TRF:+530\+G3"9HM5^CPV=+KKM$:(X_9%^0$/ +MA0D"```/MP909HM%#&:.V%B`^\`/@X@```#0XW(1>`R`XPZ`^PQT$V8QP$Y. +M9IB#XP[1XR[_DX0'``!&1H!]`P%U)V:)E6S___^)A6C____VA73___^`=4EE +M9F6)%1@```!EHQ0```#K.`^WTL'B!`'09HS29HF5;/___XF%:/____:%=/__ +M_X!U%V5FHQ0```")PL'J$,'B#&5EB148````@'T#`74%B77\ZPP/MU4TP>($ +M*=:)=?R^=O___[^`____QT6H`````,=%K`````#'1;``````QT6T`````.@T +M#0``9E"^>?___[^4____Z",-``!F6V8)V(J==/___]#3<Q6*C73___^`^9MW +M<8#YCG1L@/F*=&=E98H-`````/;1@.$_92(-!````'0$S1#KYO:%=/___X!U +M1(!]`P%U&5#$A6[___]EHPP```!E9F6,!1````!8ZR50#[>%<O___\'@!`.% +M;O___V5FHPP```#!Z!#!X`QEHQ````!8#[_;T<-F"<`N_Y/D!P``966*!0`` +M``#VT"1_92(%!````'0@L`"*C73____VP8!T!8#YFW<89F6!#00```"`@+`" +MZPIF98$E!````']_BTW\B4TP(D4Y=`+-$.GO\O__9@-%(&8#113#9@-%$,-F +M`T409@-%(,-F`T449@-%&&?C!\-F`T40Z_)FBU7ZPS'_@/S`<PN`Y`>`_`1U +M`XH^1HL&'@^A4&:+10QFCMA8@/O`#X-K_O__@\8$T.-R&'@0@.,.1X#["@^$ +M\/W__S'`3H/N`P^^P(#C#X#["'01@^,.T>,N_Y.D!P``Z<[]__^(^R'_=!"` +MYP>`_P5U"&2+!H/&!.L39HG?@^,'P>,"+O^3I`<``&:)^XC?@.<X@/\@#X27 +M_?__B-G`P0*`X0/0ZX/C'"[_D\0'``#I?OW__P-%+,,#12C#`T4DPP-%(,,# +M11QGXPC#`T489^,!PV:+5?K#`T44PP-%$,.+?2S3YP'XPXM]*-/G`?C#BWTD +MT^<!^,.+?2#3YP'XP\.+?1C3YP'XPXM]%-/G`?C#BWT0T^<!^,.Q`+4`BH:D +M"```9HG79KH%`6:)UCPQ=`T\,G0-/#!U!+`#MP'#L`;K`K`(AM]FA\K#L0/K +MS;$!Z\FQ`NO%9KD%!.O!LP&*AJ0(``!FB==FN@4!9KD%!&:)SNNYBH:L"``` +M9M'F#[_V9HN6``D``&:^``!FB<\\`'0,/#-T#3P!=`UFB?[#9KX%`\.W`;`! +MPXJ&V`@``(J62`D``(JV4`D``&92BI8X"0``BK9`"0``9E[KOXJ&Z`@``&;1 +MY@^_]F:+EE@)``!FO@$`ZZ:*AM@(``"*EG`)``"*ME`)``!F4HJ6:`D``(JV +M0`D``&9>ZX+VQ"!T3F:^`!]F(<9FP>X(#[_VBH88"0``T,"Y!````,#``F:_ +M!@!F(<</O_]F_[<0"0``XNJ*AK@(```\$'0(/!%T!#P4=0*W`69?9EYF669: +MPV:)SXJ&M`@``#P`=!`\&705/#-T(#P!="!FB?[#9KH%!&:^!0/#9KH%`6:Y +M!01FB<YFB=?#L`&W`6:Z!0%FO@4$PXJ&\`@``#PT=02W`;`O/"]U#V:Y!01F +MB==FB=9FN@4!PSP:=:*W`.N>BH;X"```/!IU`K,!/)YUZ+4$Z^2`_"EU%;,! +MMP&P+V:Y!01FB==FB=9FN@4!P["$9HG.9HG/PV:^``=F(<9FP>X(#[_VBH;@ +M"```Z^-FN`$`PP``'"0Y1!PD/$2^O+JY`0("`@$"`@._N/___^CF/P``OX#_ +M__^Q`>AA-0``Z/T5``"`^@-T!HI-@XA-N@^VVK]T$```+@^V##N(C6C___^- +M=80V_W8$]D4"`70%#[?4ZP*)XDEU`4$V_S:#[@3B^(G(OW`0```NB@P[B(UN +M____4.+]B=.)QXJ-:/___U$VBQ,[59QU"[C_____-HM3_.L6-HM#_/=UG(E% +ML(E5K.L2BT6P2(M5K(E%L`-5G'(6B56LBT6P]V68.U6L<@AWX38[0_AWVS'` +MB46TBH5N____B<'!X`+WV(G&BT0UH/=EL#8I!#-S`4*+1;0V*00S<P%"B56T +M@\8$XN`V*10S<R3_3;"*C6[___^)R,'@`O?8B<;XBT0UH#81!#.#Q@3B\S81 +M##.+1;")1#W`64ET"X/K!(/O!.E$____B<B)QHJ-;O___]#A9C8+1#/^@^X" +MXO4(Q`AENHG.BHUN____-HM$,_R)1#6(@^X$XO**C6C___\"C6[___^`^0)U +M`4'!X0(!S,-H9!```.L%:&@0``"_N/___^A=/@``OY3___^Q`>C8,P``Z'04 +M```/MMI?+HH$.XB%;O___RX/MH-L$```B<>+18"+782+58@/K-@"#ZS3`L'J +M`NL-T>#1T]'2<A"`3#T"("M%E!M=F!M5G.L%Z#<```#H/````/Z-;O___W76 +M(=)X!X!,/0(@ZP7H&@```(E%@(E=A(E5B`G8"=`/I,(09@G0",2(9;K#`T64 +M$UV8$U6<P]%EN-%5O-%5P,.+19`K1:0%_S\``(E%R(I%C#)%H(A%Q.BJ_?__ +MO[C____IZCX``(M%D`-%I"W^/P``B47(BD6,,D6@B$7$Z`H```"_N/___^G$ +M/@``'K^L____%A^-7#T`Z%D]``"_N/___^A1/0``N0@```"_E/___^@//0`` +M=00!SP'+OH#___^+1#4$"T0U`'4$`<X!RU>+?#T`,<F+1#4`]^<!`Q'1@_Z` +M=`6)2P3K)HM$-03WYP'(@](`,<D!0P01T8M$-0CWYP'(@](``4,(@](`B5,, +M7X/#!(/'!(/_H'6SOZS____HNSP``'0$@$VZ`1_#````````````@/\_`$#^ +MBAO-2WB:U`!``,"[\!=<*3NJN/\_`,`TPF@AHMH/R0!``*"8]\_[A)H@FOT_ +M`."K><_1]Q=RL?X_````````````````T.NX!@```/;C!2P2``")QL=%N``` +M``!F+HL&9HE%NBZ+1@*)1;PNBT8&B47`+@^W1@J)1<@QP(E%Q+(#O[C____H +M;!$``+^X____Z*`Q``!E@"4%````_3'`BT6HG[^X____Z4,&``!T%.@0$P`` +M9?8%``````%T/>DH!@``98`E!0```/V*18X\`'1@/`9T0#P2=2*_@/___^@% +M"0``98`-!`````%E]@4``````70%Z?`%``##/`)T!>GF!0``]D6+0'0%Z=L% +M``"`38M`Z\YE@`T$`````F7V!0`````"=--FQT60`0#&18X`N#Y````Y19!] +M6[^`____5^@(,0``7[(#L`#HIA```("]</____]U&66`#00````@@+UO____ +M_W4(98`-!0````(QP+^$____Z`D[``!T#[^`____Z*@\``#I6P4``(E%D,9% +MC@'I3P4``+^X____Z!,[``"_E/___^@+.P``@>'_````^6I!G.L749RQ`>C3 +M.P``G9P0R0A-N[$"Z,X[``"*18LDP(A%EK$"Z,P[``"PP(IENYUR"L'@$.A, +M````ZQCVU,'@$`%%E(M%O/?0$468BT7`]]`119Q9XJW0V0A-NF:+1;K^Q,'@ +M$.@<````OYC___\QP.A:.@``"D67=`2`3;I`L0CI6CL``+ZX____OY3____I +MO@D``'00Z'`1``!E]@4``````751PV6`)04```#]BD6./`!U"(!]C`!U)>M/ +M/`9U"(!]C`!U&>LR/`%T*3P2=`\\`G4%Z7+^__^`?8P`=!;HZQ```'2^O[C_ +M___H/P<``.F+````Z>/^___HPA```'2COX#____H4.W__X%MD/\_``#W19`! +M````=`7_39#K#K$!,,"_@/___^A8+P``BT60T?@%_S\``(E%R.BB_O__,<") +M1<2_N/___^C8#P``Z.D.``"_N/___^@=+P``@+UP_____W4998`-!````""` +MO6______=0AE@`T%`````K^X____Z:P#````Q)UH____,<")1#T$B$0]"":+ +M`XE$/0G!^!>(9#T,)?\```#'1:@`````=42*1#T+9@M$/0EU"L9$/0X!B40] +M$,/&1#T.!L9%JO]E]@4``````G4!P\=$/1"!/P``#[U,/0B#Z1_WV=-D/0@I +M3#T0PSS_=3+'1#T0_W\``/=$/0C___]_QD0]#@IT&O9$/0M`=0[&1:C_9?8% +M``````%T!<9$/0X"P\9$/0X`!8`_``")1#T0@$P]"X##'L6=:/___\9$/00` +MBP.)1#T%BT,$'XE$/0G!^!1FF8A4/0PE_P<``,=%J`````!U:(M$/00+1#T( +M=07I/?___\9$/0X&QD6J_V7V!0`````"=0'#QT0]$`0\```/O4P]"'07BT0] +M!(/I'_?9#Z5$/0C39#T$*4P]$,.+1#T$#[W(@^D?]]G3X(E$/0C'1#T$```` +M`(/!(.O<9CW_!W4[QT0]$/]_``"!9#T(____#XM$/0@+1#T$QD0]#@IT.?9$ +M/0L(=0[&1:C_9?8%``````%T*<9$/0X"ZPX%`#P``(E$/1#&1#T.`(M$/00/ +MI$0]"`/!9#T$`X!,/0N`PQ[%M6C___\6!X/'!%>-?#T`N0(```#SI6:+!E\? +M9IF(5#T()?]_``")1#T,QT6H`````'0O]D0]!X!U!+(2ZPAF/?]_=`FR`(A4 +M/0HQP,.R"HM$/00E____?PM$/0!TZ+("Z^2+1#T$"T0]`+(!=-BR!NO4<AD` +M`(,9``":&0``L!D``*,9``#T%@``KA<``"@<```W'```BAP``*L8```H'``` +M*!P``+@9``"`?#4`!W0B,=N)7#T`BEPU`<'C`B[_HQ\9``"*7#4`P>,"+O^3 +M,QD``#'`P^@]#0``Z!0"``!T(8G>'@^H'^D8____Z"8-``!F0.OEZ!T-```" +MA77____KV.CC`P``9D##'L6U:/___Q8'C7V6N04```#S9J4?9H%]BO__=0N_ +M@/___^BY`P``PXI%GV:8B&6,OX#____H=C8``+$24;$$Z%0W``"#99\/Z%8W +M``"^@/___^@XZ/__L0+H,3<``(M%N&8QP`%%@(M%O!%%A(M%P!%%B#'`BD6? +M9@%%@K``$46$$46(6>*UOX#____H"C8``'4(B460QD6.`</'19!.0```OX#_ +M___H?C<``,9%C@##OQH``$P;``"S&@``OQH``%,;``!T$.CO#```9?8%```` +M``%U`<.?=0AE@"4%````_;^`____OGS___\/MEPU`<'C`B[_HVD:``#H#0P` +M`&9`Z9\```">#X6!````Z,\```!T&NBR#```9?8%``````$/A+D```#HLP(` +M`.M@BT6H"<!T60C`=!UE@`T$`````67V!0`````!#X21````@$P]"T#K.`CD +M=!1E@`T$````!&7V!0`````$='3K(&6`#00````"9?8%``````)T8("]=O__ +M_P%W!<9$/0X`Z$L-``#H=`L``.L+Z&T+```"A77___^#X`>)P8I$/0[H^0P` +M`(UT/02_'`````'/P>$"`<^Y`@````^H!QX6'_.EBB9F)0"`9@M&!!]E9F6) +M!\/H)@L``&9()`?H*PL``#P#PXUU@(U]E.CN#@``@'V.`'46QT6D_S\``(M% +MD"W_/P``OX#____K;H!]C@9U),9%JO]E]@4``````G0@OY3____HU.?__XM% +MI,=%I/\_``#KRH!]C@%T!<9%C`##QD6I_V7V!0`````$=/''19#_?P``QD6+ +M@,=%C/\`"@##Q)UH____9B:+`P^_P.L)Q)UH____)HL#B<(QP(E$/0")1#T$ +M"=!U$HE$/0B(1#T,QD0]#@&)1#T0PYF(5#T,>0+WV`^]R(/I'_?9T^")1#T( +MN!Y````IR(E$/1#&1#T.`,,>Q;5H____%@=7C7P]!+D"````\Z5?'XM$/0B9 +MB%0]#$)U%/=4/02#1#T$`?=4/0B#5#T(`.L0"T0]!'4*B40]$,9$/0X!P\=$ +M/1`^0```#[U,/0AT%XM$/02#Z1_WV0^E1#T(TV0]!"E,/1##BT0]!`^]R(/I +M'_?9T^")1#T(QT0]!`````"#P2#KW.C&#```Z`4```#I/`P``+$%Z)D)```> +M#Z@?]N%FN2@`9BG!OAP```!6F`'&`<8/M\GS9J5FB<%>\V:E'\/H7@L``+$% +MZ&8)```>]N&Y*````&8IP;\<````5Y@!QP''Q;5H____@\8.@'WX`74#@\8. +M\V:E9HG!7_-FI1_I50L``,,QP(E$/03'1#T(````P,9$/0S_QT0]$/]_``#& +M1#T.`L-T#^BS"0``9?8%``````%T[F6#)04```#]Z,/\__\QP)^_E/___[Y^ +M____Z;O\__]T$F6`#04```!!98`E!0```/GK2V6`)04```"XBD6./!)T/#P" +M="`\"G04/`9U"F6`#04```!$ZR;V18N`=`AE@`T%````!-#(<PAE@`T%```` +M0-#(<PAE@`T%`````?9%C`%T"&6`#04````"PW0AZ!4)``!E]@4``````71& +MOY3____H)O___PS_G^E@____Z!O]__]T!>E1____98`E!0```/W'1:@````` +MBD6./!)U'V7V!0`````!=0EE@`T$`````<._@/___^C?_O__ZQ@\`G4M]D6+ +M0'429?8%``````%TUX!-BT#&1:C_C76`C7V4Z,(+``#HL_O__^GK_O__Z+K\ +M___IU/[__W00Z'8(``!E]@4``````74%P\9%C`#I@?O__W00Z%L(``!E]@4` +M`````74$P_95C.EG^___`%"Q`>A[,0``L0'H:S$``+Z4____O[C___]FQT0U +M````N0,```"X`````!M$-0")1#T`1D9&1D='1T?BZ>L44>CK,0``OI3____V +MA6C___\!=`6^N/___[^`____Z"8```#0E6C___]9XM3VA6C___\!=0^^E/__ +M_[^`____Z`4```#IJ#$``(M$-0!F,<`!1#T`BT0U!!%$/02+1#4($40]",,! +M[P'N'A8?%@>Y`P```/.E'\.?98`E!0```/EFQX5H____``">=`KH>`<``.GF +M`0``BD6."D6B#X7.`0``BT60*T6DOX#___]\8X/X/WX.98`-!0````2#R""# +MX#\I19!`Z/?^__\QP+^$____Z/8O``!U&67V!04````$#X0:`0``B(5H____ +MZ0\!``"_@/___^A\,0``9?8%!0````1T#,:%:/___P#IN````)^`O73___\N +M#X6J````GG4'L0'HO#```/]-I/]UC,9%H`#&18P`QH5T____!HM%D#M%I+Z` +M____?@6^E/___[_@____Z`K____H>!```+^X____Z'\O``"/18R_@/___W44 +M]H5H____`71-_H5H____]E6,ZT*^X/___X!]Q`!T#.C-_O__OX#____K*XM% +MD#M%I'0*OY3____HM/[___Z%:/____]%I(I%C(A%H.@3$```O[C___^#?#T0 +M`7UAB?[H!>'__V7_-0````!E@`T!`````\:%</___P#HU>'__V6/!0````"_ +MN/___^LQB460QD6.`8"]=/___RYU',9%C`!E]@4!````!'0.9?8%`0````AU +M!,9%C/^_@/___[Y\____Z/[X__]E@"4%````O(J%:/___\#@!G,(98`-!0`` +M``'0X',(98`-!0```$#0X',(98`-!0````+#J!!T)F6`#00````!9?8%```` +M``%TZ;^`____Z)[[__^^?/___^D*Y/__BD6BBF6.@/P"=`8\`G4IZPKV18M` +M=`H\`G48]D6?0'4298`-!`````%E]@4``````72BZ7+C__^`O73___\8#X2M +M````@/P*=)`\`72,@/P&=6-E@`T$`````F7V!0`````"#X1L____OX#___\\ +M"G4]]D6+@'00QT60`0```,9%C@#I;O___V7V!0`````0#X5@____Z"7A__^! +M19``8```98`-!````!#I1____U#H"^'__U@\!G4B98`-!`````)E]@4````` +M`@^$!?___[^4____4.CEX/__6(#\`0^$KO[__SP*#X2F_O__Z1;]__^`_`IU +M:(!]C`!U+CP!#X35_O__/`9U$F6`#00````"9?8%``````)T6XI%H(A%C+^` +M____Z?\````\"@^$I_[__SP&=1)E@`T$`````F7V!0`````"="V_E/___^@1 +M+0``B46DQD6B`>G+````@/P&=1-E@`T$`````F7V!0`````"=0'#/`8/A:8` +M``!E@`T$`````F7V!0`````"=.6`_`!U#+^4____Z"#@___K+V7V!0`````0 +M=7GIE````'0*Z+<#``#I)?[__V6`)04```#]BD6."D6B#X4%_O__9?\U```` +M`&6`#0$````,Z(L+``!ECP4`````=`B`?8P`=`SK&(M%B#T]X```?@>X/>`` +M`.L,/0(@__]]!;@"(/__`46D@7VD_G\``'\5@WVD`7P@OY3___^^?/___^EL +M]O__OI3____H!=[__^@3WO__ZRR^E/___^CTW?__9?\U`````&6`#0$````# +MQH5P____`.C$WO__98\%`````+^X____Z[*'3#T&(<EY`O;6@>'___]_"TP] +M`F8+3#T`=`+VTC#D]D0]"@%T$?;4QT0]`@````!FQT0]````N0(!``##ADP] +M""#)>0+VUH#A?PM,/00+3#T`=`+VTC#D]D0]"0%T`O;4QT0]!`````#'1#T` +M`````+D#`0``PX=,/00AR7D"]M:!X?___W\+3#T`=`+VTC#D]D0]"`%T`O;4 +MQT0]``````"Y!`$``,.+3#T$]L4$=`+VUH'A_P,```M,/0!T`O;2,.3V1#T% +M"'0"]M3'1#T``````&:!9#T$`/BY!P@``,.'3#T`(<EY`O;6@>'___]_=`+V +MTC#D]D0]!`%T`O;4N0@!``##N20``/,D```D)0``724``'PD```*)@```B8` +M```F```Q)@``,/8/O_+!Y@)F,=(QR2[_EH(E``!F(=)U&SS_B-!U!XB%<?__ +M_\-FB95P____B(5O____PSS_=0B(A7'____K"6;'A7#_____`%#HM0````^V +MV%@N_Z.6)0``]M<X?#T,=3/K)XC7".(\_W4;BIUP____9H'[_P!U#CJE;___ +M_W4&9D)T!NL.9D)T"K``B(5O____ZRC&A6______B.HP[;L,````*<L!WS#` +M`%0]`$=)$$0]`$?B^1C`@^\,PXC$Z#\````\!'4'(.1T"S#`PSP(=?D@Y'3U +M#/_#Z"0````\!'3T/`AT\.OCL@-E(A4!````PV6`)0$```#\90@%`0```,-E +M98H%`0```"0,PV6`)0$```#S90@%`0```,-E98H%!0```"0XP.@#PP^VP(G! +MT>&)R\'C`@'+@\,<96:A"````&;3Z"0#/`)U+&5F98M#"&8E_W]U`[`&PV5E +MBT,$J0```(!U`[`2PR7___]_90L#L`IT`K`"PV7V!0`````0P^B*````9?8% +M``````+#Z',```!E]@4``````<-E]@4`````",-E]@4!````$,-E]@4$```` +M(,-E]@4$`````<-E]@4$`````L-E@`T$````066`)04```#]PV6`#00```!! +M98`-!0````+#98`-!````"##98`-!````!##98`-!`````C#98`-!`````'# +M98`-!`````+#98`-!`````1E]@4`````!,-E@`T%`````<-E@`T%````0,-E +M@`T%`````L-E@`T%````!,-E@"4%````_L-E@"4%````O\-E@"4%````_<-E +M@"4%````N,-E@"4%````^\-E@"4$````W\/H"____W4'Z"C___]U>HJ%>/__ +M_^@&````BH5[____/`%T.30`=33H;/[__P*%=?___R0'B,&P`]#A9F73#0@` +M``!E@"4(````_"0#90@%"````&9ETP4(````P^@W_O__Z,[___^Q".L"L3AE +M98H%!0```"0X93`%!0````#()#AE"`4%````PQX/J`?%M6C___^_`````+D' +M````@'WX`74-\Z4?98`-`````$#K#V:E1T?B^A]E@`T`````0+D(````,=NX +M"@```/?COAP````!QK@#````92,%"````(/X`W1"969EBT8(9B7_?W099?9& +M!X!U!+("ZQQF/?]_=`2R`.L2L@+K#F5EBT8$90L&L@%T`K("98`E"````/QE +M"!4(````9F7!#0@````"0^*3PV:X?P-E9J,`````9KC__V5FHP@```!F0&5F +MHP0```##Q)UH____9B:+`V8-0`!E9J,`````P^A%````98`-`````'_#967& +M!00`````PXJ%>O___SP$96:A!````'0+Q)UH____9B:)`\-FB44LPV5FH0`` +M``!F)7\?9@U`$.O?'@^H'V9E@24`````?Q]F98$-`````$`0O@````#$O6C_ +M__^Y!P```(!]^`%U!/.E'\-FI49&XOH?PP!++```SRT```LO``#$+P``B3`` +M`+(J```++P``"R\``.8J``!T#^@L_?__9?8%``````%T8&6`)04```#]@+U] +M____!'4*Z#/P___IP?W__P^VG7S____!XP*#^PA](H!]CA)U'&6`#00````! +M9?8%``````%T'K^`____Z/OR__\N_Z,<*@``'A8?%@>Y!0```/.E'\/$O6C_ +M__\>%A^-=82Y`@```/.E'XIEC&8E`(!F"T609B:)!^E3_?__````````0'8Z +M:PO>9?\U`````&6`#0$````#@'V.`74%Z1\!``"!?9#_?P``=3!E@`T$```` +M`67V!0`````!=07I(P$``,=%@@````#'188```"`9L=%BO__Z>\```"`?8X& +M=0O'19`!````QD6.`+@^0```.460?;6_@/___U?HGQH``%^R`[``Z#WZ__\Q +MP+^$____Z,(D``!U"8E%D,9%C@'K?8M4/02!^K.VX`T/AWG___^+1#T`<@L] +M``!DIP^#:/___\=$/00`````QT0]``````"#[P*[`,J:._?S4+D$````NV0` +M``")T#'2]_.2U`K`Y`0(X(A$/0!'XNM8"<"(5#T`=!E2,=*Y"@```/?QDEF( +MQ(C(:@"Y!0```.O2@+UP_____W4998`-!````""`O6______=0AE@`T%```` +M`HIEC(#D@(AEBV7$/10````>%A^-=8*Y!0```/-FI1_HZOO__V6/!0````## +M:"TH``#$O6C___^^@3\``+(`BD6./`!T1#P&=#8\`G4<]D6+0'4698`-!``` +M``%E]@4``````709@$V+0(M-B8'A__]_@`^V19#!X!<)R":)!\._@/___^@< +MU___OWY```#H0@```,2]:/___W0M>"2+18DE____@*G___\`=!$E__]_@`^V +M39"`P8#!X1<)R":)!\.X?___?^L%N'\``(`*18S!R`CKZ%=64K``OX#____H +MH_C__^C<&```6EY?.760?%$/A(@````Y?9`/CHD```!E@`T$````"&7V!0`` +M```(=0-86,-E@`T$````(&5EB@4!````)`RT"#C$>!2*18SH$/G__YQX"&6` +M#04````"G<-E]@4`````$'4*98`-!````!#KO+^`____B?!74NA[&```6E^P +M_^@:^/__9H.]</___P!T"&6`#00````P]D6+@'4$9O]-D&:#O7#___\`=!EE +M@`T$````(("];_____]U"&6`#04````",<!`PV@M*```O@$\``"R`HI%CCP` +M=$,\!G0U/`%U"&;'19``/.M2/`)U'?9%BT!U%V6`#00````!9?8%``````%U +M`<.`38M`9L=%D/]#ZRF_@/___^B?U?__O_Y#``#HQ?[__W1]>&B*185FF+^& +M____Z/HA``!T.[$#Z&0B``#$O6C___\>%A^-=86Y`P```/-FI1]FBT609BT` +M/&;!X`2`98L/"D6+@&6,@`IEC&8FB0?#,<#$O6C___\FB0=F)HE'!(IEC(#D +M@&8FB4<&P[C_____Z-S___]FN.]_"F6,Z^?HS/___V8F@4\&\'_#N0Y````K +M39!X18/Y$78%N1$```"_@/___S#`Z!07``"_@/___[($L`#HO?;__SP`=1UF +M@7V*`(!R"'<3@'V,_W4-L``Z18QT!F;W78HPP,.`?8X`=`:`?8X&=3WHG___ +M_W4V9H.]</___P!T&66`#00````@@+UO_____W4(98`-!0````)FBT6*Q+UH +M____9B:)!^G9^/__9C'`@'V.`73H98`-!`````%FN`"`9?8%``````%UTL.Y +M'D```"M-D'A%@_DA=@6Y(0```+^`____,,#H6Q8``+^`____L@&P`.@$]O__ +M/`!U'8%]B````(!R"'<2@'V,_W4,L``Z18QT!?==B##`PX!]C@!T!H!]C@9U +M.^B?____=31F@[UP____`'0998`-!````""`O6______=0AE@`T%`````HM% +MB,2]:/___R:)!^DB^/__,<"`?8X!=.IE@`T$`````;@```"`9?8%``````%U +MT\.Y/D```"M-D'A3@_E!=@6Y00```+^`____,,#HI!4``+^`____L@.P`.A- +M]?__/`!U*X%]B````(!R#G<@@WV$`'4:@'V,_W44L``Z18QT#3'`]UV$&T6( +MB46(,,##@'V.`'0&@'V.!G5"Z)'___]U.V:#O7#___\`=!EE@`T$````(("] +M;_____]U"&6`#04````"BT6$BU6(Q+UH____)HD')HE7!.E6]___,<"9@'V. +M`73E98`-!`````&Z````@&7V!0`````!=<[#BT6`9C'``T64B46XBT6$$T68 +MB46\BT6($T6<B47`L`#0T,.+18"!990``/__*T64B46XBT6$&T68B46\BT6( +M&T6<B47`L`#0T,.^[T4``.L%OD\R``"+39`K3:1X*8/Y0W8%N4,```"P`+^4 +M____5O_67HM%D(E%R(!]C@%T,_9%BX!U+>L=]]F#^4-V!;E#````4;``OX#_ +M____UHM%I(E%R%F`?:(!=`BP`/9%GX!T`K#_@+UT____!G4#]E6@BF6,.F6@ +M=2CH,____[^X____Z!44``"*18R(1<3V1<.`=0R_N/___^AN'@``=5W#4(I% +MC(A%Q.@G____O[C___\\`+@`````=!SW5#T`@T0]``'W5#T$$40]!/=4/0@1 +M1#T(]E7$Z#`>``!U&NB`]/__-`1F2&:8B&7$6#S_=19FQT7(``##6#S_=0J_ +MN/___^B4'P``P^AB'@``BT0]`"7___\?"<)T"(%,/0`````@@60]`````.## +M=!5E@"4%````_66`#00```!!Z2@!``#'1:@`````98`E!0```/F`O73___\I +M=17&1:``QD6B`;^4____Z+4=``")1:2*18X*1:)T=CP!='*H$`^%WP```(I% +MCHIEHCP"=`>`_`)U+>L/]D6+0`^$Q````(#\`G4*]D6?0`^$M0```("]=/__ +M_R\/A,D```#IHP```(!]C@9U$<9%JO^#?9``=0?'19`!````@'VB!G41QD6J +M_X-]I`!U!\=%I`$```#&A73___\&Z`+^__^_N/___^@)'0``#X2$````98`E +M!0```+[V1<0!=`AE@`T%`````8-]J`!U!>F\]/__@'VH`'0;9?8%``````%T +M!>FG]/__PX!]J@!U!>F;]/__98`-!`````)E]@4``````G3BZ83T__]E@`T$ +M`````<=%J/\```#K$(!]C@9T!H!]H@9U!,9%JO]E@`T%````!>L(98`E!0`` +M`/YE@`T%````0.N```"``````````````(```````````````````/4?F.S` +MN_`77"D[JK@``````````````0```````````,``A&3>^3/S!+4```"-]!$% +M7ZQ_B@```````````(``_________[\`H)^'UOLY&L"5`&#R5]QH7L+3I``@ +M_3RTWL_1`*X`@.:SF$C6MQ^S`,").>QWK)O6M0"`4TR1%BZT/+<`@/R+0GBW +MA?*W`.!6NF/5:R-.N`#@,2:K4_@??+@`8/V@HA^Z*9.X`,!7&+[*>[&>N`"@ +M<$3^#15VI+@`8*H;F]*/6*>X`&"4Z9J^V,FHN`"`,W(E%X""J;@`P#3":"&B +MV@_)`(!%>]H-*SAC[0!@%>L&9,FOV_H`P#)N>V'5U*W^`,`V3N]GN=VJ_P"` +M0B6Q2]VMZO\`X+O5E-O=JOK_``!HN=3=K:K^_P#`2[G=W:JJ__\`H$O=W:VJ +MZO__`*#;W=VJJOK__P#@W=VMJJK^__\`X-W=JJJJ____`.#=K:JJZO___P#@ +MW:JJJOK___\`X*VJJJK^____`,PY``"S.0``L#D``*<Y```6.0``I$$``*I! +M``!!.@``-#P```D\```'.P``"3P``$`\```)/```\SL``#0\```T/```03H` +M`!X\``#B.@``'CP``/,[``"S.@``03H``/<\``#`/```!SL``%L\```D.P`` +MWSP``%L\``"O/```)#L```,]``"9/```XCH```T]```-/0``LSH``$$Z```[ +M/0``.ST```<[``!9/0``.ST``"0[``!#/0``KSP``"0[```#/0``F3P``.(Z +M```E/0``)3T``+,Z``!E_S4`````98$-```````#``!E@`T$````(,9%Q`!F +MQX5L____``"+1:0K19")1:B#^`]^/X/X/W\%Z=````#H$-S__^@H#P``BT7( +M@_@`#X\%`@``#XP#`@``O[S____H=!D```^%\P$``,9%Q@'IZ@$``.C-&0`` +MZ-$9``#H.OK__V;1I6S___^`?<,`=53H1AL``+^`____Z)<9``#H]OG__^A' +M&P``ON@S``#H;AL``.@Q&P``@664````_^@+&P``9O^%;/___X!]PP!T$[D! +M````Z&T9``"!990```#_ZP7H%!H``/]%J(-]J`]VC+D'````Z`,:``"!98`` +M``#@QT6H#P```.CA&0``Z`;9___HUQH``+D)````Z"X9``"!98```.#_Z+H: +M``#H$AD``(%EE```X/^!9;@```#@Z)0:``#H3OG__^B1&@``Z'D:``#H;1H` +M`+D>````Z.P8``#'18``````N1X```#HB1D``.@EV___O[C____HL1@``,9% +MP\#H6QH``(%EE````."`??<`L`&Y!P```'4'L`"Y"````%"_[O___^A0&0`` +MZ`,:``#H7-C__X%EN````.!8T.BY!P```',%N08```#H9A@``.@2&@``9M&M +M;/___W,9N`H```#W9:@%XC0``(G&Z"8:``#H\!D``&:#O6S___\`=!:Y`0`` +M`.@]&```@66```#@__]-J.N^@'V+`+D(````=`BY!P```/]-J.C'&```QT60 +M_C\``(M%J"E%D+^`____Z!49``#H1!D``(M%D(E%R,9%Q@!ECP4`````P\=$ +M/0``````QT0]!`````#'1#T(`````,=$/0P```$`QT0]$`````##QT0]```` +M``#'1#T$-<)H(<=$/0BBV@_)QT0]#`````#'1#T0_S\``,.*58R*=:#&18P` +MQD6@`(CS@^,(BT6D.T60?S=\*XM%B#M%G'\M?"&+180[19A_(WP7O[C____H +MG/____]-R`G;#X2S````ZSA3Z!<2``!;@\L$4U+H[_S__UI;"=MU(8M%R(/X +M`']/?`:`?<8!=&=2Z*?)__]:98`-!````!#K>;^`____Z$W___\N_Z."-0`` +MQH5T____!>L*_T60QH5T____!E*-?92-=;CHWO#__^AW]___6F7_-0````!E +M@0T```````,``%+HZPL``%IECP4`````9H.]</___P!T&66`#00````@@+UO +M_____W4(98`-!0````*(5<3#=!?H6>W__V7V!0`````!=0'#Z/;'___K>&6` +M)04```#]#[9=C@I=H@^%W@````^VG73___^`ZQ1T4(#[_'0\@7V0_S\```^/ +MQ0```'PF@'V,``^$N0```(%]B````(`/A:P```"#?80`#X6B````Z=0!``"[ +M"````.L/@'V,``^%C````+L$````+O^3DC4``+^X____OGS____I6\O__V6` +M#00````"9?8%``````(/A%O___^_@/___^CQR/__OY3____HY\C__^E?____ +M98`-!`````)E]@4``````@^$+/___[^`____Z,+(___I.O___V6`#00````" +M9?8%``````(/A`?____KM/;#$'0;98`-!`````%E]@4``````0^$ZO[__^GF +M_O__@'V.`G0(@'VB`G4OZPSV18M`=`R`?:("=1SV19]`=19E@`T$`````67V +M!0`````!#X2O_O__Z33*__^*?8X/MH5T____+!1T03S\="F!?9#_/P``?!D/ +MA,;^__^!?9#_?P``=8"`?8P`#X5V____N(````#K%(!]C`!T"8#_`0^%8/__ +M_[A`````BEVB]L<$=`*W`_;#!'0"LP.!XP,#``#`YP(`^S#_P>,"`<,N_Z.> +M-0``98`-!`````)E]@4``````@^$&_[__X!]H`!T-[^X____Z-3\____1<CK +M,F6`#00````"9?8%``````(/A/#]__^_N/___^BO_/__ZQ"`?:``=<F_N/__ +M_^AT_/__BE6,B%7$Z4G^__]E@`T$````!&7V!0`````$#X2S_?__QT6(```` +M@,=%C```"@#'19#_?P``OX#___^*5:#VTHA4/0SI$/[__V6`#00````"9?8% +M``````(/A'7]__^_@/___XI5H(A4/0SIZ?W__X%]D/\_``!_.GPU@7V(```` +M@'<O@WV$`'4IZ47^__]E@`T$````!&7V!0`````$#X0O_?__ZPF!?9#_/P`` +M?0/V5:"_E/___^F<_?__98`-!`````)E]@4``````@^$`?W__^O;98`-!``` +M``)E]@4``````@^$Z?S__XI5C#!5H.O`98`-!`````)E]@4``````@^$R_S_ +M_XI5H#!5C+^`____Z4#]__];_W0U`/]T-03_=#4(_W0U#/]T-1#_XUN/1#40 +MCT0U#(]$-0B/1#4$CT0U`/_CQT0U``````#'1#4$`````,=$-0@```"`QT0U +M#`````#'1#40_S\``,-FQX5L____``"X_S\``"M%D(/X/W]1B460@_@/=W[H +MRA(``+@*````]V60!4(T``")QNBA%```9M&E;/___X!]PP!U$^A%%```@66` +M``#@_V;_A6S_____19"#?9`/=S#H-A,``.N\QD6@`,=%I/\_``"^!C0``.AM +M%```Z`T3``#H;-3__^B$!P``Z2X!``#'19`/````Z/L2``#HG!,``+$>Z$<2 +M``"#98``L1[HZA(``.B&U/__Z-$3``"!98````#@OOPS``#H$10``(M-D(/! +M"N@$$@``@66X``#@_^B($P``Z`,2``#HF!,``.AG\O__BTV00>C@$0``@66X +M``#@_^A^$P``O@8T``#HMA,``.A5$P``Z&H3``#H;!(``.B1T?__N0@```#H +MK!$``(%EN```X/]FT:UL____<R3H2!,``.@\$P``BTV0Z),1``"!990``.#_ +M9H%-GH``Z-/Q__]F@[UL____`'01Z&<1``"!9;@``.#__TV0Z[BQ"(!]PP!T +M!;$'_TV0Z/41``"X_S\``"M%D(E%R,=%Q`````#V1<.`=0JQ`>C6$0``_TW( +MZ-42``#H[1(``,/V58SH.?[__XUUN(U]E.@2Z___OH#____H^_W__\9%C/_& +MA73___\&OKC____HN/W__^B,\?__Z!0&``"-=;B-?93HWNK__[Z`____Z+#] +M___HW=+__^CU!0``P^CW!@``=07IL]K__X%]D/\_``!_\GP:@7V(````@'?G +M@WV$`'7A@'V,`'3;_TV0Z]9E@`T$````(&7_-0````!E@0T```````,``&6! +M)0````#_\___@7V0_C\``'0(@'V,`'59ZQ6!?8@```"`=U6#?80`=4^`?8P` +M=4+H9/W__XM-R(/Y`'X&QD7&`.L-Z,/"__]E@`T$````$("];_____]U"&6` +M#04````"98\%`````+^X____Z0;:___HX?[__^N\@'V,`'5_OI3____HY_S_ +M_\:%=/___P;HAO#__^@.!0``C76XC7V`Z-CI___HKO[__XUUN(U]@.C(Z?__ +MOI3____HL?S__\:%=/___P7H4/#__^C8!```_T7(C76XC7V`Z)_I__^^E/__ +M_^B(_/__QH5T____!N@G\/__Z*\$``#I4O___[Z4____Z&C\___&A73___\% +MZ`?P___HCP0``(UUN(U]@.A9Z?__Z'#\__^-=;B-?8#H2>G__[Z4____Z#+\ +M___&A73___\%Z-'O___H600``(UUN(U]@.@CZ?___TV0OI3____H"?S__\:% +M=/___P;HJ.___^@P!```Z=/^___&1=H`ZP3&1=H!9?\U`````&6!#0`````` +M`P``O^+____H)1```(I%HHA%XHM%I(F%:/___XI%H(A%SL9%V`"`?=H`=2CH +MOPX``+XD-```Z*`0``"`?<,`=5SI7`$``.CN#P``QD78_^F+`0``BT60B47( +M@'V,`'7FZ.4/``"Y_S\``"M-D(/Y2'8%N4@```#H;0X``&:+39:!X?\?```) +MRG0&9H%-E@`@@$V?@(%EE````.#K.^CX#@``Z)(/``"Y`0```.C9#@``O[K_ +M___HN@T``'0XQT7(_C\``+^X____Z%</``#H=P\``.BY#P``_[5H____Z-C- +M__^/A6C___^!9;@```#@Z=0```#H]-C__[^"____Z'$-``!U%\9%C@&*1<XP +M18R-=8"-?;CHS.?__^MO@67@````_^A`#P``BX5H____B46DBD7.B$6@Z-[/ +M___HT`(``(M%R#W^?P``?TB#^`!\2G\2O[S____H'0T``'4\QD7&`>L$QD7& +M`&:#O7#___\`=!EE@`T$````(("];_____]U"&6`#04````"98\%`````,/H +M!+___^OQZ-6____KZO]%D.B@#@``OA`T``#H&`\``+D'````Z,P-``#&1=C_ +MQT7(_S\``+^Z____Z*(,``!T"K^X____Z$8.``#HI@X``&;'A6S___\``+C_ +M/P``*T7(B46H@_@/#X>0````Z(L.``"Y!P````--J.C?#```Z,\,``#H'NW_ +M_X!]PP!T&8%EN```X/_&1<,`Z%<.``!FQX5L____`0!FT:5L____Z.4-``"+ +M3:CHH@P``(%-B```@`#HW^S__X!]PP!T%X%EN```X/_&1<,`Z!@.``!F_X5L +M____N0$```#H$`T``/]%J(-]J`]VL.@`#0``98`-!````"#HCPT``.B>SO__ +MBTVH@_E(=@6Y2````.@K#```@66X````X.C)#0``Z)CL___HN`T``(M-J(/Y +M2'8%N4@```#H%0P``(%E@````.#H80T``+X:-```Z-D-``#HF0T``.A9#0`` +MBTVH08/Y2'8%N4@```#HV0L``(%EE````/#H0NS__X%EN````.#H-0T``.A= +M#0``_[5H____Z(/+__^/A6C___^!9;@```#@Z$<-``"^+C0``.B.#0``N0<` +M``#H@@L``/]-J&;1K6S___]S(HMUJ$ZX"@```/?FB<:!QDPT``#H:@T``.@* +M#0``Z*_K__]F@[UL____`'0*Z$,+``#_3:CKP;@'0```*T6HB47(@66X``#@ +M_[^X____Z-H*``!T"K^X____Z%T,``"`?=H`=3CH&];__^BR#```BT7(B46D +MQH5T____!8I%V(A%H.B<Z___Z)P,``"*1<2(18R+1<B)19#I]/S__XI%V(A% +MQ.OBQD6Z`;(#O[C___^P`.C)W___O[C___^T!*@!=0'#_T0]$+$!Z,(*```A +MTG0%@$P]`@'#B<&'1#T0*<&#^4-^!;E#````,,#KVW0JZ%CA__]E@"4%```` +M^V7V!0`````!=0)8P[^`____OG[___]8G^EDU/__98`E!0```/OH3=7__W0$ +M,<#KW,=%J`````!E@"4%````_8I%CCP!=FT\!G(.=#^_@/___^@?U___ZPKV +M18M`=<Z`38M`9?8%``````%U"F6`#00````!ZY2-=8"-?93H`.3__\9%J/]8 +MZ1S7__]E]@4``````G4-98`-!`````+I:/___\9%JO^_@/___^CQO/__@\@! +MPW0?Z)7@__]E@"4%````^V7V!0`````!=0)8PUCIHM/__V6`)04```#YBD6. +M/`%^:SP&?"IT1H"]=/___Q)U$SP2=`^`?8P`=-+'19#_/P``Z\F_@/___^AE +MUO__ZPKV18M`=;>`38M`98`-!`````%E]@4``````72?ZY]E@`T$`````F7V +M!0`````"=(N_@/___^A5O/__@\@!PS'`BH5T____BF6,B46LQD6,`&;'A6C_ +M__\``(M%D"W^/P``?%6#^#]^"F6`#04````$6,/'19@UPF@AQT6<HMH/R<=% +MH`````#'1:3^/P``*4600.A@U___BT6$"T6(=0^)18")19#'18X!````ZQ*_ +M@/___^CU"0``98`-!````"#VA6C___\$=`1F]U6M]H5H____`G0&]E6O]E6N +M]H5H____`74!P_95KX!]C@%U)X!]K!-U(<9%BX#&18X`QT60_S\``+Z4____ +MZ%3U__^#Q`3I(@,``&6`#00````@QT64`````,=%F#7":"''19RBV@_)QT6@ +M`````,=%I/X_``#&18S_QH5T____!>B\Z/__9?\U`````&6!)0````#_\___ +MZ#+]__]ECP4`````C76XC7V`Z/7A___#9L>%;/___P``B460@_@0?UC_39#H +MY`<``+@*````]V60!>(T``")QNB["0``9M&E;/___X!]PP!U$^A?"0``@66` +M``#@_V;_A6S_____19"#?9`/=P?H4`@``.N\Z$4(``#'18``````Z-\(``#H +MV\G__XM-D-'A@\$(Z&T'``"!9;@```#_Z`L)``"^0C0``.A#"0``Z-L(``#H +M5`<``.C8"```L0GH4P<``.C8"```Z)+G___H,`<``.C0"```_TV09M&M;/__ +M_W,VZ*P(``#HC0@``.AMY___Z+`(``"+39#1X>@,!P``@664``#@_^AUY___ +MZ(X(``"!9>````#_9H.];/___P!T&+$!O^S____H[`8``(%E[```X/__39#K +MG[$(@'WW`'0%L0?_39"_[O___^AW!P``Z"H(``"X_S\``"M%D(E%D+^`____ +MZ-T'``#H'`@``.@_!P``QT6D_S\``,=%H`````"_E/___^B[!P``P^CG^___ +M=0^^E/___^A>\___Z8S3___H,/W__X!]C@%U+X!]K_]UX,=%@`````#'180` +M````QT6(````@,=%C```"@#'19#_?P``QD6I_^NWN/\_```K19"#^#]^?#W_ +M/P``?$"*1:V(18QE@`T$````$&7V!0`````0=0F!19``8```ZX*_@/___[@! +M````Z$+[___'19``````QD6.!NEC____OI3____HP?+__X!]K_\/A8T```!F +MBT6MB$6,B&6@9?\U`````&6!#0```````P``_W6LZS!E_S4`````98$-```` +M```#``#_=:SHE?W__X]%K&:+1:V(18R(9:#_=:R`?:__=0?H90```.L%Z&,` +M``"`O6______=0AE@`T%`````HUUN(U]@.A+W___OI3____H-/+__X]%K&6/ +M!0````"*1:TR1:Z(18SI5]+__[Z`____Z.3Q__^-=92-?8#H%-___[Z4____ +MZ.;Q___#Z-O___]E_S4`````98$-```````#``#H^\;__^@3^O__98\%```` +M`,.^N/___^B;\?__Z,W___^-=;B-?8#HQM[__[Z4____Z)CQ__]E@0T````` +M``\``,:%=/___P7H0^7__^C+^?__C76XC7V4Z)7>__^^@/___^A^\?___T60 +MZ'_____#Z+#Z__]U&L9%K"N*18R(1:V-=8"-?;CH9M[__^F=`0``Z"O[__^` +M?8X!=0N`?:__==_I?0$``(!]K_\/A-@!``"X_S\``"M%D(/X'GQ4?Q&!?8@` +M`!"-=TERMH-]A`!TL#W_/P``?*EE@`T$````$&7V!0`````0=0F!19``8``` +MZXZ_@/___[@!````Z$KY___'19``````QD6.!NEO_____W6L9?\U`````&6! +M)0````#_\___98$-```````#``!`Z,;[__^^@/___^AU\/__OI3____H:_#_ +M_^B8_O__OI3____H<_#__[Z`____Z&GP___HJ/[__V6/!0````"/1:QE@"4% +M````_8"];_____\/A:4```!E@`T%`````NF8````C76`C7V4Z%'=___H@,7_ +M_^AR^/__C76XC7V`Z#S=__^^E/___^@E\/__QH5T____!>C$X___Z$SX__^- +M=;B-?93H%MW__[Z`____Z/_O____19#H`/[__XUUN(U]E.CYW/__OH#____H +MXN___\:%=/___P;H@>/__^@)^/__P^@+^?__=2W&1:PLQD6N`+ZX____Z+?O +M__^`?:PL=`6*1:WK`XI%KHA%Q+^X____Z:3,___H<_G__X!]C@%U*X!]K_]U +MRL=%N`````#'1;P`````QT7``````,=%Q````0#'1<@`````Z[J`?:__#X0H +M_O__N/\_```K19"#^#Y\%7^,@7V(````@'<*@WV$``^$>?____]UK&7_-0`` +M``!E@24`````__/__V6!#0```````P``0.@M^O__Z!/]__^-=;B-?8#H$=S_ +M_^BP_O__98\%`````(]%K&6`)04```#]@+UO_____P^%*?___V6`#04````" +MZ1S____H2_?__W4L,<"*98R)1:R^E/___^BZ[O__@'VO_W4%Z)/\__]FBT6M +MB$6,B&6@Z=/.___H=_C__X!]C@%TT;C_/P``*T60@_@^?%)_#X%]B````(!W +M1X-]A`!U03W_/P``?*QE@`T$````$&7V!0`````0=0F!19``8```ZY&_@/__ +M_[@!````Z*WV___'19``````QD6.!NER_____W6L9?\U`````&6!)0````#_ +M\___98$-```````#``"#^!Y\'W\1@7V(```0C7<4<@:#?80`=0PQV[Z`____ +MZ,3M__^<0.@$^?__G704OH#____HL.W__[Z4____Z*;M__^<Z-+[__^==!2^ +ME/___^BJ[?__OH#____HH.W__[ZX____Z'_M__]T!YSHTOO__YV^@/___^B# +M[?__=`J^N/___^A@[?__Z#K]__^^@/___^AH[?__C76XC7V4Z(':__]ECP4` +M````CT6LZ:O^__]FBT0]"&8+1#T&9@M$/01F"T0]`F8+1#T`=0'#9KC__\.+ +M1#T`"T0]!`M$/0AU`<.X_____\,QP(E$/0")1#T$B40]",-F,<!FB40]"&:) +M1#T&9HE$/01FB40]`F:)1#T`PXM-J-#AZQFQ`;^X____ZQ"Q"+^4____ZP>Q +M"+^`____L``QTM#(#[[`4+4"@/D@<EF`^6!R(70#9@G""U0]``M4/00+5#T( +MB.")1#T`B40]!(E$/0A8PQX6'Q8'`>^)_HC+@^%@P>D%B,@+%H/&!.+Y9KD# +M`"C!B,_SI8C!B.#SJX/O#"GO'V:)V5A74(M$/0`QVP^MPPG:BT0]!`^M1#T` +M@\<$_LUU\%B(X`^M1#T`7_C#O[K____K%+$(OY;____K"[$(ZP*Q`;^"____ +M@/E0?@*Q4(C()'AT,E%7'F:89L'H`V9(N0D````!SP^_P"G!`>^)_BG&3A8? +M%@?]\Z2(#XG!B?Y/\Z3\'U]9@^$'=!MFT60]`&;15#T"9M%4/01FT50]!F;1 +M5#T(XN7#]D0]"X!U&%>Y`0```(/'`NC/____7_],/1#V1#T+@,/HV____W3Y +MP[^"____OI;____K8K^Z____ZP^_XO___^@%````OY;___^^@O___^M%OY;_ +M___K!;^"____ON[____K,K^6____ZP6_@O___[[B____ZQ^_XO___^L3O^[_ +M___K#+^6____ZP6_@O___[ZZ____`>X>%A\6!P'ON04```#S9J4?P[[R,P`` +MZ`H```"_N/___^EE\___Z"4```#I;][__[^"____Z!L```#I@][__[^6____ +MZ^_H!0```.E5P/__OY;___\>#NNJ````+G-Y;71A8@`N<W1R=&%B`"YS:'-T +M<G1A8@`N=&5X=``N9&%T80`N8G-S```````````````````````````````` +M```````#``$``````+12`````````P`"``````"T4@````````,``P`!```` +M```````````1``$`"P```!@`````````$0`!`!L````<`````````!$``0`G +M````M%(````````1``$``&9P95]S=&%R=`!F<&5?<F5G7W-E9VUE;G0`9G!E +M7W)E8V]V97(`9G!E7V5N9``````````````````````````````````````` +M`````````````````!L````!````!P``````````$```M%(````````````` +M!``````````A`````0````,```"T4@``M&(```````````````````0````` +M````)P````@````#````M%(``+1B```````````````````$`````````!$` +M```#``````````````"T8@``+````````````````0`````````!`````@`` +M````````````X&(``(`````&````!`````0````0````"0````,````````` +<`````&!C```O```````````````!```````````0 +` +end diff --git a/i386/i386/fpe_linkage.c b/i386/i386/fpe_linkage.c new file mode 100644 index 00000000..cac58e0b --- /dev/null +++ b/i386/i386/fpe_linkage.c @@ -0,0 +1,359 @@ +/* + * 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. + */ + +/* + * Support routines for FP emulator. + */ + +#include <fpe.h> + +#include <cpus.h> + +#include <mach/std_types.h> +#include <mach/exception.h> +#include <mach/thread_status.h> + +#include <kern/cpu_number.h> +#include <kern/thread.h> + +#include <vm/vm_kern.h> + +#include <mach/machine/eflags.h> +#include "vm_param.h" +#include <i386/pmap.h> +#include <i386/thread.h> +#include <i386/fpu.h> +#include "proc_reg.h" +#include "seg.h" +#include "idt.h" +#include "gdt.h" + +#if NCPUS > 1 +#include <i386/mp_desc.h> +#endif + +extern vm_offset_t kvtophys(); + +/* + * Symbols exported from FPE emulator. + */ +extern char fpe_start[]; /* start of emulator text; + also emulation entry point */ +extern char fpe_end[]; /* end of emulator text */ +extern int fpe_reg_segment; + /* word holding segment number for + FPE register/status area */ +extern char fpe_recover[]; /* emulation fault recovery entry point */ + +extern void fix_desc(); + +#if NCPUS > 1 +#define curr_gdt(mycpu) (mp_gdt[mycpu]) +#define curr_idt(mycpu) (mp_desc_table[mycpu]->idt) +#else +#define curr_gdt(mycpu) (gdt) +#define curr_idt(mycpu) (idt) +#endif + +#define gdt_desc_p(mycpu,sel) \ + ((struct real_descriptor *)&curr_gdt(mycpu)[sel_idx(sel)]) +#define idt_desc_p(mycpu,idx) \ + ((struct real_gate *)&curr_idt(mycpu)[idx]) + +void set_user_access(); /* forward */ + +/* + * long pointer for calling FPE register recovery routine. + */ +struct long_ptr { + unsigned long offset; + unsigned short segment; +}; + +struct long_ptr fpe_recover_ptr; + +/* + * Initialize descriptors for FP emulator. + */ +void +fpe_init() +{ + register struct real_descriptor *gdt_p; + register struct real_gate *idt_p; + + /* + * Map in the pages for the FP emulator: + * read-only, user-accessible. + */ + set_user_access(pmap_kernel(), + (vm_offset_t)fpe_start, + (vm_offset_t)fpe_end, + FALSE); + + /* + * Put the USER_FPREGS segment value in the FP emulator. + */ + fpe_reg_segment = USER_FPREGS; + + /* + * Change exception 7 gate (coprocessor not present) + * to a trap gate to the FPE code segment. + */ + idt_p = idt_desc_p(cpu_number(), 7); + idt_p->offset_low = 0; /* offset of FPE entry */ + idt_p->offset_high = 0; + idt_p->selector = FPE_CS; /* FPE code segment */ + idt_p->word_count = 0; + idt_p->access = ACC_P|ACC_PL_K|ACC_TRAP_GATE; + /* trap gate */ + /* kernel privileges only, + so INT $7 does not call + the emulator */ + + /* + * Build GDT entry for FP code segment. + */ + gdt_p = gdt_desc_p(cpu_number(), FPE_CS); + gdt_p->base_low = ((vm_offset_t) fpe_start) & 0xffff; + gdt_p->base_med = (((vm_offset_t) fpe_start) >> 16) & 0xff; + gdt_p->base_high = ((vm_offset_t) fpe_start) >> 24; + gdt_p->limit_low = (vm_offset_t) fpe_end + - (vm_offset_t) fpe_start + - 1; + gdt_p->limit_high = 0; + gdt_p->granularity = SZ_32; + gdt_p->access = ACC_P|ACC_PL_K|ACC_CODE_CR; + /* conforming segment, + usable by kernel */ + + /* + * Build GDT entry for user FP state area - template, + * since each thread has its own. + */ + gdt_p = gdt_desc_p(cpu_number(), USER_FPREGS); + /* descriptor starts as 0 */ + gdt_p->limit_low = sizeof(struct i386_fp_save) + + sizeof(struct i386_fp_regs) + - 1; + gdt_p->limit_high = 0; + gdt_p->granularity = 0; + gdt_p->access = ACC_PL_U|ACC_DATA_W; + /* start as "not present" */ + + /* + * Set up the recovery routine pointer + */ + fpe_recover_ptr.offset = fpe_recover - fpe_start; + fpe_recover_ptr.segment = FPE_CS; + + /* + * Set i386 to emulate coprocessor. + */ + set_cr0((get_cr0() & ~CR0_MP) | CR0_EM); +} + +/* + * Enable FPE use for a new thread. + * Allocates the FP save area. + */ +boolean_t +fp_emul_error(regs) + struct i386_saved_state *regs; +{ + register struct i386_fpsave_state *ifps; + register vm_offset_t start_va; + + if ((regs->err & 0xfffc) != (USER_FPREGS & ~SEL_PL)) + return FALSE; + + /* + * Make the FPU save area user-accessible (by FPE) + */ + ifps = current_thread()->pcb->ims.ifps; + if (ifps == 0) { + /* + * No FP register state yet - allocate it. + */ + fp_state_alloc(); + ifps = current_thread()->pcb->ims.ifps; + } + + panic("fp_emul_error: FP emulation is probably broken because of VM changes; fix! XXX"); + start_va = (vm_offset_t) &ifps->fp_save_state; + set_user_access(current_map()->pmap, + start_va, + start_va + sizeof(struct i386_fp_save), + TRUE); + + /* + * Enable FPE use for this thread + */ + enable_fpe(ifps); + + return TRUE; +} + +/* + * Enable FPE use. ASSUME that kernel does NOT use FPU + * except to handle user exceptions. + */ +void +enable_fpe(ifps) + register struct i386_fpsave_state *ifps; +{ + struct real_descriptor *dp; + vm_offset_t start_va; + + dp = gdt_desc_p(cpu_number(), USER_FPREGS); + start_va = (vm_offset_t)&ifps->fp_save_state; + + dp->base_low = start_va & 0xffff; + dp->base_med = (start_va >> 16) & 0xff; + dp->base_high = start_va >> 24; + dp->access |= ACC_P; +} + +void +disable_fpe() +{ + /* + * The kernel might be running with fs & gs segments + * which refer to USER_FPREGS, if we entered the kernel + * from a FP-using thread. We have to clear these segments + * lest we get a Segment Not Present trap. This would happen + * if the kernel took an interrupt or fault after clearing + * the present bit but before exiting to user space (which + * would reset fs & gs from the current user thread). + */ + + asm volatile("xorl %eax, %eax"); + asm volatile("movw %ax, %fs"); + asm volatile("movw %ax, %gs"); + + gdt_desc_p(cpu_number(), USER_FPREGS)->access &= ~ACC_P; +} + +void +set_user_access(pmap, start, end, writable) + pmap_t pmap; + vm_offset_t start; + vm_offset_t end; + boolean_t writable; +{ + register vm_offset_t va; + register pt_entry_t * dirbase = pmap->dirbase; + register pt_entry_t * ptep; + register pt_entry_t * pdep; + + start = i386_trunc_page(start); + end = i386_round_page(end); + + for (va = start; va < end; va += I386_PGBYTES) { + + pdep = &dirbase[lin2pdenum(kvtolin(va))]; + *pdep |= INTEL_PTE_USER; + ptep = (pt_entry_t *)ptetokv(*pdep); + ptep = &ptep[ptenum(va)]; + *ptep |= INTEL_PTE_USER; + if (!writable) + *ptep &= ~INTEL_PTE_WRITE; + } +} + +/* + * Route exception through emulator fixup routine if + * it occured within the emulator. + */ +extern void exception(); + +void +fpe_exception_fixup(exc, code, subcode) + int exc, code, subcode; +{ + thread_t thread = current_thread(); + pcb_t pcb = thread->pcb; + + if (pcb->iss.efl & EFL_VM) { + /* + * The emulator doesn`t handle V86 mode. + * If this is a GP fault on the emulator`s + * code segment, change it to an FP not present + * fault. + */ + if (exc == EXC_BAD_INSTRUCTION + && code == EXC_I386_GPFLT + && subcode == FPE_CS + 1) + { + exc = EXC_ARITHMETIC; /* arithmetic error: */ + code = EXC_I386_NOEXT; /* no FPU */ + subcode = 0; + } + } + else + if ((pcb->iss.cs & 0xfffc) == FPE_CS) { + /* + * Pass registers to emulator, + * to let it fix them up. + * The emulator fixup routine knows about + * an i386_thread_state. + */ + struct i386_thread_state tstate; + unsigned int count; + + count = i386_THREAD_STATE_COUNT; + (void) thread_getstatus(thread, + i386_REGS_SEGS_STATE, + (thread_state_t) &tstate, + &count); + + /* + * long call to emulator register recovery routine + */ + asm volatile("pushl %0; lcall %1; addl $4,%%esp" + : + : "r" (&tstate), + "m" (*(char *)&fpe_recover_ptr) ); + + (void) thread_setstatus(thread, + i386_REGS_SEGS_STATE, + (thread_state_t) &tstate, + count); + /* + * In addition, check for a GP fault on 'int 16' in + * the emulator, since the interrupt gate is protected. + * If so, change it to an arithmetic error. + */ + if (exc == EXC_BAD_INSTRUCTION + && code == EXC_I386_GPFLT + && subcode == 8*16+2) /* idt[16] */ + { + exc = EXC_ARITHMETIC; + code = EXC_I386_EXTERR; + subcode = pcb->ims.ifps->fp_save_state.fp_status; + } + } + exception(exc, code, subcode); +} diff --git a/i386/i386/fpu.c b/i386/i386/fpu.c new file mode 100644 index 00000000..dc49a6a4 --- /dev/null +++ b/i386/i386/fpu.c @@ -0,0 +1,750 @@ +/* + * 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. + */ +/* + * Support for 80387 floating point or FP emulator. + */ +#include <cpus.h> +#include <fpe.h> +#include <platforms.h> + +#include <mach/exception.h> +#include <mach/machine/thread_status.h> +#include <mach/machine/fp_reg.h> + +#include <machine/machspl.h> /* spls */ +#include <kern/mach_param.h> +#include <kern/thread.h> +#include <kern/zalloc.h> + +#include <i386/thread.h> +#include <i386/fpu.h> +#include "cpu_number.h" + +#if 0 +#include <i386/ipl.h> +extern int curr_ipl; +#define ASSERT_IPL(L) \ +{ \ + if (curr_ipl != L) { \ + printf("IPL is %d, expected %d\n", curr_ipl, L); \ + panic("fpu: wrong ipl"); \ + } \ +} +#else +#define ASSERT_IPL(L) +#endif + +extern void i386_exception(); + +int fp_kind = FP_387; /* 80387 present */ +zone_t ifps_zone; /* zone for FPU save area */ + +#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() +{ + unsigned short status, control; + + /* + * 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)); /* allow use of FPU */ + + 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; + asm volatile(".byte 0xdb; .byte 0xe4"); /* fnsetpm */ + } + else { + /* + * We have a 387. + */ + fp_kind = FP_387; + } + /* + * Trap wait instructions. Turn off FPU for now. + */ + set_cr0(get_cr0() | CR0_TS | CR0_MP); + } + else { +#if FPE + /* + * Use the floating-point emulator. + */ + fp_kind = FP_SOFT; + fpe_init(); +#else /* no fpe */ + /* + * NO FPU. + */ + fp_kind = FP_NO; + set_cr0(get_cr0() | CR0_EM); +#endif + } +} + +/* + * Initialize FP handling. + */ +void +fpu_module_init() +{ + ifps_zone = zinit(sizeof(struct i386_fpsave_state), + THREAD_MAX * sizeof(struct i386_fpsave_state), + THREAD_CHUNK * sizeof(struct i386_fpsave_state), + 0, "i386 fpsave state"); +} + +/* + * Free a FPU save area. + * Called only when thread terminating - no locking necessary. + */ +void +fp_free(fps) + 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 + */ + fwait(); + + /* Mark it free and disable access */ + clear_fpu(); + } +#endif /* NCPUS == 1 */ + zfree(ifps_zone, (vm_offset_t) fps); +} + +/* + * 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(thread, state) + thread_t thread; + struct i386_float_state *state; +{ + register pcb_t pcb = thread->pcb; + register struct i386_fpsave_state *ifps; + register 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) { + 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) { + zfree(ifps_zone, (vm_offset_t) ifps); + } + } + else { + /* + * Valid state. Allocate the fp state if there is none. + */ + register struct i386_fp_save *user_fp_state; + register 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 *) zalloc(ifps_zone); + goto Retry; + } + ifps = new_ifps; + new_ifps = 0; + pcb->ims.ifps = ifps; + } + + /* + * Ensure that reserved parts of the environment are 0. + */ + bzero((char *)&ifps->fp_save_state, sizeof(struct i386_fp_save)); + + 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; + +#if FPE + if (fp_kind == FP_SOFT) { + /* + * The emulator stores the registers by physical + * register number, not from top-of-stack. + * Shuffle the registers into the correct order. + */ + register char *src; /* user regs */ + register char *dst; /* kernel regs */ + int i; + + src = (char *)user_fp_regs; + dst = (char *)&ifps->fp_regs; + i = (ifps->fp_save_state.fp_status & FPS_TOS) + >> FPS_TOS_SHIFT; /* physical register + for st(0) */ + if (i == 0) + bcopy(src, dst, 8 * 10); + else { + bcopy(src, + dst + 10 * i, + 10 * (8 - i)); + bcopy(src + 10 * (8 - i), + dst, + 10 * i); + } + } + else + ifps->fp_regs = *user_fp_regs; +#else /* no FPE */ + ifps->fp_regs = *user_fp_regs; +#endif /* FPE */ + + simple_unlock(&pcb->lock); + if (new_ifps != 0) + zfree(ifps_zone, (vm_offset_t) 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(thread, state) + thread_t thread; + register struct i386_float_state *state; +{ + register pcb_t pcb = thread->pcb; + register 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); + bzero((char *)state, sizeof(struct i386_float_state)); + return KERN_SUCCESS; + } + + /* Make sure we`ve got the latest fp state info */ + clear_ts(); + fp_save(thread); + clear_fpu(); + + state->fpkind = fp_kind; + state->exc_status = 0; + + { + register struct i386_fp_save *user_fp_state; + register 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. + */ + bzero((char *)user_fp_state, sizeof(struct i386_fp_save)); + + 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; + +#if FPE + if (fp_kind == FP_SOFT) { + /* + * The emulator stores the registers by physical + * register number, not from top-of-stack. + * Shuffle the registers into the correct order. + */ + register char *src; /* kernel regs */ + register char *dst; /* user regs */ + int i; + + src = (char *)&ifps->fp_regs; + dst = (char *)user_fp_regs; + i = (ifps->fp_save_state.fp_status & FPS_TOS) + >> FPS_TOS_SHIFT; /* physical register + for st(0) */ + if (i == 0) + bcopy(src, dst, 8 * 10); + else { + bcopy(src + 10 * i, + dst, + 10 * (8 - i)); + bcopy(src, + dst + 10 * (8 - i), + 10 * i); + } + } + else + *user_fp_regs = ifps->fp_regs; +#else /* no FPE */ + *user_fp_regs = ifps->fp_regs; +#endif /* FPE */ + } + simple_unlock(&pcb->lock); + + return KERN_SUCCESS; +} + +/* + * Initialize FPU. + * + * Raise exceptions for: + * invalid operation + * divide by zero + * overflow + * + * Use 53-bit precision. + */ +void fpinit() +{ + unsigned short control; + +ASSERT_IPL(SPL0); + clear_ts(); + fninit(); + fnstcw(&control); + control &= ~(FPC_PC|FPC_RC); /* Clear precision & rounding control */ + control |= (FPC_PC_53 | /* Set precision */ + FPC_RC_RN | /* round-to-nearest */ + FPC_ZE | /* Suppress zero-divide */ + FPC_OE | /* and overflow */ + FPC_UE | /* underflow */ + FPC_IE | /* Allow NaNQs and +-INF */ + FPC_DE | /* Allow denorms as operands */ + FPC_PE); /* No trap for precision loss */ + fldcw(control); +} + +/* + * Coprocessor not present. + */ +fpnoextflt() +{ + /* + * 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. + */ +fpextovrflt() +{ + register thread_t thread = current_thread(); + register pcb_t pcb; + register 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) + zfree(ifps_zone, (vm_offset_t) ifps); + + /* + * Raise exception. + */ + i386_exception(EXC_BAD_ACCESS, VM_PROT_READ|VM_PROT_EXECUTE, 0); + /*NOTREACHED*/ +} + +/* + * FPU error. Called by AST. + */ +fpexterrflt() +{ + register 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, + thread->pcb->ims.ifps->fp_save_state.fp_status); + /*NOTREACHED*/ +} + +/* + * 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. + */ +fp_save(thread) + register thread_t thread; +{ + register pcb_t pcb = thread->pcb; + register struct i386_fpsave_state *ifps = pcb->ims.ifps; + + if (ifps != 0 && !ifps->fp_valid) { + /* registers are in FPU */ + ifps->fp_valid = TRUE; + fnsave(&ifps->fp_save_state); + } +} + +/* + * Restore FPU state from PCB. + * + * Locking not needed; always called on the current thread. + */ +fp_load(thread) + register thread_t thread; +{ + register pcb_t pcb = thread->pcb; + register struct i386_fpsave_state *ifps; + +ASSERT_IPL(SPL0); + ifps = pcb->ims.ifps; + if (ifps == 0) { + ifps = (struct i386_fpsave_state *) zalloc(ifps_zone); + bzero(ifps, sizeof *ifps); + pcb->ims.ifps = ifps; + fpinit(); +#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, + thread->pcb->ims.ifps->fp_save_state.fp_status); + /*NOTREACHED*/ +#endif + } else { + frstor(ifps->fp_save_state); + } + ifps->fp_valid = FALSE; /* in FPU */ +} + +/* + * Allocate and initialize FP state for current thread. + * Don't load state. + * + * Locking not needed; always called on the current thread. + */ +void +fp_state_alloc() +{ + pcb_t pcb = current_thread()->pcb; + struct i386_fpsave_state *ifps; + + ifps = (struct i386_fpsave_state *)zalloc(ifps_zone); + bzero(ifps, sizeof *ifps); + pcb->ims.ifps = ifps; + + ifps->fp_valid = TRUE; + ifps->fp_save_state.fp_control = (0x037f + & ~(FPC_IM|FPC_ZM|FPC_OM|FPC_PC)) + | (FPC_PC_53|FPC_IC_AFF); + ifps->fp_save_state.fp_status = 0; + ifps->fp_save_state.fp_tag = 0xffff; /* all empty */ +} + +#if AT386 || PS2 +/* + * Handle a coprocessor error interrupt on the AT386. + * This comes in on line 5 of the slave PIC at SPL1. + */ +fpintr() +{ + spl_t s; + thread_t thread = current_thread(); + +ASSERT_IPL(SPL1); + /* + * Turn off the extended 'busy' line. + */ + outb(0xf0, 0); + + /* + * Save the FPU context to the thread using it. + */ +#if NCPUS == 1 + if (fp_thread == THREAD_NULL) { + printf("fpintr: FPU not belonging to anyone!\n"); + clear_ts(); + fninit(); + clear_fpu(); + return; + } + + 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; + } + if (fp_intr_thread != THREAD_NULL) + panic("fp_intr: already caught intr"); + fp_intr_thread = thread; +#endif /* NCPUS == 1 */ + + clear_ts(); + fp_save(thread); + fninit(); + clear_fpu(); + + /* + * 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 || PS2 */ diff --git a/i386/i386/fpu.h b/i386/i386/fpu.h new file mode 100644 index 00000000..7db1a8ef --- /dev/null +++ b/i386/i386/fpu.h @@ -0,0 +1,130 @@ +/* + * 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 <cpus.h> +#include <fpe.h> +#include <i386/proc_reg.h> +#include <i386/thread.h> + +/* + * FPU instructions. + */ +#define fninit() \ + asm volatile("fninit") + +#define fnstcw(control) \ + asm("fnstcw %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 fwait() \ + asm("fwait"); + +/* + * If floating-point instructions are emulated, + * we must load the floating-point register selector + * when switching to a new thread. + */ +#if FPE +extern void disable_fpe(); +extern void enable_fpe(); + +#define fpu_save_context(thread) \ + { \ + if (fp_kind == FP_SOFT) \ + disable_fpe(); \ + else \ + set_ts(); \ + } + +#define fpu_load_context(pcb) \ + { \ + register struct i386_fpsave_state *ifps; \ + if (fp_kind == FP_SOFT && (ifps = pcb->ims.ifps) != 0) \ + enable_fpe(ifps); \ + } + +#else /* no FPE */ + +#define fpu_load_context(pcb) + +/* + * 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) \ + { \ + register struct i386_fpsave_state *ifps; \ + ifps = (thread)->pcb->ims.ifps; \ + if (ifps != 0 && !ifps->fp_valid) { \ + /* registers are in FPU - save to memory */ \ + ifps->fp_valid = TRUE; \ + fnsave(&ifps->fp_save_state); \ + set_ts(); \ + } \ + } + +#else /* NCPUS == 1 */ +#define fpu_save_context(thread) \ + { \ + set_ts(); \ + } + +#endif /* NCPUS == 1 */ + +#endif /* no FPE */ + +extern int fp_kind; + +#endif /* _I386_FPU_H_ */ diff --git a/i386/i386/gdt.c b/i386/i386/gdt.c new file mode 100644 index 00000000..d8551110 --- /dev/null +++ b/i386/i386/gdt.c @@ -0,0 +1,88 @@ +/* + * 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 <platforms.h> + +#include "vm_param.h" +#include "seg.h" +#include "gdt.h" + +#if PS2 +extern unsigned long abios_int_return; +extern unsigned long abios_th_return; +extern char intstack[]; +#endif /* PS2 */ + +struct real_descriptor gdt[GDTSZ]; + +void +gdt_init() +{ + /* Initialize the kernel code and data segment descriptors. */ + fill_gdt_descriptor(KERNEL_CS, + LINEAR_MIN_KERNEL_ADDRESS, + LINEAR_MAX_KERNEL_ADDRESS - LINEAR_MIN_KERNEL_ADDRESS - 1, + ACC_PL_K|ACC_CODE_R, SZ_32); + fill_gdt_descriptor(KERNEL_DS, + LINEAR_MIN_KERNEL_ADDRESS, + LINEAR_MAX_KERNEL_ADDRESS - LINEAR_MIN_KERNEL_ADDRESS - 1, + ACC_PL_K|ACC_DATA_W, SZ_32); + + /* Load the new GDT. */ + { + struct pseudo_descriptor pdesc; + + pdesc.limit = sizeof(gdt)-1; + pdesc.linear_base = kvtolin(&gdt); + lgdt(&pdesc); + } + + /* 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. */ + asm volatile(" + ljmp %0,$1f + 1: + movw %w2,%%ds + movw %w2,%%es + movw %w2,%%fs + movw %w2,%%gs + + movw %w1,%%ds + movw %w1,%%es + movw %w1,%%ss + " : : "i" (KERNEL_CS), "r" (KERNEL_DS), "r" (0)); +} + diff --git a/i386/i386/gdt.h b/i386/i386/gdt.h new file mode 100644 index 00000000..10d47624 --- /dev/null +++ b/i386/i386/gdt.h @@ -0,0 +1,72 @@ +/* + * 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 code */ +#define KERNEL_DS 0x10 /* kernel data */ +#define KERNEL_LDT 0x18 /* master LDT */ +#define KERNEL_TSS 0x20 /* master TSS (uniprocessor) */ +#define USER_LDT 0x28 /* place for per-thread LDT */ +#define USER_TSS 0x30 /* place for per-thread TSS + that holds IO bitmap */ +#define FPE_CS 0x38 /* floating-point emulator code */ +#define USER_FPREGS 0x40 /* user-mode access to saved + floating-point registers */ + +#ifdef PS2 +#define ABIOS_INT_RET 0x48 /* 16 bit return selector for ABIOS */ +#define ABIOS_TH_RET 0x50 /* 16 bit return selector for ABIOS */ +#define ABIOS_INT_SS 0x58 /* ABIOS interrupt stack selector */ +#define ABIOS_TH_SS 0x60 /* ABIOS current stack selector */ +#define ABIOS_FIRST_AVAIL_SEL \ + 0x68 /* first selector for ABIOS + to allocate */ +#define GDTSZ 0x300 /* size of gdt table */ +#else /* PS2 */ +#define GDTSZ 11 +#endif /* PS2 */ + + +extern struct real_descriptor gdt[GDTSZ]; + +/* Fill a segment descriptor in the GDT. */ +#define fill_gdt_descriptor(segment, base, limit, access, sizebits) \ + fill_descriptor(&gdt[segment/8], base, limit, access, sizebits) + +#endif _I386_GDT_ diff --git a/i386/i386/hardclock.c b/i386/i386/hardclock.c new file mode 100644 index 00000000..b4804da3 --- /dev/null +++ b/i386/i386/hardclock.c @@ -0,0 +1,99 @@ +/* + * 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 <platforms.h> + +#include <kern/time_out.h> +#include <i386/thread.h> + +#ifdef SYMMETRY +#include <sqt/intctl.h> +#endif +#if defined(AT386) || defined(iPSC386) +#include <i386/ipl.h> +#endif +#ifdef PS2 +#include <i386/pic.h> +#include <i386/pio.h> +#endif PS2 + +extern void clock_interrupt(); +extern char return_to_iret[]; + +void +#ifdef PS2 +hardclock(iunit, ivect, old_ipl, ret_addr, regs) + int iunit; /* 'unit' number */ + int ivect; /* interrupt number */ +#else /* PS2 */ +hardclock(iunit, old_ipl, ret_addr, regs) + int iunit; /* 'unit' number */ + int old_ipl; /* old interrupt level */ +#endif /* PS2 */ + 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(PS2) || defined(LINUX_DEV) + FALSE /* ignore SPL0 */ +#else /* PS2 */ + old_ipl == SPL0 /* base priority */ +#endif /* PS2 */ + ); + else + /* + * Interrupt from interrupt stack. + */ + clock_interrupt(tick, /* usec per tick */ + FALSE, /* kernel mode */ + FALSE); /* not SPL0 */ + +#ifdef LINUX_DEV + linux_timer_intr(); +#endif + +#ifdef PS2 + /* + * Reset the clock interrupt line. + */ + outb(0x61, inb(0x61) | 0x80); +#endif /* PS2 */ +} diff --git a/i386/i386/i386asm.sym b/i386/i386/i386asm.sym new file mode 100644 index 00000000..e38a1bd6 --- /dev/null +++ b/i386/i386/i386asm.sym @@ -0,0 +1,139 @@ +/* + * 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. + */ + +#include <platforms.h> +#include <cpus.h> +#include <mach_kdb.h> +#include <stat_time.h> + +/* + * 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 "vm_param.h" +#include "seg.h" +#include "tss.h" +#include "idt.h" +#include "gdt.h" +#include "ldt.h" +#include "mp_desc.h" + + +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 +expr &STACK_IKS(0)->k_esi KSS_ESI +expr &STACK_IKS(0)->k_edi KSS_EDI +expr &STACK_IKS(0)->k_eip KSS_EIP +size i386_kernel_state iks + +size i386_exception_link iel + +offset i386_saved_state r cs +offset i386_saved_state r uesp +offset i386_saved_state r eax +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_interrupt_state i eip +offset i386_interrupt_state i cs +offset i386_interrupt_state i efl + +offset i386_tss tss esp0 +offset i386_tss tss ss0 + +expr I386_PGBYTES NBPG +expr VM_MIN_ADDRESS +expr VM_MAX_ADDRESS +expr VM_MIN_KERNEL_ADDRESS KERNELBASE +expr KERNEL_STACK_SIZE + +expr PDESHIFT +expr PTESHIFT +expr PTEMASK + +expr INTEL_PTE_PFN PTE_PFN +expr INTEL_PTE_VALID PTE_V +expr INTEL_PTE_WRITE PTE_W +expr ~INTEL_PTE_VALID PTE_INVALID +expr NPTES PTES_PER_PAGE +expr INTEL_PTE_VALID|INTEL_PTE_WRITE INTEL_PTE_KERNEL + +expr IDTSZ +expr GDTSZ +expr LDTSZ + +expr KERNEL_CS +expr KERNEL_DS +expr KERNEL_TSS +expr KERNEL_LDT + +expr (VM_MIN_KERNEL_ADDRESS>>PDESHIFT)*sizeof(pt_entry_t) KERNELBASEPDE + +#if MACH_KDB +expr RB_KDB +#endif MACH_KDB + +#if NCPUS > 1 +offset mp_desc_table mp gdt +offset mp_desc_table mp idt +#endif NCPUS > 1 +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 + diff --git a/i386/i386/idt-gen.h b/i386/i386/idt-gen.h new file mode 100644 index 00000000..4663593e --- /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(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 00000000..56688517 --- /dev/null +++ b/i386/i386/idt.c @@ -0,0 +1,59 @@ +/* + * 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 "vm_param.h" +#include "seg.h" +#include "idt.h" +#include "gdt.h" + +struct real_gate idt[IDTSZ]; + +struct idt_init_entry +{ + unsigned entrypoint; + unsigned short vector; + unsigned short type; +}; +extern struct idt_init_entry idt_inittab[]; + +void idt_init() +{ + struct idt_init_entry *iie = idt_inittab; + + /* Initialize the exception vectors from the idt_inittab. */ + while (iie->entrypoint) + { + fill_idt_gate(iie->vector, iie->entrypoint, KERNEL_CS, iie->type, 0); + iie++; + } + + /* Load the IDT pointer into the processor. */ + { + struct pseudo_descriptor pdesc; + + pdesc.limit = sizeof(idt)-1; + pdesc.linear_base = kvtolin(&idt); + lidt(&pdesc); + } +} + diff --git a/i386/i386/idt_inittab.S b/i386/i386/idt_inittab.S new file mode 100644 index 00000000..77185681 --- /dev/null +++ b/i386/i386/idt_inittab.S @@ -0,0 +1,121 @@ +/* + * 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 "seg.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. + */ +#define IDT_ENTRY(n,entry,type) \ + .data 2 ;\ + .long entry ;\ + .word n ;\ + .word type ;\ + .text + +/* + * 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) +EXCEPTION(0x0f,t_trap_0f) +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 + diff --git a/i386/i386/io_emulate.c b/i386/i386/io_emulate.c new file mode 100644 index 00000000..1bc5a75b --- /dev/null +++ b/i386/i386/io_emulate.c @@ -0,0 +1,108 @@ +/* + * 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 <platforms.h> + +#include <mach/boolean.h> +#include <mach/port.h> +#include <kern/thread.h> +#include <kern/task.h> + +#include <ipc/ipc_port.h> +#include <ipc/ipc_space.h> +#include <ipc/ipc_right.h> +#include <ipc/ipc_object.h> +#include <ipc/ipc_entry.h> + +#include <device/dev_hdr.h> + +#include <i386/thread.h> +#include <i386/io_port.h> +#include <i386/io_emulate.h> + +extern ipc_port_t iopl_device_port; +extern mach_device_t iopl_device; + +int +emulate_io(regs, opcode, io_port) + struct i386_saved_state *regs; + int opcode; + int io_port; +{ + thread_t thread = current_thread(); + +#if AT386 + if (iopl_emulate(regs, opcode, io_port)) + return EM_IO_DONE; +#endif /* AT386 */ + + if (iopb_check_mapping(thread, iopl_device)) + return EM_IO_ERROR; + + /* + * Check for send rights to the IOPL device port. + */ + if (iopl_device_port == IP_NULL) + return EM_IO_ERROR; + { + ipc_space_t space = current_space(); + mach_port_t name; + ipc_entry_t entry; + boolean_t has_rights = FALSE; + + is_write_lock(space); + assert(space->is_active); + + if (ipc_right_reverse(space, (ipc_object_t) iopl_device_port, + &name, &entry)) { + /* iopl_device_port is locked and active */ + if (entry->ie_bits & MACH_PORT_TYPE_SEND) + has_rights = TRUE; + ip_unlock(iopl_device_port); + } + + is_write_unlock(space); + if (!has_rights) { + return EM_IO_ERROR; + } + } + + + /* + * Map the IOPL port set into the thread. + */ + + if (i386_io_port_add(thread, iopl_device) + != KERN_SUCCESS) + return EM_IO_ERROR; + + /* + * Make the thread use its IO_TSS to get the IO permissions; + * it may not have had one before this. + */ + switch_ktss(thread->pcb); + + return EM_IO_RETRY; +} diff --git a/i386/i386/io_emulate.h b/i386/i386/io_emulate.h new file mode 100644 index 00000000..de0d12df --- /dev/null +++ b/i386/i386/io_emulate.h @@ -0,0 +1,43 @@ +/* + * 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_IO_EMULATE_H_ +#define _I386_IO_EMULATE_H_ + +/* + * Return codes from IO emulation. + */ +extern int emulate_io(/* + struct i386_saved_state *regs, + int opcode, + int io_port + */); + +#define EM_IO_DONE 0 /* IO instruction executed, proceed */ +#define EM_IO_RETRY 1 /* IO port mapped, retry instruction */ +#define EM_IO_ERROR 2 /* IO port not mapped */ + +#endif /* _I386_IO_EMULATE_H_ */ diff --git a/i386/i386/io_map.c b/i386/i386/io_map.c new file mode 100644 index 00000000..256a9a08 --- /dev/null +++ b/i386/i386/io_map.c @@ -0,0 +1,58 @@ +/* + * 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/vm_param.h> +#include <vm/vm_kern.h> +#include <vm/vm_map.h> + +extern vm_offset_t kernel_virtual_start; + +/* + * Allocate and map memory for devices that may need to be mapped before + * Mach VM is running. + */ +vm_offset_t +io_map(phys_addr, size) + vm_offset_t phys_addr; + vm_size_t size; +{ + vm_offset_t start; + + if (kernel_map == VM_MAP_NULL) { + /* + * VM is not initialized. Grab memory. + */ + start = kernel_virtual_start; + kernel_virtual_start += round_page(size); + printf("stealing kernel virtual addresses %08x-%08x\n", start, kernel_virtual_start); + } + else { + (void) kmem_alloc_pageable(kernel_map, &start, round_page(size)); + } + (void) pmap_map_bd(start, phys_addr, phys_addr + round_page(size), + VM_PROT_READ|VM_PROT_WRITE); + return (start); +} diff --git a/i386/i386/io_port.h b/i386/i386/io_port.h new file mode 100644 index 00000000..62022b78 --- /dev/null +++ b/i386/i386/io_port.h @@ -0,0 +1,43 @@ +/* + * 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_IO_PORT_H_ +#define _I386_IO_PORT_H_ +/* + * IO register definitions. + */ +typedef unsigned short io_reg_t; + +#define IO_REG_NULL (0x00ff) /* reserved */ + +/* + * Allocate and destroy io port sets for users to map into + * threads. + */ +extern void io_port_create(/* device_t, io_reg_t * */); +extern void io_port_destroy(/* device_t */); + +#endif /* _I386_IO_PORT_H_ */ diff --git a/i386/i386/iopb.c b/i386/i386/iopb.c new file mode 100644 index 00000000..d2addac0 --- /dev/null +++ b/i386/i386/iopb.c @@ -0,0 +1,615 @@ +/* + * 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. + */ +/* + * Code to manipulate IO permission bitmaps. + */ + +#include <mach/boolean.h> +#include <mach/kern_return.h> + +#include <ipc/ipc_port.h> + +#include <kern/kalloc.h> +#include <kern/lock.h> +#include <kern/queue.h> +#include <kern/thread.h> + +#include <device/dev_hdr.h> + +#include "io_port.h" +#include "iopb.h" +#include "seg.h" +#include "gdt.h" + +/* + * A set of ports for an IO device. + */ +struct io_port { + mach_device_t device; /* Mach device */ + queue_chain_t dev_list; /* link in device list */ + queue_chain_t io_use_list; /* List of threads that use it */ + io_reg_t *io_port_list; /* list of IO ports that use it */ + /* list ends with IO_REG_NULL */ +}; +typedef struct io_port *io_port_t; + +/* + * Lookup table for device -> io_port mapping + * (a linked list - I don't expect too many) + */ +queue_head_t device_to_io_port_list; + +/* + * Cross-reference: + * all threads that have IO ports mapped + * all IO ports that have threads mapped + */ +struct io_use { + queue_chain_t psq; /* Links from port set */ + queue_chain_t tsq; /* links from tss */ + io_port_t ps; /* Port set */ + iopb_tss_t ts; /* Task segment */ +}; +typedef struct io_use *io_use_t; + +/* + * Big lock for the whole mess. + */ +decl_simple_lock_data(, iopb_lock) + +/* + * Initialize the package. + */ +void +iopb_init(void) +{ + queue_init(&device_to_io_port_list); + simple_lock_init(&iopb_lock); +} + +/* + * Initialize bitmap (set all bits to OFF == 1) + */ +void +io_bitmap_init( + isa_iopb bp, + boolean_t on_off) +{ + register unsigned char *b = bp; + register int s; + unsigned char c; + + /* + * Disallow access to ports 0x00 .. 0xff + */ + for (s = 0; s < (0xff+1)/8; s++) { + *b++ = ~0; /* no access */ + } + + if (on_off) + c = 0; + else + c = ~0; + + for (; s < sizeof(isa_iopb); s++) { + *b++ = c; + } +} + +/* + * Set selected bits in bitmap to ON == 0 + */ +void +io_bitmap_set( + isa_iopb bp, + io_reg_t *bit_list) +{ + io_reg_t io_bit; + + while ((io_bit = *bit_list++) != IO_REG_NULL) { + bp[io_bit>>3] &= ~(1 << (io_bit & 0x7)); + } +} + +/* + * Set selected bits in bitmap to OFF == 1 + */ +void +io_bitmap_clear( + isa_iopb bp, + io_reg_t *bit_list) +{ + io_reg_t io_bit; + + while ((io_bit = *bit_list++) != IO_REG_NULL) { + bp[io_bit>>3] |= (1 << (io_bit & 0x7)); + } +} + +/* + * Lookup an io-port set by device + */ +io_port_t +device_to_io_port_lookup( + mach_device_t device) +{ + register io_port_t io_port; + + queue_iterate(&device_to_io_port_list, io_port, io_port_t, dev_list) { + if (io_port->device == device) { + return io_port; + } + } + return 0; +} + +/* + * [exported] + * Create an io_port set + */ +void +io_port_create( + mach_device_t device, + io_reg_t *io_port_list) +{ + register io_port_t io_port; + + io_port = (io_port_t) kalloc(sizeof(struct io_port)); + + simple_lock(&iopb_lock); + if (device_to_io_port_lookup(device) != 0) { + simple_unlock(&iopb_lock); + kfree((vm_offset_t) io_port, sizeof(struct io_port)); + return; + } + + io_port->device = device; + queue_init(&io_port->io_use_list); + io_port->io_port_list = io_port_list; + + /* + * Enter in lookup list. + */ + queue_enter(&device_to_io_port_list, io_port, io_port_t, dev_list); + + simple_unlock(&iopb_lock); +} + +/* + * [exported] + * Destroy an io port set, removing any IO mappings. + */ +void +io_port_destroy( + mach_device_t device) +{ + io_port_t io_port; + io_use_t iu; + + simple_lock(&iopb_lock); + io_port = device_to_io_port_lookup(device); + if (io_port == 0) { + simple_unlock(&iopb_lock); + return; + } + + queue_iterate(&io_port->io_use_list, iu, io_use_t, psq) { + iopb_tss_t io_tss; + io_tss = iu->ts; + io_bitmap_clear(io_tss->bitmap, io_port->io_port_list); + queue_remove(&io_tss->io_port_list, iu, io_use_t, tsq); + } + queue_remove(&device_to_io_port_list, io_port, io_port_t, dev_list); + simple_unlock(&iopb_lock); + + while (!queue_empty(&io_port->io_use_list)) { + iu = (io_use_t) queue_first(&io_port->io_use_list); + queue_remove(&io_port->io_use_list, iu, io_use_t, psq); + kfree((vm_offset_t)iu, sizeof(struct io_use)); + } + + kfree((vm_offset_t)io_port, sizeof(struct io_port)); +} + +/* + * Initialize an IO TSS. + */ +void +io_tss_init( + iopb_tss_t io_tss, + boolean_t access_all) /* allow access or not */ +{ + vm_offset_t addr = (vm_offset_t) io_tss; + vm_size_t size = (char *)&io_tss->barrier - (char *)io_tss; + + bzero(&io_tss->tss, sizeof(struct i386_tss)); + io_tss->tss.io_bit_map_offset + = (char *)&io_tss->bitmap - (char *)io_tss; + io_tss->tss.ss0 = KERNEL_DS; + io_bitmap_init(io_tss->bitmap, access_all); + io_tss->barrier = ~0; + queue_init(&io_tss->io_port_list); + io_tss->iopb_desc[0] = ((size-1) & 0xffff) + | ((addr & 0xffff) << 16); + io_tss->iopb_desc[1] = ((addr & 0x00ff0000) >> 16) + | ((ACC_TSS|ACC_PL_K|ACC_P) << 8) + | ((size-1) & 0x000f0000) + | (addr & 0xff000000); +} + +/* + * [exported] + * Create an IOPB_TSS + */ +iopb_tss_t +iopb_create(void) +{ + register iopb_tss_t ts; + + ts = (iopb_tss_t) kalloc(sizeof (struct iopb_tss)); + io_tss_init(ts, TRUE); /* XXX */ + return ts; +} + +/* + * [exported] + * Destroy an IOPB_TSS + */ +void +iopb_destroy( + iopb_tss_t io_tss) +{ + io_use_t iu; + io_port_t io_port; + + simple_lock(&iopb_lock); + + queue_iterate(&io_tss->io_port_list, iu, io_use_t, tsq) { + io_port = iu->ps; + /* skip bitmap clear - entire bitmap will vanish */ + queue_remove(&io_port->io_use_list, iu, io_use_t, psq); + } + + simple_unlock(&iopb_lock); + + while (!queue_empty(&io_tss->io_port_list)) { + iu = (io_use_t) queue_first(&io_tss->io_port_list); + queue_remove(&io_tss->io_port_list, iu, io_use_t, tsq); + kfree((vm_offset_t)iu, sizeof(struct io_use)); + } + + kfree((vm_offset_t)io_tss, sizeof(struct iopb_tss)); +} + +/* + * Add an IO mapping to a thread. + */ +kern_return_t +i386_io_port_add( + thread_t thread, + mach_device_t device) +{ + pcb_t pcb; + iopb_tss_t io_tss, new_io_tss; + io_port_t io_port; + io_use_t iu, old_iu; + + if (thread == THREAD_NULL + || device == DEVICE_NULL) + return KERN_INVALID_ARGUMENT; + + pcb = thread->pcb; + + new_io_tss = 0; + iu = (io_use_t) kalloc(sizeof(struct io_use)); + + Retry: + simple_lock(&iopb_lock); + + /* find the io_port_t for the device */ + io_port = device_to_io_port_lookup(device); + if (io_port == 0) { + /* + * Device does not have IO ports available. + */ + simple_unlock(&iopb_lock); + if (new_io_tss) + kfree((vm_offset_t)new_io_tss, sizeof(struct iopb_tss)); + kfree((vm_offset_t) iu, sizeof(struct io_use)); + return KERN_INVALID_ARGUMENT; + } + + /* Have the IO port. */ + + /* Make sure the thread has a TSS. */ + + simple_lock(&pcb->lock); + io_tss = pcb->ims.io_tss; + if (io_tss == 0) { + if (new_io_tss == 0) { + /* + * Allocate an IO-tss. + */ + simple_unlock(&pcb->lock); + simple_unlock(&iopb_lock); + + new_io_tss = (iopb_tss_t) kalloc(sizeof(struct iopb_tss)); + io_tss_init(new_io_tss, TRUE); /* XXX */ + + goto Retry; + } + io_tss = new_io_tss; + pcb->ims.io_tss = io_tss; + new_io_tss = 0; + } + + /* + * Have io_port and io_tss. + * See whether device is already mapped. + */ + queue_iterate(&io_tss->io_port_list, old_iu, io_use_t, tsq) { + if (old_iu->ps == io_port) { + /* + * Already mapped. + */ + simple_unlock(&pcb->lock); + simple_unlock(&iopb_lock); + + kfree((vm_offset_t)iu, sizeof(struct io_use)); + if (new_io_tss) + kfree((vm_offset_t)new_io_tss, sizeof(struct iopb_tss)); + return KERN_SUCCESS; + } + } + + /* + * Add mapping. + */ + iu->ps = io_port; + iu->ts = io_tss; + queue_enter(&io_port->io_use_list, iu, io_use_t, psq); + queue_enter(&io_tss->io_port_list, iu, io_use_t, tsq); + io_bitmap_set(io_tss->bitmap, io_port->io_port_list); + + simple_unlock(&pcb->lock); + simple_unlock(&iopb_lock); + + if (new_io_tss) + kfree((vm_offset_t)new_io_tss, sizeof(struct iopb_tss)); + return KERN_SUCCESS; + +} + +/* + * Remove an IO mapping from a thread. + */ +kern_return_t +i386_io_port_remove(thread, device) + thread_t thread; + mach_device_t device; +{ + pcb_t pcb; + iopb_tss_t io_tss; + io_port_t io_port; + io_use_t iu; + + if (thread == THREAD_NULL + || device == DEVICE_NULL) + return KERN_INVALID_ARGUMENT; + + pcb = thread->pcb; + + simple_lock(&iopb_lock); + + /* find the io_port_t for the device */ + + io_port = device_to_io_port_lookup(device); + if (io_port == 0) { + /* + * Device does not have IO ports available. + */ + simple_unlock(&iopb_lock); + return KERN_INVALID_ARGUMENT; + } + + simple_lock(&pcb->lock); + io_tss = pcb->ims.io_tss; + if (io_tss == 0) { + simple_unlock(&pcb->lock); + simple_unlock(&iopb_lock); + return KERN_INVALID_ARGUMENT; /* not mapped */ + } + + /* + * Find the mapping. + */ + queue_iterate(&io_tss->io_port_list, iu, io_use_t, tsq) { + if (iu->ps == io_port) { + /* + * Found mapping. Remove it. + */ + io_bitmap_clear(io_tss->bitmap, io_port->io_port_list); + + queue_remove(&io_port->io_use_list, iu, io_use_t, psq); + queue_remove(&io_tss->io_port_list, iu, io_use_t, tsq); + + simple_unlock(&pcb->lock); + simple_unlock(&iopb_lock); + + kfree((vm_offset_t)iu, sizeof(struct io_use)); + + return KERN_SUCCESS; + } + } + + /* + * No mapping. + */ + return KERN_INVALID_ARGUMENT; +} + +/* + * Return the IO ports mapped into a thread. + */ +extern ipc_port_t mach_convert_device_to_port(/* device_t */); + +kern_return_t +i386_io_port_list(thread, list, list_count) + thread_t thread; + mach_device_t **list; + unsigned int *list_count; +{ + register pcb_t pcb; + register iopb_tss_t io_tss; + unsigned int count, alloc_count; + mach_device_t *devices; + vm_size_t size_needed, size; + vm_offset_t addr; + int i; + + if (thread == THREAD_NULL) + return KERN_INVALID_ARGUMENT; + + pcb = thread->pcb; + + alloc_count = 16; /* a guess */ + + do { + size_needed = alloc_count * sizeof(ipc_port_t); + if (size_needed <= size) + break; + + if (size != 0) + kfree(addr,size); + + assert(size_needed > 0); + size = size_needed; + + addr = kalloc(size); + if (addr == 0) + return KERN_RESOURCE_SHORTAGE; + + devices = (mach_device_t *)addr; + count = 0; + + simple_lock(&iopb_lock); + simple_lock(&pcb->lock); + io_tss = pcb->ims.io_tss; + if (io_tss != 0) { + register io_use_t iu; + + queue_iterate(&io_tss->io_port_list, iu, io_use_t, tsq) { + if (++count < alloc_count) { + *devices = iu->ps->device; + device_reference(*devices); + devices++; + } + } + } + simple_unlock(&pcb->lock); + simple_unlock(&iopb_lock); + } while (count > alloc_count); + + if (count == 0) { + /* + * No IO ports + */ + *list = 0; + *list_count = 0; + + if (size != 0) + kfree(addr, size); + } + else { + /* + * If we allocated too much, must copy. + */ + size_needed = count * sizeof(ipc_port_t); + if (size_needed < size) { + vm_offset_t new_addr; + + new_addr = kalloc(size_needed); + if (new_addr == 0) { + for (i = 0; i < count; i++) + device_deallocate(devices[i]); + kfree(addr, size); + return KERN_RESOURCE_SHORTAGE; + } + + bcopy((void *)addr, (void *)new_addr, size_needed); + kfree(addr, size); + devices = (mach_device_t *)new_addr; + } + + for (i = 0; i < count; i++) + ((ipc_port_t *)devices)[i] = + mach_convert_device_to_port(devices[i]); + } + *list = devices; + *list_count = count; + + return KERN_SUCCESS; +} + +/* + * Check whether an IO device is mapped to a particular thread. + * Used to support the 'iopl' device automatic mapping. + */ +boolean_t +iopb_check_mapping(thread, device) + thread_t thread; + mach_device_t device; +{ + pcb_t pcb; + io_port_t io_port; + io_use_t iu; + + pcb = thread->pcb; + + simple_lock(&iopb_lock); + + /* Find the io port for the device */ + + io_port = device_to_io_port_lookup(device); + if (io_port == 0) { + simple_unlock(&iopb_lock); + return FALSE; + } + + /* Look up the mapping in the device`s mapping list. */ + + queue_iterate(&io_port->io_use_list, iu, io_use_t, psq) { + if (iu->ts == pcb->ims.io_tss) { + /* + * Device is mapped. + */ + simple_unlock(&iopb_lock); + return TRUE; + } + } + simple_unlock(&iopb_lock); + return FALSE; +} diff --git a/i386/i386/iopb.h b/i386/i386/iopb.h new file mode 100644 index 00000000..0a3e5745 --- /dev/null +++ b/i386/i386/iopb.h @@ -0,0 +1,62 @@ +/* + * 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_IOPB_H_ +#define _I386_IOPB_H_ + +#include <i386/tss.h> +#include <kern/queue.h> + +/* + * IO permission bitmap. + * + * Allows only IO ports 0 .. 0x3ff: for ISA machines. + */ + +#define iopb_howmany(a,b) (((a)+(b)-1)/(b)) + +#define IOPB_MAX 0xffff /* ISA bus allows ports 0..3ff */ + /* but accelerator cards are funky */ +#define IOPB_BYTES (iopb_howmany(IOPB_MAX+1,8)) + +typedef unsigned char isa_iopb[IOPB_BYTES]; + +/* + * An IO permission map is a task segment with an IO permission bitmap. + */ + +struct iopb_tss { + struct i386_tss tss; /* task state segment */ + isa_iopb bitmap; /* bitmap of mapped IO ports */ + unsigned int barrier; /* bitmap barrier for CPU slop */ + queue_head_t io_port_list; /* list of mapped IO ports */ + int iopb_desc[2]; /* descriptor for this TSS */ +}; + +typedef struct iopb_tss *iopb_tss_t; + +#endif /* _I386_IOPB_H_ */ + diff --git a/i386/i386/ipl.h b/i386/i386/ipl.h new file mode 100644 index 00000000..06ee58a0 --- /dev/null +++ b/i386/i386/ipl.h @@ -0,0 +1,77 @@ +/* + * 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. +*/ + + +#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> +extern int (*ivect[])(); +extern int iunit[]; +extern int intpri[]; +#endif ASSEMBLER +#endif KERNEL diff --git a/i386/i386/ktss.c b/i386/i386/ktss.c new file mode 100644 index 00000000..836a6f6c --- /dev/null +++ b/i386/i386/ktss.c @@ -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. + */ +/* + * 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" + +void +ktss_init() +{ + /* XXX temporary exception stack */ + static exception_stack[1024]; + + /* Initialize the master TSS descriptor. */ + fill_gdt_descriptor(KERNEL_TSS, + kvtolin(&ktss), sizeof(ktss)+65536/8+1-1, + ACC_PL_K|ACC_TSS, 0); + + /* Initialize the master TSS. */ + ktss.ss0 = KERNEL_DS; + ktss.esp0 = (unsigned)(exception_stack+1024); + ktss.io_bit_map_offset = sizeof(ktss); + + /* Set the last byte in the I/O bitmap to all 1's. */ + ((unsigned char*)&ktss)[sizeof(ktss)+65536/8] = 0xff; + + /* Load the TSS. */ + ltr(KERNEL_TSS); +} + diff --git a/i386/i386/ktss.h b/i386/i386/ktss.h new file mode 100644 index 00000000..021f47fd --- /dev/null +++ b/i386/i386/ktss.h @@ -0,0 +1,30 @@ +/* + * 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 i386_tss ktss; + +#endif _I386_KTSS_ diff --git a/i386/i386/kttd_interface.c b/i386/i386/kttd_interface.c new file mode 100644 index 00000000..3f2f3900 --- /dev/null +++ b/i386/i386/kttd_interface.c @@ -0,0 +1,577 @@ +/* + * 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. + */ + +#include "mach_ttd.h" + +#if MACH_TTD + +#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 <sys/types.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) == 0) { + /* + * 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) { + /* + * 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 gs; + int fs; + int edi; + int esi; + int ebp; + int ebx; + struct i386_interrupt_state *is; +}; + +void +kttd_netentry(int_regs) + 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) { + /* + * 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 = int_regs->fs; + kttd_regs.gs = int_regs->gs; + + kttd_active++; + kttd_task_trap(-1, 0, (kttd_regs.cs & 0x3) != 0); + kttd_active--; + + if (kttd_regs.cs & 0x3) { + ((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; + int_regs->fs = kttd_regs.fs & 0xffff; + int_regs->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 00000000..8ac7de18 --- /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 00000000..a2b125ce --- /dev/null +++ b/i386/i386/ldt.c @@ -0,0 +1,64 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * 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/vm_types.h> + +#include "vm_param.h" +#include "seg.h" +#include "gdt.h" +#include "ldt.h" + +extern int syscall(); + +struct real_descriptor ldt[LDTSZ]; + +void +ldt_init() +{ + /* Initialize the master LDT descriptor in the GDT. */ + fill_gdt_descriptor(KERNEL_LDT, + kvtolin(&ldt), sizeof(ldt)-1, + ACC_PL_K|ACC_LDT, 0); + + /* Initialize the LDT descriptors. */ + fill_ldt_gate(USER_SCALL, + (vm_offset_t)&syscall, KERNEL_CS, + ACC_PL_U|ACC_CALL_GATE, 0); + fill_ldt_descriptor(USER_CS, + VM_MIN_ADDRESS, VM_MAX_ADDRESS-VM_MIN_ADDRESS, + /* XXX LINEAR_... */ + ACC_PL_U|ACC_CODE_R, SZ_32); + fill_ldt_descriptor(USER_DS, + VM_MIN_ADDRESS, VM_MAX_ADDRESS-VM_MIN_ADDRESS, + ACC_PL_U|ACC_DATA_W, SZ_32); + + /* Activate the LDT. */ + lldt(KERNEL_LDT); +} + diff --git a/i386/i386/ldt.h b/i386/i386/ldt.h new file mode 100644 index 00000000..da0b0af3 --- /dev/null +++ b/i386/i386/ldt.h @@ -0,0 +1,66 @@ +/* + * 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 */ +#define USER_CS 0x17 /* user code segment */ +#define USER_DS 0x1f /* user data segment */ + +#define LDTSZ 4 + + +#ifndef ASSEMBLER + +extern struct real_descriptor ldt[LDTSZ]; + +/* Fill a segment descriptor in the LDT. */ +#define fill_ldt_descriptor(selector, base, limit, access, sizebits) \ + fill_descriptor(&ldt[selector/8], base, limit, access, sizebits) + +#define fill_ldt_gate(selector, offset, dest_selector, access, word_count) \ + fill_gate((struct real_gate*)&ldt[selector/8], \ + offset, dest_selector, access, word_count) + +#endif !ASSEMBLER + +#endif _I386_LDT_ diff --git a/i386/i386/lock.h b/i386/i386/lock.h new file mode 100644 index 00000000..053a3ea6 --- /dev/null +++ b/i386/i386/lock.h @@ -0,0 +1,130 @@ +/* + * 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 + +/* + * 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) \ + ({ register int _old_val_; \ + asm volatile("xchgl %0, %2" \ + : "=r" (_old_val_) \ + : "0" (new_val), "m" (*(lock)) \ + ); \ + _old_val_; \ + }) + +#define simple_lock_init(l) \ + ((l)->lock_data = 0) + +#define simple_lock(l) \ + ({ \ + while(_simple_lock_xchg_(l, 1)) \ + while (*(volatile int *)&(l)->lock_data) \ + continue; \ + 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" (bit), "m" (*(volatile int *)(l))); \ + 0; \ + }) + +#define bit_unlock(bit, l) \ + ({ \ + asm volatile(" lock \n\ + btrl %0, %1" \ + : \ + : "r" (bit), "m" (*(volatile int *)(l))); \ + 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" (bit), "m" (*(l)) ); \ + 0; \ + }) + +#define i_bit_clear(bit, l) \ + ({ \ + asm volatile(" lock \n\ + btrl %0, %1" \ + : \ + : "r" (bit), "m" (*(l)) ); \ + 0; \ + }) + +#endif /* __GNUC__ */ + +extern void simple_lock_pause(); + +#endif NCPUS > 1 + + +#include_next "lock.h" + + +#endif /* _I386_LOCK_H_ */ diff --git a/i386/i386/locore.S b/i386/i386/locore.S new file mode 100644 index 00000000..8bc5d5e0 --- /dev/null +++ b/i386/i386/locore.S @@ -0,0 +1,1726 @@ +/* + * 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 <cpus.h> +#include <platforms.h> +#include <mach_kdb.h> +#include <mach_ttd.h> +#include <stat_time.h> + +#include <mach/machine/asm.h> +#include <mach/machine/eflags.h> +#include "proc_reg.h" +#include "trap.h" +#include "seg.h" +#include "ldt.h" +#include "i386asm.h" +#include "cpu_number.h" + +/* + * 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 _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 \ + cli /* block interrupts */ ;\ + movl VA_ETC,%ebx /* get timer value */ ;\ + movl CX(_current_tstamp,%edx),%ecx /* get old time stamp */;\ + movl %ebx,CX(_current_tstamp,%edx) /* set new time stamp */;\ + subl %ecx,%ebx /* elapsed = new-old */ ;\ + movl CX(_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_SYS_TIMER-TH_USER_TIMER),%ecx ;\ + /* switch to sys timer */;\ + movl %ecx,CX(_current_timer,%edx) /* make it current */ ;\ + sti /* 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 \ + cli /* block interrupts */ ;\ + movl VA_ETC,%ebx /* get timer value */ ;\ + movl CX(_current_tstamp,%edx),%ecx /* get old time stamp */;\ + movl %ebx,CX(_current_tstamp,%edx) /* set new time stamp */;\ + subl %ecx,%ebx /* elapsed = new-old */ ;\ + movl CX(_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_SYS_TIMER-TH_USER_TIMER),%ecx ;\ + /* switch to sys timer */;\ + movl %ecx,CX(_current_timer,%edx) /* make it current */ ;\ + sti /* 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(_current_tstamp,%edx),%ecx /* get old time stamp */;\ + movl %ebx,CX(_current_tstamp,%edx) /* set new time stamp */;\ + subl %ecx,%ebx /* elapsed = new-old */ ;\ + movl CX(_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_SYS_TIMER),%ecx ;\ + /* switch to user timer */;\ + movl %ecx,CX(_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(_current_tstamp,%edx),%ebx /* get old time stamp */;\ + movl %ecx,CX(_current_tstamp,%edx) /* set new time stamp */;\ + subl %ebx,%ecx /* elapsed = new-old */ ;\ + movl CX(_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(_kernel_timer,%edx),%ecx /* get interrupt timer*/;\ + movl %ecx,CX(_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(_current_tstamp,%edx),%ecx /* get old time stamp */;\ + movl %eax,CX(_current_tstamp,%edx) /* set new time stamp */;\ + subl %ecx,%eax /* elapsed = new-old */ ;\ + movl CX(_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(_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(_current_tstamp,%edx),%eax /* get old time stamp */ + movl %ecx,CX(_current_tstamp,%edx) /* set new time stamp */ + subl %ecx,%eax /* elapsed = new - old */ + movl CX(_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(_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(_current_tstamp,%edx) /* set initial time stamp */ + movl S_ARG0,%ecx /* get timer */ + movl %ecx,CX(_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 */ + testl $3,12(%esp) /* is trap from kernel mode? */ + jne 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 */ + testl $3,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 */ + movl %cr2,%eax /* get the faulting address */ + movl %eax,12(%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: + pushl %ds /* and the segment registers */ + pushl %es + pushl %fs + pushl %gs + + /* Note that we have to load the 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?) */ + mov %ss,%ax /* switch to kernel data segment */ + mov %ax,%ds /* (same as kernel stack segment) */ + mov %ax,%es + +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 */ + testb $3,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 */ + + cmpl EXT(int_stack_high),%esp /* on an interrupt stack? */ + jb 1f /* OK if so */ + + CPU_NUMBER(%edx) /* get CPU number */ + cmpl CX(EXT(kernel_stack),%edx),%esp + /* already on kernel stack? */ + ja 0f + cmpl CX(EXT(active_stacks),%edx),%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 */ + jmp *%eax /* goto continuation */ + + + +#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 +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) + +/* 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) + pushl %ecx /* save registers */ + pushl %edx + cld /* clear direction flag */ + + cmpl %ss:EXT(int_stack_high),%esp /* on an interrupt stack? */ + jb int_from_intstack /* if not: */ + + pushl %ds /* save segment registers */ + pushl %es + mov %ss,%dx /* switch to kernel segments */ + mov %dx,%ds + mov %dx,%es + + 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 + + call EXT(interrupt) /* call generic interrupt routine */ + + .globl EXT(return_to_iret) +LEXT(return_to_iret) /* ( label for kdb_kintr and hardclock) */ + + CPU_NUMBER(%edx) +#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 */ + testb $3,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 %es /* restore segment regs */ + pop %ds + pop %edx + pop %ecx + pop %eax + iret /* return to caller */ + +int_from_intstack: + call EXT(interrupt) /* call interrupt routine */ +_return_to_iret_i: /* ( label for kdb_kintr) */ + pop %edx /* must have been on kernel segs */ + pop %ecx + pop %eax /* no ASTs */ + iret + +/* + * Take an AST from an interrupt. + * On PCB stack. + * sp-> es -> edx + * ds -> ecx + * edx -> eax + * ecx -> trapno + * eax -> code + * eip + * cs + * efl + * esp + * ss + */ +ast_from_interrupt: + pop %es /* restore all registers ... */ + pop %ds + popl %edx + popl %ecx + popl %eax + pushl $0 /* zero code */ + pushl $0 /* zero trap number */ + pusha /* save general registers */ + push %ds /* save segment registers */ + push %es + push %fs + push %gs + mov %ss,%dx /* switch to kernel segments */ + mov %dx,%ds + mov %dx,%es + + 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 + * iunit + * [ PS2 - saved interrupt number ] + * saved SPL + * 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 + * iunit + * [ PS2 - saved interrupt number ] + * saved SPL + * return address == return_to_iret + * pointer to save area on old stack + * [ saved %ebx, if accurate timing ] + * + * old stack: 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 PS2 +#define RET_OFFSET 20 +#else /* not PS2 */ +#define RET_OFFSET 16 +#endif /* PS2 */ + +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 */ + jmp 0b + +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 + push %fs + push %gs + pushl %esp /* pass regs */ + call EXT(kdb_kentry) /* to kdb */ + addl $4,%esp /* pop parameters */ + pop %gs /* restore registers */ + pop %fs + popl %edi + 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 %ds /* save segment registers */ + push %es + push %fs + push %gs + pushl %esp /* pass regs, */ + pushl $0 /* code, */ + pushl $-1 /* type to kdb */ + call EXT(kdb_trap) + addl $12,%esp /* remove parameters */ + pop %gs /* restore segment registers */ + pop %fs + pop %es + pop %ds + 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 + push %fs + push %gs + pushl %esp /* pass regs */ + call _kttd_netentry /* to kdb */ + addl $4,%esp /* pop parameters */ + pop %gs /* restore registers */ + pop %fs + popl %edi + 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 %ds /* save segment registers */ + push %es + push %fs + push %gs + pushl %esp /* pass regs, */ + pushl $0 /* code, */ + pushl $-1 /* type to kdb */ + call _kttd_trap + addl $12,%esp /* remove parameters */ + pop %gs /* restore segment registers */ + pop %fs + pop %es + pop %ds + 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: + pushl %eax /* save system call number */ + pushl $0 /* clear trap number slot */ + + pusha /* save the general registers */ + pushl %ds /* and the segment registers */ + pushl %es + pushl %fs + pushl %gs + + mov %ss,%dx /* switch to kernel data segment */ + mov %dx,%ds + mov %dx,%es + +/* + * 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 CX(EXT(active_threads),%edx),%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 */ + 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 */ + +/**/ +/* + * Utility routines. + */ + +/* + * Copy from user address space. + * arg0: user address + * arg1: kernel address + * arg2: byte count + */ +ENTRY(copyin) +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,%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 to user address space. + * arg0: kernel address + * arg1: user address + * arg2: byte count + */ +ENTRY(copyout) +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,%edx /* get count */ + + movl $USER_DS,%eax /* use user data segment for accesses */ + mov %ax,%es + +/* + * Check whether user address space is writable + * before writing to it - hardware is broken. + * XXX only have to do this on 386's. + */ +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,4),%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,4),%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 */ + +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 */ + +/* XXX turn the following stubs into inline functions. */ + +/* + * FPU routines. + */ + +/* + * Initialize FPU. + */ +ENTRY(_fninit) + fninit + ret + +/* + * Read control word + */ +ENTRY(_fstcw) + pushl %eax /* get stack space */ + fstcw (%esp) + popl %eax + ret + +/* + * Set control word + */ +ENTRY(_fldcw) + fldcw 4(%esp) + ret + +/* + * Read status word + */ +ENTRY(_fnstsw) + xor %eax,%eax /* clear high 16 bits of eax */ + fnstsw %ax /* read FP status */ + ret + +/* + * Clear FPU exceptions + */ +ENTRY(_fnclex) + fnclex + ret + +/* + * Clear task-switched flag. + */ +ENTRY(_clts) + clts + ret + +/* + * Save complete FPU state. Save error for later. + */ +ENTRY(_fpsave) + movl 4(%esp),%eax /* get save area pointer */ + fnsave (%eax) /* save complete state, including */ + /* errors */ + ret + +/* + * Restore FPU state. + */ +ENTRY(_fprestore) + movl 4(%esp),%eax /* get save area pointer */ + frstor (%eax) /* restore complete state */ + ret + +/* + * Set cr3 + */ +ENTRY(set_cr3) + movl 4(%esp),%eax /* get new cr3 value */ + movl %eax,%cr3 /* load it */ + ret + +/* + * Read cr3 + */ +ENTRY(get_cr3) + movl %cr3,%eax + ret + +/* + * Flush TLB + */ +ENTRY(flush_tlb) + movl %cr3,%eax /* flush tlb by reloading CR3 */ + movl %eax,%cr3 /* with itself */ + ret + +/* + * Read cr2 + */ +ENTRY(get_cr2) + movl %cr2,%eax + ret + +/* + * Read ldtr + */ +ENTRY(get_ldt) + xorl %eax,%eax + sldt %ax + ret + +/* + * Set ldtr + */ +ENTRY(set_ldt) + lldt 4(%esp) + ret + +/* + * Read task register. + */ +ENTRY(get_tr) + xorl %eax,%eax + str %ax + ret + +/* + * Set task register. Also clears busy bit of task descriptor. + */ +ENTRY(set_tr) + movl S_ARG0,%eax /* get task segment number */ + subl $8,%esp /* push space for SGDT */ + sgdt 2(%esp) /* store GDT limit and base (linear) */ + movl 4(%esp),%edx /* address GDT */ + movb $(ACC_P|ACC_PL_K|ACC_TSS),5(%edx,%eax) + /* fix access byte in task descriptor */ + ltr %ax /* load task register */ + addl $8,%esp /* clear stack */ + ret /* and return */ + +/* + * Set task-switched flag. + */ +ENTRY(_setts) + movl %cr0,%eax /* get cr0 */ + orl $(CR0_TS),%eax /* or in TS bit */ + movl %eax,%cr0 /* set cr0 */ + ret + +/* + * void outb(unsigned char *io_port, + * unsigned char byte) + * + * Output a byte to an IO port. + */ +ENTRY(outb) + movl S_ARG0,%edx /* IO port address */ + movl S_ARG1,%eax /* data to output */ + outb %al,%dx /* send it out */ +#ifdef iPSC386 + mull %ecx /* Delay a little to make H/W happy */ +#endif iPSC386 + ret + +/* + * unsigned char inb(unsigned char *io_port) + * + * Input a byte from an IO port. + */ +ENTRY(inb) + movl S_ARG0,%edx /* IO port address */ + xor %eax,%eax /* clear high bits of register */ + inb %dx,%al /* get the byte */ +#ifdef iPSC386 +/ Do a long multiply to delay a little to make H/W happy. Must +/ save and restore EAX which is used to hold result of multiply + pushl %eax + mull %ecx + popl %eax +#endif iPSC386 + ret + +/* + * void outw(unsigned short *io_port, + * unsigned short word) + * + * Output a word to an IO port. + */ +ENTRY(outw) + movl S_ARG0,%edx /* IO port address */ + movl S_ARG1,%eax /* data to output */ + outw %ax,%dx /* send it out */ + ret + +/* + * unsigned short inw(unsigned short *io_port) + * + * Input a word from an IO port. + */ +ENTRY(inw) + movl S_ARG0,%edx /* IO port address */ + xor %eax,%eax /* clear high bits of register */ + inw %dx,%ax /* get the word */ + ret + +/* + * void outl(unsigned int *io_port, + * unsigned int byte) + * + * Output an int to an IO port. + */ +ENTRY(outl) + movl S_ARG0,%edx /* IO port address */ + movl S_ARG1,%eax /* data to output */ + outl %eax,%dx /* send it out */ + ret + +/* + * unsigned int inl(unsigned int *io_port) + * + * Input an int from an IO port. + */ +ENTRY(inl) + movl S_ARG0,%edx /* IO port address */ + inl %dx,%eax /* get the int */ + ret + +/* + * void loutb(unsigned byte *io_port, + * unsigned byte *data, + * unsigned int count) + * + * Output an array of bytes to an IO port. + */ +ENTRY(loutb) + movl %esi,%eax /* save register */ + movl S_ARG0,%edx /* get io port number */ + movl S_ARG1,%esi /* get data address */ + movl S_ARG2,%ecx /* get count */ + + cld /* count up */ + + rep + outsb /* output */ + + movl %eax,%esi /* restore register */ + ret /* exit */ + + +/* + * void loutw(unsigned short *io_port, + * unsigned short *data, + * unsigned int count) + * + * Output an array of shorts to an IO port. + */ +ENTRY(loutw) + movl %esi,%eax /* save register */ + movl S_ARG0,%edx /* get io port number */ + movl S_ARG1,%esi /* get data address */ + movl S_ARG2,%ecx /* get count */ + + cld /* count up */ + + rep + outsw /* output */ + + movl %eax,%esi /* restore register */ + ret /* exit */ + + +/* + * void linb(unsigned char *io_port, + * unsigned char *data, + * unsigned int count) + * + * Input an array of bytes from an IO port. + */ +ENTRY(linb) + movl %edi,%eax /* save register */ + movl S_ARG0,%edx /* get io port number */ + movl S_ARG1,%edi /* get data address */ + movl S_ARG2,%ecx /* get count */ + + cld /* count up */ + + rep + insb /* input */ + + movl %eax,%edi /* restore register */ + ret /* exit */ + + +/* + * void linw(unsigned short *io_port, + * unsigned short *data, + * unsigned int count) + * + * Input an array of shorts from an IO port. + */ +ENTRY(linw) + movl %edi,%eax /* save register */ + movl S_ARG0,%edx /* get io port number */ + movl S_ARG1,%edi /* get data address */ + movl S_ARG2,%ecx /* get count */ + + cld /* count up */ + + rep + insw /* input */ + + movl %eax,%edi /* restore register */ + ret /* exit */ + + +/* + * 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 + + + +ENTRY(dr6) + movl %db6, %eax + ret + +/* dr<i>(address, type, len, persistence) + */ +ENTRY(dr0) + movl S_ARG0, %eax + movl %eax,EXT(dr_addr) + movl %eax, %db0 + movl $0, %ecx + jmp 0f +ENTRY(dr1) + movl S_ARG0, %eax + movl %eax,EXT(dr_addr)+1*4 + movl %eax, %db1 + movl $2, %ecx + jmp 0f +ENTRY(dr2) + movl S_ARG0, %eax + movl %eax,EXT(dr_addr)+2*4 + movl %eax, %db2 + movl $4, %ecx + jmp 0f + +ENTRY(dr3) + movl S_ARG0, %eax + movl %eax,EXT(dr_addr)+3*4 + movl %eax, %db3 + movl $6, %ecx + +0: + pushl %ebp + movl %esp, %ebp + + movl %db7, %edx + movl %edx,EXT(dr_addr)+4*4 + andl dr_msk(,%ecx,2),%edx /* clear out new entry */ + movl %edx,EXT(dr_addr)+5*4 + movzbl B_ARG3, %eax + andb $3, %al + shll %cl, %eax + orl %eax, %edx + + movzbl B_ARG1, %eax + andb $3, %al + addb $0x10, %ecx + shll %cl, %eax + orl %eax, %edx + + movzbl B_ARG2, %eax + andb $3, %al + addb $0x2, %ecx + shll %cl, %eax + orl %eax, %edx + + movl %edx, %db7 + movl %edx,EXT(dr_addr)+7*4 + movl %edx, %eax + leave + ret + + .data +dr_msk: + .long ~0x000f0003 + .long ~0x00f0000c + .long ~0x0f000030 + .long ~0xf00000c0 +ENTRY(dr_addr) + .long 0,0,0,0 + .long 0,0,0,0 + .text + +/* + * Waste 10 microseconds. + */ +ENTRY(tenmicrosec) + movl EXT(microdata),%ecx /* cycle count for 10 microsecond loop */ +tenmicroloop: + loop tenmicroloop + ret + +/* + * cpu_shutdown() + * Force reboot + */ +null_idtr: + .word 0 + .long 0 + +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 */ + + +/* + * Allocate enough space for a kernel TSS with a complete I/O bitmap, + * for making v86-mode BIOS calls. XXX + */ + .data + .globl EXT(ktss) + .comm EXT(ktss),0x68+65536/8+1 + diff --git a/i386/i386/loose_ends.c b/i386/i386/loose_ends.c new file mode 100644 index 00000000..6a10adc3 --- /dev/null +++ b/i386/i386/loose_ends.c @@ -0,0 +1,82 @@ +/* + * 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 <mach_assert.h> + + + /* + * For now we will always go to single user mode, since there is + * no way pass this request through the boot. + */ +int boothowto = 0; + + /* + * Should be rewritten in asm anyway. + */ +/* + * ovbcopy - like bcopy, but recognizes overlapping ranges and handles + * them correctly. + */ +ovbcopy(from, to, bytes) + char *from, *to; + int bytes; /* num bytes to copy */ +{ + /* Assume that bcopy copies left-to-right (low addr first). */ + if (from + bytes <= to || to + bytes <= from || to == from) + bcopy(from, to, bytes); /* non-overlapping or no-op*/ + else if (from > to) + bcopy(from, to, bytes); /* overlapping but OK */ + else { + /* to > from: overlapping, and must copy right-to-left. */ + from += bytes - 1; + to += bytes - 1; + while (bytes-- > 0) + *to-- = *from--; + } +} + +/* Someone with time should write code to set cpuspeed automagically */ +int cpuspeed = 4; +#define DELAY(n) { register int N = cpuspeed * (n); while (--N > 0); } +delay(n) +{ + DELAY(n); +} + +#if MACH_ASSERT + +/* + * Machine-dependent routine to fill in an array with up to callstack_max + * levels of return pc information. + */ +void machine_callstack( + unsigned long *buf, + int callstack_max) +{ +} + +#endif /* MACH_ASSERT */ diff --git a/i386/i386/mach_i386.srv b/i386/i386/mach_i386.srv new file mode 100644 index 00000000..48d16ba4 --- /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 00000000..d7d4deee --- /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 00000000..a1fb489e --- /dev/null +++ b/i386/i386/machine_routines.h @@ -0,0 +1,37 @@ +/* + * 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_ROUTINE mach_i386_server_routine + +#endif + diff --git a/i386/i386/machspl.h b/i386/i386/machspl.h new file mode 100644 index 00000000..bbb26754 --- /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/mp_desc.c b/i386/i386/mp_desc.c new file mode 100644 index 00000000..d7b4f61e --- /dev/null +++ b/i386/i386/mp_desc.c @@ -0,0 +1,235 @@ +/* + * 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 <cpus.h> + +#if NCPUS > 1 + +#include <kern/cpu_number.h> +#include <mach/machine.h> +#include <vm/vm_kern.h> + +#include <i386/mp_desc.h> +#include <i386/lock.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 interrupt_stack[NCPUS]; +vm_offset_t int_stack_top[NCPUS]; + +/* + * Barrier address. + */ +vm_offset_t int_stack_high; + +/* + * First cpu`s interrupt stack. + */ +char intstack[]; /* bottom */ +char eintstack[]; /* top */ + +/* + * We allocate interrupt stacks from physical memory. + */ +extern +vm_offset_t avail_start; + +/* + * 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. + */ + +/* + * Allocated descriptor tables. + */ +struct mp_desc_table *mp_desc_table[NCPUS] = { 0 }; + +/* + * Pointer to TSS for access in load_context. + */ +struct i386_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]; +extern struct i386_tss ktss; + +/* + * Allocate and initialize the per-processor descriptor tables. + */ + +struct mp_desc_table * +mp_desc_init(mycpu) + register int mycpu; +{ + register struct mp_desc_table *mpt; + + if (mycpu == master_cpu) { + /* + * Master CPU uses the tables built at boot time. + * Just set the TSS and GDT pointers. + */ + mp_ktss[mycpu] = &ktss; + mp_gdt[mycpu] = gdt; + return 0; + } + else { + /* + * Other CPUs allocate the table from the bottom of + * the interrupt stack. + */ + mpt = (struct mp_desc_table *) interrupt_stack[mycpu]; + + mp_desc_table[mycpu] = mpt; + mp_ktss[mycpu] = &mpt->ktss; + mp_gdt[mycpu] = mpt->gdt; + + /* + * Copy the tables + */ + bcopy((char *)idt, + (char *)mpt->idt, + sizeof(idt)); + bcopy((char *)gdt, + (char *)mpt->gdt, + sizeof(gdt)); + bcopy((char *)ldt, + (char *)mpt->ldt, + sizeof(ldt)); + bzero((char *)&mpt->ktss, + sizeof(struct i386_tss)); + + /* + * Fix up the entries in the GDT to point to + * this LDT and this TSS. + */ + fill_descriptor(&mpt->gdt[sel_idx(KERNEL_LDT)], + (unsigned)&mpt->ldt, + LDTSZ * sizeof(struct real_descriptor) - 1, + ACC_P|ACC_PL_K|ACC_LDT, 0); + fill_descriptor(&mpt->gdt[sel_idx(KERNEL_TSS)], + (unsigned)&mpt->ktss, + sizeof(struct i386_tss) - 1, + ACC_P|ACC_PL_K|ACC_TSS, 0); + + mpt->ktss.ss0 = KERNEL_DS; + mpt->ktss.io_bit_map_offset = 0x0FFF; /* no IO bitmap */ + + return mpt; + } +} + + +/* + * Called after all CPUs have been found, but before the VM system + * is running. The machine array must show which CPUs exist. + */ +void +interrupt_stack_alloc() +{ + register int i; + int cpu_count; + vm_offset_t stack_start; + + /* + * Count the number of CPUs. + */ + cpu_count = 0; + for (i = 0; i < NCPUS; i++) + if (machine_slot[i].is_cpu) + cpu_count++; + + /* + * Allocate an interrupt stack for each CPU except for + * the master CPU (which uses the bootstrap stack) + */ + if (!init_alloc(INTSTACK_SIZE*(cpu_count-1), &stack_start)) + panic("not enough memory for interrupt stacks"); + + /* + * Set up pointers to the top of the interrupt stack. + */ + for (i = 0; i < NCPUS; i++) { + if (i == master_cpu) { + interrupt_stack[i] = (vm_offset_t) intstack; + int_stack_top[i] = (vm_offset_t) eintstack; + } + else if (machine_slot[i].is_cpu) { + interrupt_stack[i] = stack_start; + int_stack_top[i] = stack_start + INTSTACK_SIZE; + + stack_start += INTSTACK_SIZE; + } + } + + /* + * Set up the barrier address. All thread stacks MUST + * be above this address. + */ + int_stack_high = stack_start; +} + +/* 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() +{ + 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 */ +} + +#endif /* NCPUS > 1 */ diff --git a/i386/i386/mp_desc.h b/i386/i386/mp_desc.h new file mode 100644 index 00000000..dbc3f5ea --- /dev/null +++ b/i386/i386/mp_desc.h @@ -0,0 +1,84 @@ +/* + * 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 <cpus.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 "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 i386_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 i386_tss *mp_ktss[NCPUS]; + +/* + * So does the GDT. + */ +extern struct real_descriptor *mp_gdt[NCPUS]; + + +/* + * Each CPU calls this routine to set up its descriptor tables. + */ +extern struct mp_desc_table * mp_desc_init(/* int */); + + +#endif MULTIPROCESSOR + +#endif /* _I386_MP_DESC_H_ */ diff --git a/i386/i386/pcb.c b/i386/i386/pcb.c new file mode 100644 index 00000000..f16f21a6 --- /dev/null +++ b/i386/i386/pcb.c @@ -0,0 +1,769 @@ +/* + * 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 <cpus.h> +#include <mach_debug.h> + +#include <mach/std_types.h> +#include <mach/kern_return.h> +#include <mach/thread_status.h> +#include <mach/exec/exec.h> + +#include "vm_param.h" +#include <kern/counters.h> +#include <kern/mach_param.h> +#include <kern/thread.h> +#include <kern/sched_prim.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/tss.h> +#include <i386/user_ldt.h> +#include <i386/fpu.h> +#include "eflags.h" +#include "gdt.h" +#include "ldt.h" +#include "ktss.h" + +#if NCPUS > 1 +#include <i386/mp_desc.h> +#endif + +extern thread_t Switch_context(); +extern void Thread_continue(); + +extern iopb_tss_t iopb_create(); +extern void iopb_destroy(); +extern void user_ldt_free(); + +zone_t pcb_zone; + +vm_offset_t kernel_stack[NCPUS]; /* top of active_stack */ + +/* + * stack_attach: + * + * Attach a kernel stack to a thread. + */ + +void stack_attach(thread, stack, continuation) + register thread_t thread; + register vm_offset_t stack; + void (*continuation)(); +{ + 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 = (int) Thread_continue; + STACK_IKS(stack)->k_ebx = (int) continuation; + STACK_IKS(stack)->k_esp = (int) STACK_IEL(stack); + + /* + * Point top of kernel stack to user`s registers. + */ + STACK_IEL(stack)->saved_state = &thread->pcb->iss; +} + +/* + * stack_detach: + * + * Detaches a kernel stack from a thread, returning the old stack. + */ + +vm_offset_t stack_detach(thread) + register thread_t thread; +{ + register 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) (gdt) +#define curr_ktss(mycpu) (&ktss) +#endif + +#define gdt_desc_p(mycpu,sel) \ + ((struct real_descriptor *)&curr_gdt(mycpu)[sel_idx(sel)]) + +void switch_ktss(pcb) + register pcb_t pcb; +{ + int mycpu = cpu_number(); + { + register iopb_tss_t tss = pcb->ims.io_tss; + 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. + */ + + pcb_stack_top = (pcb->iss.efl & EFL_VM) + ? (int) (&pcb->iss + 1) + : (int) (&pcb->iss.v86_segs); + + if (tss == 0) { + /* + * No per-thread IO permissions. + * Use standard kernel TSS. + */ + if (!(gdt_desc_p(mycpu,KERNEL_TSS)->access & ACC_TSS_BUSY)) + set_tr(KERNEL_TSS); + curr_ktss(mycpu)->esp0 = pcb_stack_top; + } + else { + /* + * Set the IO permissions. Use this thread`s TSS. + */ + *gdt_desc_p(mycpu,USER_TSS) + = *(struct real_descriptor *)tss->iopb_desc; + tss->tss.esp0 = pcb_stack_top; + set_tr(USER_TSS); + gdt_desc_p(mycpu,KERNEL_TSS)->access &= ~ ACC_TSS_BUSY; + } + } + + { + register user_ldt_t ldt = pcb->ims.ldt; + /* + * Set the thread`s LDT. + */ + if (ldt == 0) { + /* + * Use system LDT. + */ + set_ldt(KERNEL_LDT); + } + else { + /* + * Thread has its own LDT. + */ + *gdt_desc_p(mycpu,USER_LDT) = ldt->desc; + set_ldt(USER_LDT); + } + } + /* + * Load the floating-point context, if necessary. + */ + fpu_load_context(pcb); + +} + +/* + * stack_handoff: + * + * Move the current thread's kernel stack to the new thread. + */ + +void stack_handoff(old, new) + register thread_t old; + register thread_t new; +{ + register int mycpu = cpu_number(); + register 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); + } + } + + /* + * 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; + active_threads[mycpu] = new; + + /* + * Switch exception link to point to new + * user registers. + */ + + STACK_IEL(stack)->saved_state = &new->pcb->iss; + +} + +/* + * Switch to the first thread on a CPU. + */ +void load_context(new) + register 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(old, continuation, new) + register thread_t old; + void (*continuation)(); + register 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); + } + } + + /* + * 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() +{ + pcb_zone = zinit(sizeof(struct pcb), + THREAD_MAX * sizeof(struct pcb), + THREAD_CHUNK * sizeof(struct pcb), + 0, "i386 pcb state"); + + fpu_module_init(); + iopb_init(); +} + +void pcb_init(thread) + register thread_t thread; +{ + register pcb_t pcb; + + pcb = (pcb_t) zalloc(pcb_zone); + 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. + */ + bzero((char *) pcb, 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; + pcb->iss.ds = USER_DS; + pcb->iss.es = USER_DS; + pcb->iss.fs = USER_DS; + pcb->iss.gs = USER_DS; + pcb->iss.efl = EFL_USER_SET; + + thread->pcb = pcb; +} + +void pcb_terminate(thread) + register thread_t thread; +{ + register pcb_t pcb = thread->pcb; + + counter(if (--c_threads_current < c_threads_min) + c_threads_min = c_threads_current); + + if (pcb->ims.io_tss != 0) + iopb_destroy(pcb->ims.io_tss); + if (pcb->ims.ifps != 0) + fp_free(pcb->ims.ifps); + if (pcb->ims.ldt != 0) + user_ldt_free(pcb->ims.ldt); + zfree(pcb_zone, (vm_offset_t) pcb); + thread->pcb = 0; +} + +/* + * pcb_collect: + * + * Attempt to free excess pcb memory. + */ + +void pcb_collect(thread) + thread_t thread; +{ +} + + +/* + * thread_setstatus: + * + * Set the status of the specified thread. + */ + +kern_return_t thread_setstatus(thread, flavor, tstate, count) + thread_t thread; + int flavor; + thread_state_t tstate; + unsigned int count; +{ + switch (flavor) { + case i386_THREAD_STATE: + case i386_REGS_SEGS_STATE: + { + register struct i386_thread_state *state; + register struct i386_saved_state *saved_state; + + if (count < i386_THREAD_STATE_COUNT) { + return(KERN_INVALID_ARGUMENT); + } + + 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; + state->ds &= 0xffff; + state->es &= 0xffff; + state->fs &= 0xffff; + state->gs &= 0xffff; + + 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; + } + + state = (struct i386_thread_state *) tstate; + + saved_state = USER_REGS(thread); + + /* + * General registers + */ + 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; + + /* + * Segment registers. Set differently in V8086 mode. + */ + if (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 = + state->efl & (EFL_TF | EFL_IF); + } + } + else 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; + saved_state->ds = USER_DS; + saved_state->es = USER_DS; + saved_state->fs = USER_DS; + saved_state->gs = USER_DS; + } + 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; + saved_state->ds = state->ds; + saved_state->es = state->es; + saved_state->fs = state->fs; + saved_state->gs = state->gs; + } + 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; + register iopb_tss_t tss; + + 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; + } + + bcopy((char *) state->pm, + (char *) tss->bitmap, + sizeof state->pm); +#endif + break; + } + + case i386_V86_ASSIST_STATE: + { + register 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_ADDRESS || + int_table + + int_count * sizeof(struct v86_interrupt_table) + > VM_MAX_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; + } + + default: + return(KERN_INVALID_ARGUMENT); + } + + return(KERN_SUCCESS); +} + +/* + * thread_getstatus: + * + * Get the status of the specified thread. + */ + +kern_return_t thread_getstatus(thread, flavor, tstate, count) + register 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 (*count < 4) + return (KERN_INVALID_ARGUMENT); + tstate[0] = i386_THREAD_STATE; + tstate[1] = i386_FLOAT_STATE; + tstate[2] = i386_ISA_PORT_MAP_STATE; + tstate[3] = i386_V86_ASSIST_STATE; + *count = 4; + break; + + case i386_THREAD_STATE: + case i386_REGS_SEGS_STATE: + { + register struct i386_thread_state *state; + register 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. + */ + 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->efl = saved_state->efl; + state->uesp = saved_state->uesp; + + state->cs = saved_state->cs; + state->ss = saved_state->ss; + 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) + 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; + } + *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: { + register struct i386_isa_port_map_state *state; + register iopb_tss_t tss; + + if (*count < i386_ISA_PORT_MAP_STATE_COUNT) + return(KERN_INVALID_ARGUMENT); + + state = (struct i386_isa_port_map_state *) tstate; + tss = thread->pcb->ims.io_tss; + + if (tss == 0) { + int i; + + /* + * The thread has no ktss, so no IO permissions. + */ + + for (i = 0; i < sizeof state->pm; i++) + state->pm[i] = 0xff; + } else { + /* + * The thread has its own ktss. + */ + + bcopy((char *) tss->bitmap, + (char *) state->pm, + sizeof state->pm); + } + + *count = i386_ISA_PORT_MAP_STATE_COUNT; + break; + } + + case i386_V86_ASSIST_STATE: + { + register 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; + } + + 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, retval) + thread_t thread; + kern_return_t retval; +{ + thread->pcb->iss.eax = retval; +} + + +/* + * Return prefered 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(stack_size) + vm_size_t stack_size; +{ + return (VM_MAX_ADDRESS - stack_size); +} + +/* + * Allocate argument area and set registers for first user thread. + */ +vm_offset_t +set_user_regs(stack_base, stack_size, exec_info, arg_size) + vm_offset_t stack_base; /* low address */ + vm_offset_t stack_size; + struct exec_info *exec_info; + vm_size_t arg_size; +{ + vm_offset_t arg_addr; + register struct i386_saved_state *saved_state; + + arg_size = (arg_size + sizeof(int) - 1) & ~(sizeof(int)-1); + arg_addr = stack_base + stack_size - arg_size; + + saved_state = USER_REGS(current_thread()); + saved_state->uesp = (int)arg_addr; + saved_state->eip = exec_info->entry; + + return (arg_addr); +} diff --git a/i386/i386/phys.c b/i386/i386/phys.c new file mode 100644 index 00000000..518812d3 --- /dev/null +++ b/i386/i386/phys.c @@ -0,0 +1,102 @@ +/* + * 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 <mach/boolean.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 <mach/machine/vm_param.h> + +/* + * pmap_zero_page zeros the specified (machine independent) page. + */ +pmap_zero_page(p) + vm_offset_t p; +{ + assert(p != vm_page_fictitious_addr); + bzero(phystokv(p), PAGE_SIZE); +} + +/* + * pmap_copy_page copies the specified (machine independent) pages. + */ +pmap_copy_page(src, dst) + vm_offset_t src, dst; +{ + assert(src != vm_page_fictitious_addr); + assert(dst != vm_page_fictitious_addr); + + bcopy(phystokv(src), phystokv(dst), PAGE_SIZE); +} + +/* + * copy_to_phys(src_addr_v, dst_addr_p, count) + * + * Copy virtual memory to physical memory + */ +copy_to_phys(src_addr_v, dst_addr_p, count) + vm_offset_t src_addr_v, dst_addr_p; + int count; +{ + assert(dst_addr_p != vm_page_fictitious_addr); + bcopy(src_addr_v, phystokv(dst_addr_p), 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). + */ +copy_from_phys(src_addr_p, dst_addr_v, count) + vm_offset_t src_addr_p, dst_addr_v; + int count; +{ + assert(src_addr_p != vm_page_fictitious_addr); + bcopy(phystokv(src_addr_p), dst_addr_v, count); +} + +/* + * kvtophys(addr) + * + * Convert a kernel virtual address to a physical address + */ +vm_offset_t +kvtophys(addr) +vm_offset_t addr; +{ + pt_entry_t *pte; + + if ((pte = pmap_pte(kernel_pmap, addr)) == PT_ENTRY_NULL) + return 0; + return i386_trunc_page(*pte) | (addr & INTEL_OFFMASK); +} diff --git a/i386/i386/pic.c b/i386/i386/pic.c new file mode 100644 index 00000000..8380db84 --- /dev/null +++ b/i386/i386/pic.c @@ -0,0 +1,270 @@ +/* + * 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. +*/ + +#include <platforms.h> + +#include <sys/types.h> +#include <i386/ipl.h> +#include <i386/pic.h> +#include <i386/machspl.h> + +spl_t curr_ipl; +int pic_mask[NSPL]; +int curr_pic_mask; + +int iunit[NINTR] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + +int nintr = NINTR; +int npics = NPICS; + +char *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 a table of interrupt priority levels +** * Establishes a table of interrupt masks to be put +** in the PICs. +** * Establishes location of PICs in the system +** * Initialises them +** +** At this stage the interrupt functionality of this system should be +** coplete. +** +*/ + + +/* +** 1. First we form a table of PIC masks - rather then calling form_pic_mask() +** each time there is a change of interrupt level - we will form a table +** of pic masks, as there are only 7 interrupt priority levels. +** +** 2. The next thing we must do is to determine which of the PIC interrupt +** request lines have to be masked out, this is done by calling +** form_pic_mask() with a (int_lev) of zero, this will find all the +** interrupt lines that have priority 0, (ie to be ignored). +** Then we split this up for the master/slave PICs. +** +** 2. Initialise the PICs , master first, then the slave. +** All the register field definitions are described in pic_jh.h, also +** the settings of these fields for the various registers are selected. +** +*/ + +picinit() +{ + + u_short i; + + asm("cli"); + + /* + ** 1. Form pic mask table + */ +#if 0 + printf (" Let the console driver screw up this line ! \n"); +#endif + + form_pic_mask(); + + /* + ** 1a. Select current SPL. + */ + + curr_ipl = SPLHI; + curr_pic_mask = pic_mask[SPLHI]; + + /* + ** 2. Generate addresses to each PIC port. + */ + + master_icw = (char *)PIC_MASTER_ICW; + master_ocw = (char *)PIC_MASTER_OCW; + slaves_icw = (char *)PIC_SLAVE_ICW; + slaves_ocw = (char *)PIC_SLAVE_OCW; + +#ifdef PS2 +#else /* PS2 */ + /* + ** 3. 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 +#ifdef iPSC386 + PICM_ICW3 = ( SLAVE_ON_IR7 ); + PICS_ICW3 = ( I_AM_SLAVE_7 ); +#endif iPSC386 + +#ifdef iPSC386 + /* Use Buffered mode for iPSC386 */ + PICM_ICW4 = (SNF_MODE_DIS | BUFFERD_MODE | I_AM_A_MASTR | + NRML_EOI_MOD | I8086_EMM_MOD); + PICS_ICW4 = (SNF_MODE_DIS | BUFFERD_MODE | I_AM_A_SLAVE | + NRML_EOI_MOD | I8086_EMM_MOD); +#else iPSC386 + 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); +#endif iPSC386 + + 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 ); + + + /* + ** 4. 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 ); + + /* + ** 5. 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 ); + + /* + ** 6. Initialise interrupts + */ + outb ( master_ocw, PICM_OCW1 ); + +#endif /* PS2 */ + +#if 0 + printf(" spl set to %x \n", curr_pic_mask); +#endif + +} + + +/* +** form_pic_mask(int_lvl) +** +** For a given interrupt priority level (int_lvl), this routine goes out +** and scans through the interrupt level table, and forms a mask based on the +** entries it finds there that have the same or lower interrupt priority level +** as (int_lvl). It returns a 16-bit mask which will have to be split up between +** the 2 pics. +** +*/ + +#if defined(AT386) || defined(PS2) +#define SLAVEMASK (0xFFFF ^ SLAVE_ON_IR2) +#endif /* defined(AT386) || defined(PS2) */ +#ifdef iPSC386 +#define SLAVEMASK (0xFFFF ^ SLAVE_ON_IR7) +#endif iPSC386 + +#define SLAVEACTV 0xFF00 + +form_pic_mask() +{ + unsigned int i, j, bit, mask; + + for (i=SPL0; i < NSPL; i++) { + for (j=0x00, bit=0x01, mask = 0; j < NINTR; j++, bit<<=1) + if (intpri[j] <= i) + mask |= bit; + + if ((mask & SLAVEACTV) != SLAVEACTV ) + mask &= SLAVEMASK; + + pic_mask[i] = mask; + } +} + +intnull(unit_dev) +{ + printf("intnull(%d)\n", unit_dev); +} + +int prtnull_count = 0; +prtnull(unit) +{ + ++prtnull_count; +} diff --git a/i386/i386/pic.h b/i386/i386/pic.h new file mode 100644 index 00000000..66b92d80 --- /dev/null +++ b/i386/i386/pic.h @@ -0,0 +1,197 @@ +/* + * 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_ + +#include <platforms.h> + +#define NINTR 0x10 +#define NPICS 0x02 + +/* +** The following are definitions used to locate the PICs in the system +*/ + +#if defined(AT386) || defined(PS2) +#define ADDR_PIC_BASE 0x20 +#define OFF_ICW 0x00 +#define OFF_OCW 0x01 +#define SIZE_PIC 0x80 +#endif /* defined(AT386) || defined(PS2) */ + +#ifdef iPSC386 +#define ADDR_PIC_BASE 0xC0 +#define OFF_ICW 0x00 +#define OFF_OCW 0x02 +#define SIZE_PIC 0x04 +#endif iPSC386 + +#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(PS2) +#define PICM_VECTBASE 0x40 +#define PICS_VECTBASE PICM_VECTBASE + 0x08 +#endif /* defined(AT386) || defined(PS2) */ + +#ifdef iPSC386 +#define PICM_VECTBASE 0x40 +#define PICS_VECTBASE PICM_VECTBASE + 0x08 +#endif iPSC386 + +/* +** 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 +#if iPSC386 +#define I_AM_A_SLAVE 0x00 +#define I_AM_A_MASTR 0x04 +#endif iPSC386 +#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 0x30 +#define ROT_NON_SPEC 0x50 +#define SET_ROT_AEOI 0x40 +#define RSET_ROTAEOI 0x00 +#define ROT_SPEC_EOI 0x70 +#define SET_PRIORITY 0x60 +#define NO_OPERATION 0x20 + +#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 + +#endif _I386_PIC_H_ diff --git a/i386/i386/pio.h b/i386/i386/pio.h new file mode 100644 index 00000000..b2427f92 --- /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 long _tmp__; \ + asm volatile("inl %1, %0" : "=a" (_tmp__) : "d" ((unsigned short)(y))); \ + _tmp__; }) + +#define inw(y) \ +({ unsigned short _tmp__; \ + asm volatile(".byte 0x66; inl %1, %0" : "=a" (_tmp__) : "d" ((unsigned short)(y))); \ + _tmp__; }) + +#define inb(y) \ +({ unsigned char _tmp__; \ + asm volatile("inb %1, %0" : "=a" (_tmp__) : "d" ((unsigned short)(y))); \ + _tmp__; }) + + +#define outl(x, y) \ +{ asm volatile("outl %0, %1" : : "a" (y) , "d" ((unsigned short)(x))); } + + +#define outw(x, y) \ +{asm volatile(".byte 0x66; outl %0, %1" : : "a" ((unsigned short)(y)) , "d" ((unsigned short)(x))); } + + +#define outb(x, y) \ +{ asm volatile("outb %0, %1" : : "a" ((unsigned char)(y)) , "d" ((unsigned short)(x))); } + +#endif /* _I386_PIO_H_ */ diff --git a/i386/i386/pit.c b/i386/i386/pit.c new file mode 100644 index 00000000..3ae14870 --- /dev/null +++ b/i386/i386/pit.c @@ -0,0 +1,236 @@ +/* + * 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 <platforms.h> +#include <kern/time_out.h> +#include <i386/ipl.h> +#include <i386/pit.h> + +int pitctl_port = PITCTL_PORT; /* For 386/20 Board */ +int pitctr0_port = PITCTR0_PORT; /* For 386/20 Board */ +int pitctr1_port = PITCTR1_PORT; /* For 386/20 Board */ +int pitctr2_port = PITCTR2_PORT; /* For 386/20 Board */ +/* We want PIT 0 in square wave mode */ + +int pit0_mode = PIT_C0|PIT_SQUAREMODE|PIT_READMODE ; + + +unsigned int delaycount; /* loop count in trying to delay for + * 1 millisecond + */ +unsigned long microdata=50; /* loop count for 10 microsecond wait. + MUST be initialized for those who + insist on calling "tenmicrosec" + it before the clock has been + initialized. + */ +unsigned int clknumb = CLKNUM; /* interrupt interval for timer 0 */ + +#ifdef PS2 +extern int clock_int_handler(); + +#include <sys/types.h> +#include <i386ps2/abios.h> +static struct generic_request *clock_request_block; +static int clock_flags; +char cqbuf[200]; /*XXX temporary.. should use kmem_alloc or whatever..*/ +#endif /* PS2 */ + +clkstart() +{ + unsigned int flags; + unsigned char byte; + int s; + + intpri[0] = SPLHI; + form_pic_mask(); + + findspeed(); + microfind(); + s = sploff(); /* disable interrupts */ + +#ifdef PS2 + abios_clock_start(); +#endif /* PS2 */ + + /* 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; + byte = clknumb; + outb(pitctr0_port, byte); + byte = clknumb>>8; + outb(pitctr0_port, byte); + splon(s); /* restore interrupt state */ +} + +#define COUNT 10000 /* should be a multiple of 1000! */ + +findspeed() +{ + unsigned int flags; + unsigned char byte; + unsigned int leftover; + int i; + int j; + int s; + + s = sploff(); /* disable interrupts */ + /* Put counter in count down mode */ +#define PIT_COUNTDOWN PIT_READMODE|PIT_NDIVMODE + outb(pitctl_port, PIT_COUNTDOWN); + /* output a count of -1 to counter 0 */ + outb(pitctr0_port, 0xff); + outb(pitctr0_port, 0xff); + delaycount = COUNT; + spinwait(1); + /* Read the value left in the counter */ + byte = inb(pitctr0_port); /* least siginifcant */ + leftover = inb(pitctr0_port); /* most significant */ + leftover = (leftover<<8) + byte ; + /* Formula for delaycount is : + * (loopcount * timer clock speed)/ (counter ticks * 1000) + * 1000 is for figuring out milliseconds + */ + /* we arrange calculation so that it doesn't overflow */ + delaycount = ((COUNT/1000) * CLKNUM) / (0xffff-leftover); + printf("findspeed: delaycount=%d (tics=%d)\n", + delaycount, (0xffff-leftover)); + splon(s); /* restore interrupt state */ +} + +#ifdef PS2 + +abios_clock_start() +{ + struct generic_request temp_request_block; + int rc; + + nmi_enable(); /* has to happen somewhere! */ + temp_request_block.r_current_req_blck_len = ABIOS_MIN_REQ_SIZE; + temp_request_block.r_logical_id = abios_next_LID(SYSTIME_ID, + ABIOS_FIRST_LID); + temp_request_block.r_unit = 0; + temp_request_block.r_function = ABIOS_LOGICAL_PARAMETER; + temp_request_block.r_return_code = ABIOS_UNDEFINED; + + abios_common_start(&temp_request_block,0); + if (temp_request_block.r_return_code != ABIOS_DONE) { + panic("couldn init abios time code!\n"); + } + + /* + * now build the clock request for the hardware system clock + */ + clock_request_block = (struct generic_request *)cqbuf; + clock_request_block->r_current_req_blck_len = + temp_request_block.r_request_block_length; + clock_request_block->r_logical_id = temp_request_block.r_logical_id; + clock_request_block->r_unit = 0; + clock_request_block->r_function = ABIOS_DEFAULT_INTERRUPT; + clock_request_block->r_return_code = ABIOS_UNDEFINED; + clock_flags = temp_request_block.r_logical_id_flags; +} + +ackrtclock() +{ + if (clock_request_block) { + clock_request_block->r_return_code = ABIOS_UNDEFINED; + abios_common_interrupt(clock_request_block,clock_flags); + } + } +#endif /* PS2 */ + + +spinwait(millis) + int millis; /* number of milliseconds to delay */ +{ + int i, j; + + for (i=0;i<millis;i++) + for (j=0;j<delaycount;j++) + ; +} + +#define MICROCOUNT 1000 /* keep small to prevent overflow */ +microfind() +{ + unsigned int flags; + unsigned char byte; + unsigned short leftover; + int s; + + + s = sploff(); /* disable interrupts */ + + /* Put counter in count down mode */ + outb(pitctl_port, PIT_COUNTDOWN); + /* output a count of -1 to counter 0 */ + outb(pitctr0_port, 0xff); + outb(pitctr0_port, 0xff); + microdata=MICROCOUNT; + tenmicrosec(); + /* Read the value left in the counter */ + byte = inb(pitctr0_port); /* least siginifcant */ + leftover = inb(pitctr0_port); /* most significant */ + leftover = (leftover<<8) + byte ; + /* Formula for delaycount is : + * (loopcount * timer clock speed)/ (counter ticks * 1000) + * Note also that 1000 is for figuring out milliseconds + */ + microdata = (MICROCOUNT * CLKNUM) / ((0xffff-leftover)*100000); + if (!microdata) + microdata++; + + splon(s); /* restore interrupt state */ +} diff --git a/i386/i386/pit.h b/i386/i386/pit.h new file mode 100644 index 00000000..3cadb30a --- /dev/null +++ b/i386/i386/pit.h @@ -0,0 +1,118 @@ +/* + * 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 <platforms.h> +#if defined(MB1) || defined(MB2) || EXL > 0 || iPSC386 +/* Definitions for 8254 Programmable Interrupt Timer ports on 386/20 */ +#define PITCTR0_PORT 0xD0 /* counter 0 port */ +#define PITCTR1_PORT 0xD2 /* counter 1 port */ +#define PITCTR2_PORT 0xD4 /* counter 2 port */ +#define PITCTL_PORT 0xD6 /* PIT control port */ +#else /* defined(AT386) || defined(PS2) */ +/* 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 */ +#endif /* defined(AT386) || defined(PS2) */ + +/* 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 */ +#define PIT_SQUAREMODE 0x06 /* square-wave mode */ + +/* 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_RATEMODE 0x06 /* square-wave mode for USART */ + +/* + * Clock speed for the timer in hz divided by the constant HZ + * (defined in param.h) + */ +#if AT386 || PS2 +#define CLKNUM 1193167 +#endif /* AT386 || PS2 */ +#if defined(MB1) +#define CLKNUM 12300 +#endif +#if defined(MB2) || EXL > 0 +#define CLKNUM 12500 +#endif +#if iPSC386 +#define CLKNUM 1000000 +#endif iPSC386 + +#if EXL +/* added micro-timer support. --- csy */ +typedef struct time_latch { + time_t ticks; /* time in HZ since boot */ + time_t uticks; /* time in 1.25 MHZ */ +/* don't need these two for now. --- csy */ +/* time_t secs; /* seconds since boot */ +/* time_t epochsecs; /* seconds since epoch */ + } time_latch; +/* a couple in-line assembly codes for efficiency. */ +asm int intr_disable() +{ + pushfl + cli +} + +asm int intr_restore() +{ + popfl +} + +#endif EXL diff --git a/i386/i386/pmap.h b/i386/i386/pmap.h new file mode 100644 index 00000000..28b8cead --- /dev/null +++ b/i386/i386/pmap.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. + */ +/* + * Now using shared pmap module for i386 and i860. + */ + +#include <intel/pmap.h> diff --git a/i386/i386/proc_reg.h b/i386/i386/proc_reg.h new file mode 100644 index 00000000..1aa646b8 --- /dev/null +++ b/i386/i386/proc_reg.h @@ -0,0 +1,150 @@ +/* + * 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 */ + +#ifndef ASSEMBLER +#ifdef __GNUC__ + +static inline unsigned +get_eflags() +{ + unsigned eflags; + asm volatile("pushfd; popl %0" : "=r" (eflags)); + return eflags; +} + +static inline void +set_eflags(unsigned eflags) +{ + asm volatile("pushl %0; popfd" : : "r" (eflags)); +} + +#define get_esp() \ + ({ \ + register unsigned int _temp__; \ + asm("mov %%esp, %0" : "=r" (_temp__)); \ + _temp__; \ + }) + +#define get_eflags() \ + ({ \ + register unsigned int _temp__; \ + asm("pushf; popl %0" : "=r" (_temp__)); \ + _temp__; \ + }) + +#define get_cr0() \ + ({ \ + register unsigned int _temp__; \ + asm("mov %%cr0, %0" : "=r" (_temp__)); \ + _temp__; \ + }) + +#define set_cr0(value) \ + ({ \ + register unsigned int _temp__ = (value); \ + asm volatile("mov %0, %%cr0" : : "r" (_temp__)); \ + }) + +#define get_cr2() \ + ({ \ + register unsigned int _temp__; \ + asm("mov %%cr2, %0" : "=r" (_temp__)); \ + _temp__; \ + }) + +#define get_cr3() \ + ({ \ + register unsigned int _temp__; \ + asm("mov %%cr3, %0" : "=r" (_temp__)); \ + _temp__; \ + }) + +#define set_cr3(value) \ + ({ \ + register unsigned int _temp__ = (value); \ + asm volatile("mov %0, %%cr3" : : "r" (_temp__)); \ + }) + +#define set_ts() \ + set_cr0(get_cr0() | CR0_TS) + +#define clear_ts() \ + asm volatile("clts") + +#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 + 0: + ") + +#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 00000000..cb372e51 --- /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 18 bits to convert + * microseconds to priorities. + */ + +#define PRI_SHIFT 18 + +#endif _I386_SCHED_PARAM_H_ diff --git a/i386/i386/seg.c b/i386/i386/seg.c new file mode 100644 index 00000000..d57c255e --- /dev/null +++ b/i386/i386/seg.c @@ -0,0 +1,5 @@ + +#define MACH_INLINE +#include "seg.h" +#include "tss.h" + diff --git a/i386/i386/seg.h b/i386/i386/seg.h new file mode 100644 index 00000000..b86e967c --- /dev/null +++ b/i386/i386/seg.h @@ -0,0 +1,184 @@ +/* + * 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 <platforms.h> + +/* + * i386 segmentation. + */ + +#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 */ +}; + +struct real_gate { + unsigned int offset_low:16, /* offset 0..15 */ + selector:16, + word_count:8, + access:8, + offset_high:16; /* offset 16..31 */ +}; + +#endif !ASSEMBLER + +#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 0x00 /* 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 0x00 /* 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> + + +/* Format of a "pseudo-descriptor", used for loading the IDT and GDT. */ +struct pseudo_descriptor +{ + short pad; + unsigned short limit; + unsigned long linear_base; +}; + + +/* Load the processor's IDT, GDT, or LDT pointers. */ +MACH_INLINE void lgdt(struct pseudo_descriptor *pdesc) +{ + __asm volatile("lgdt %0" : : "m" (pdesc->limit)); +} +MACH_INLINE void lidt(struct pseudo_descriptor *pdesc) +{ + __asm volatile("lidt %0" : : "m" (pdesc->limit)); +} +MACH_INLINE void lldt(unsigned short ldt_selector) +{ + __asm volatile("lldt %w0" : : "r" (ldt_selector)); +} + +#ifdef CODE16 +#define i16_lgdt lgdt +#define i16_lidt lidt +#define i16_lldt lldt +#endif + + +/* Fill a segment descriptor. */ +MACH_INLINE void +fill_descriptor(struct real_descriptor *desc, unsigned base, unsigned limit, + unsigned char access, unsigned char sizebits) +{ + 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; +} + +/* Fill a gate with particular values. */ +MACH_INLINE void +fill_gate(struct real_gate *gate, unsigned 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; +} + +#endif !ASSEMBLER + +#endif /* _I386_SEG_H_ */ diff --git a/i386/i386/setjmp.h b/i386/i386/setjmp.h new file mode 100644 index 00000000..21c856dc --- /dev/null +++ b/i386/i386/setjmp.h @@ -0,0 +1,36 @@ +/* + * 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 { + int jmp_buf[6]; /* ebx, esi, edi, ebp, esp, eip */ +} jmp_buf_t; + +#endif /* _I386_SETJMP_H_ */ diff --git a/i386/i386/spl.S b/i386/i386/spl.S new file mode 100644 index 00000000..f77b5563 --- /dev/null +++ b/i386/i386/spl.S @@ -0,0 +1,220 @@ +/* + * 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/pic.h> + +/* + * Set IPL to the specified value. + * + * NOTE: Normally we would not have to enable interrupts + * here. Linux drivers, however, use cli()/sti(), so we must + * guard against the case where a Mach routine which + * has done an spl() calls a Linux routine that returns + * with interrupts disabled. A subsequent splx() can, + * potentially, return with interrupts disabled. + */ +#define SETIPL(level) \ + movl $(level),%edx; \ + cmpl EXT(curr_ipl),%edx; \ + jne spl; \ + sti; \ + movl %edx,%eax; \ + ret + +/* + * Program PICs with mask in %eax. + */ +#define SETMASK() \ + cmpl EXT(curr_pic_mask),%eax; \ + je 9f; \ + outb %al,$(PIC_MASTER_OCW); \ + movl %eax,EXT(curr_pic_mask); \ + movb %ah,%al; \ + outb %al,$(PIC_SLAVE_OCW); \ +9: + +ENTRY(spl0) + movl EXT(curr_ipl),%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: + cmpl $(SPL0),EXT(curr_ipl) /* are we at spl0? */ + je 1f /* yes, all done */ + movl $(SPL0),EXT(curr_ipl) /* set ipl */ + movl EXT(pic_mask)+SPL0*4,%eax + /* get PIC mask */ + SETMASK() /* program PICs with new mask */ +1: + sti /* enable interrupts */ + popl %eax /* return previous mask */ + ret + +Entry(splsoftclock) +ENTRY(spl1) + SETIPL(SPL1) + +ENTRY(spl2) + SETIPL(SPL2) + +ENTRY(spl3) + SETIPL(SPL3) + +Entry(splnet) +Entry(splhdw) +ENTRY(spl4) + SETIPL(SPL4) + +Entry(splbio) +Entry(spldcm) +ENTRY(spl5) + SETIPL(SPL5) + +Entry(spltty) +Entry(splimp) +Entry(splvm) +ENTRY(spl6) + SETIPL(SPL6) + +Entry(splclock) +Entry(splsched) +Entry(splhigh) +Entry(splhi) +ENTRY(spl7) + SETIPL(SPL7) + +ENTRY(splx) + movl 4(%esp),%edx /* get ipl */ + testl %edx,%edx /* spl0? */ + jz EXT(spl0) /* yes, handle specially */ + cmpl EXT(curr_ipl),%edx /* same ipl as current? */ + jne spl /* no */ + sti /* ensure interrupts are enabled */ + 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 4(%esp),%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: + cmpl EXT(curr_ipl),%edx /* same ipl as current? */ + je 1f /* yes, all done */ + movl %edx,EXT(curr_ipl) /* set ipl */ + movl EXT(pic_mask)(,%edx,4),%eax + /* get PIC mask */ + SETMASK() /* program PICs with new mask */ +1: + ret + +/* + * NOTE: This routine must *not* use %ecx, otherwise + * the interrupt code will break. + */ + .align TEXT_ALIGN + .globl spl +spl: + movl EXT(pic_mask)(,%edx,4),%eax + /* get PIC mask */ + cli /* disable interrupts */ + xchgl EXT(curr_ipl),%edx /* set ipl */ + SETMASK() /* program PICs with new mask */ + 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 00000000..219ee9f2 --- /dev/null +++ b/i386/i386/spl.h @@ -0,0 +1,51 @@ +/* + * 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 (spl1)(void); + +extern spl_t (spl2)(void); + +extern spl_t (spl3)(void); + +extern spl_t (spl4)(void); +extern spl_t (splhdw)(void); + +extern spl_t (spl5)(void); +extern spl_t (spldcm)(void); + +extern spl_t (spl6)(void); + +#endif /* _MACHINE_SPL_H_ */ diff --git a/i386/i386/thread.h b/i386/i386/thread.h new file mode 100644 index 00000000..922427eb --- /dev/null +++ b/i386/i386/thread.h @@ -0,0 +1,195 @@ +/* + * 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 <kern/lock.h> + +#include <i386/iopb.h> +#include <i386/tss.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 { + unsigned int gs; + unsigned int fs; + unsigned int es; + unsigned int ds; + unsigned int edi; + unsigned int esi; + unsigned int ebp; + unsigned int cr2; /* kernel esp stored by pusha - + we save cr2 here later */ + unsigned int ebx; + unsigned int edx; + unsigned int ecx; + unsigned int eax; + unsigned int trapno; + unsigned int err; + unsigned int eip; + unsigned int cs; + unsigned int efl; + unsigned int uesp; + unsigned int ss; + struct v86_segs { + unsigned int v86_es; /* virtual 8086 segment registers */ + unsigned int v86_ds; + unsigned int v86_fs; + unsigned int v86_gs; + } v86_segs; +}; + +/* + * 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 { + int k_ebx; /* kernel context */ + int k_esp; + int k_ebp; + int k_edi; + int k_esi; + int k_eip; +}; + +/* + * Save area for user floating-point state. + * Allocated only when necessary. + */ + +struct i386_fpsave_state { + boolean_t fp_valid; + struct i386_fp_save fp_save_state; + struct i386_fp_regs fp_regs; +}; + +/* + * 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 */ + +/* + * 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 { + int es; + int ds; + int edx; + int ecx; + int eax; + int eip; + int cs; + int 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 { + iopb_tss_t io_tss; + struct user_ldt * ldt; + struct i386_fpsave_state *ifps; + struct v86_assist_state v86s; +}; + +typedef struct pcb { + struct i386_interrupt_state iis[2]; /* interrupt and NMI */ + struct i386_saved_state iss; + struct i386_machine_state ims; + decl_simple_lock_data(, lock) +} *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) + +#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 00000000..43bb956b --- /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/timer.h b/i386/i386/timer.h new file mode 100644 index 00000000..b74965df --- /dev/null +++ b/i386/i386/timer.h @@ -0,0 +1,71 @@ +/* + * 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_TIMER_H_ +#define _I386_TIMER_H_ + +/* + * Machine dependent timer definitions. + */ + +#include <platforms.h> + +#ifdef SYMMETRY + +/* + * TIMER_MAX is not used on the Sequent because a 32-bit rollover + * timer does not need to be adjusted for maximum value. + */ + +/* + * TIMER_RATE is the rate of the timer in ticks per second. + * It is used to calculate percent cpu usage. + */ + +#define TIMER_RATE 1000000 + +/* + * TIMER_HIGH_UNIT is the unit for high_bits in terms of low_bits. + * Setting it to TIMER_RATE makes the high unit seconds. + */ + +#define TIMER_HIGH_UNIT TIMER_RATE + +/* + * TIMER_ADJUST is used to adjust the value of a timer after + * it has been copied into a time_value_t. No adjustment is needed + * on Sequent because high_bits is in seconds. + */ + +/* + * MACHINE_TIMER_ROUTINES should defined if the timer routines are + * implemented in machine-dependent code (e.g. assembly language). + */ +#define MACHINE_TIMER_ROUTINES + +#endif + +#endif /* _I386_TIMER_H_ */ diff --git a/i386/i386/trap.c b/i386/i386/trap.c new file mode 100644 index 00000000..6096a39f --- /dev/null +++ b/i386/i386/trap.c @@ -0,0 +1,1139 @@ +/* + * 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 <cpus.h> +#include <fpe.h> +#include <mach_kdb.h> +#include <mach_ttd.h> +#include <mach_pcsample.h> + +#include <sys/types.h> +#include <mach/machine/eflags.h> +#include <i386/trap.h> +#include <machine/machspl.h> /* for spl_t */ + +#include <mach/exception.h> +#include <mach/kern_return.h> +#include "vm_param.h" +#include <mach/machine/thread_status.h> + +#include <vm/vm_kern.h> +#include <vm/vm_map.h> + +#include <kern/ast.h> +#include <kern/thread.h> +#include <kern/task.h> +#include <kern/sched.h> +#include <kern/sched_prim.h> + +#include <i386/io_emulate.h> + +#include "debug.h" + +extern void exception(); +extern void thread_exception_return(); + +extern void i386_exception(); + +#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() +{ + register thread_t thread = current_thread(); + register 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 + +void +user_page_fault_continue(kr) + kern_return_t kr; +{ + register thread_t thread = current_thread(); + register 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*/ +} + +/* + * Fault recovery in copyin/copyout routines. + */ +struct recovery { + int fault_addr; + int 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[]; + + +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)"; +} + + +boolean_t brb = TRUE; + +/* + * Trap from kernel mode. Only page-fault errors are recoverable, + * and then only in special circumstances. All other errors are + * fatal. + */ +void kernel_trap(regs) + register struct i386_saved_state *regs; +{ + int exc; + int code; + int subcode; + register int type; + vm_map_t map; + kern_return_t result; + register 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", type, 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 (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 >= (int)start + && subcode < (int)etext)) { + printf("Kernel page fault at address 0x%x, " + "eip = 0x%x\n", + subcode, regs->eip); + goto badtrap; + } + } else { + assert(thread); + map = thread->task->map; + if (map == kernel_map) { + printf("kernel page fault at %08x:\n"); + 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), + VM_PROT_READ|VM_PROT_WRITE, + 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 ((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)); + } + + if (result == KERN_SUCCESS) { + /* + * Certain faults require that we back up + * the EIP. + */ + register struct recovery *rp; + + 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. + */ + { + register struct recovery *rp; + + 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 %d", type); + printf(", eip 0x%x\n", regs->eip); +#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 %d, code = %x\n", + type, code); + dump_ss(regs); + panic("trap"); + return; + } +} + + +/* + * Trap from user mode. + * Return TRUE if from emulated system call. + */ +int user_trap(regs) + register struct i386_saved_state *regs; +{ + int exc; + int code; + int subcode; + register int type; + vm_map_t map; + kern_return_t result; + register thread_t thread = current_thread(); + extern vm_offset_t phys_last_addr; + + if ((vm_offset_t)thread < phys_last_addr) { + printf("user_trap: bad thread pointer 0x%x\n", thread); + printf("trap type %d, code 0x%x, va 0x%x, eip 0x%x\n", + regs->trapno, regs->err, regs->cr2, regs->eip); + asm volatile ("1: hlt; jmp 1b"); + } +#if 0 +printf("user trap %d error %d sub %08x\n", type, code, subcode); +#endif + + if (regs->efl & EFL_VM) { + /* + * If hardware assist can handle exception, + * continue execution. + */ + if (v86_assist(thread, regs)) + return 0; + } + + 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 + 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 + { + boolean_t db_find_breakpoint_here(); + + 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 + 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: +#if FPE + if (fp_emul_error(regs)) + return 0; +#endif /* FPE */ + + 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: + if (!(regs->efl & EFL_VM)) { + if (check_io_fault(regs)) + return 0; + } + /* 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; + } + } + exc = EXC_BAD_INSTRUCTION; + code = EXC_I386_GPFLT; + subcode = regs->err & 0xffff; + break; + + case T_PAGE_FAULT: +#if 0 + printf("user page fault at linear address %08x\n", subcode); +#endif + assert(subcode < LINEAR_MIN_KERNEL_ADDRESS); + subcode = regs->cr2; + (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; + + 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 %d, code = %x\n", + type, regs->err); + dump_ss(regs); + panic("trap"); + return 0; + } + +#if MACH_TTD + if (debug_all_traps_with_kttd && kttd_trap(type, regs->err, regs)) + return 0; +#endif /* MACH_TTD */ +#if MACH_KDB + if (debug_all_traps_with_kdb && + kdb_trap(type, regs->err, regs)) + return 0; +#endif MACH_KDB + + i386_exception(exc, code, subcode); + /*NOTREACHED*/ +} + +/* + * V86 mode assist for interrupt handling. + */ +boolean_t v86_assist_on = TRUE; +boolean_t v86_unsafe_ok = FALSE; +boolean_t v86_do_sti_cli = TRUE; +boolean_t v86_do_sti_immediate = FALSE; + +#define V86_IRET_PENDING 0x4000 + +int cli_count = 0; +int sti_count = 0; + +boolean_t +v86_assist(thread, regs) + thread_t thread; + register struct i386_saved_state *regs; +{ + register struct v86_assist_state *v86 = &thread->pcb->ims.v86s; + +/* + * Build an 8086 address. Use only when off is known to be 16 bits. + */ +#define Addr8086(seg,off) ((((seg) & 0xffff) << 4) + (off)) + +#define EFL_V86_SAFE ( EFL_OF | EFL_DF | EFL_TF \ + | EFL_SF | EFL_ZF | EFL_AF \ + | EFL_PF | EFL_CF ) + struct iret_32 { + int eip; + int cs; + int eflags; + }; + struct iret_16 { + unsigned short ip; + unsigned short cs; + unsigned short flags; + }; + union iret_struct { + struct iret_32 iret_32; + struct iret_16 iret_16; + }; + + struct int_vec { + unsigned short ip; + unsigned short cs; + }; + + if (!v86_assist_on) + return FALSE; + + /* + * If delayed STI pending, enable interrupts. + * Turn off tracing if on only to delay STI. + */ + if (v86->flags & V86_IF_PENDING) { + v86->flags &= ~V86_IF_PENDING; + v86->flags |= EFL_IF; + if ((v86->flags & EFL_TF) == 0) + regs->efl &= ~EFL_TF; + } + + if (regs->trapno == T_DEBUG) { + + if (v86->flags & EFL_TF) { + /* + * Trace flag was also set - it has priority + */ + return FALSE; /* handle as single-step */ + } + /* + * Fall through to check for interrupts. + */ + } + else if (regs->trapno == T_GENERAL_PROTECTION) { + /* + * General protection error - must be an 8086 instruction + * to emulate. + */ + register int eip; + boolean_t addr_32 = FALSE; + boolean_t data_32 = FALSE; + int io_port; + + /* + * Set up error handler for bad instruction/data + * fetches. + */ + asm("movl $(addr_error), %0" : "=m" (thread->recover)); + + eip = regs->eip; + while (TRUE) { + unsigned char opcode; + + if (eip > 0xFFFF) { + thread->recover = 0; + return FALSE; /* GP fault: IP out of range */ + } + + opcode = *(unsigned char *)Addr8086(regs->cs,eip); + eip++; + switch (opcode) { + case 0xf0: /* lock */ + case 0xf2: /* repne */ + case 0xf3: /* repe */ + case 0x2e: /* cs */ + case 0x36: /* ss */ + case 0x3e: /* ds */ + case 0x26: /* es */ + case 0x64: /* fs */ + case 0x65: /* gs */ + /* ignore prefix */ + continue; + + case 0x66: /* data size */ + data_32 = TRUE; + continue; + + case 0x67: /* address size */ + addr_32 = TRUE; + continue; + + case 0xe4: /* inb imm */ + case 0xe5: /* inw imm */ + case 0xe6: /* outb imm */ + case 0xe7: /* outw imm */ + io_port = *(unsigned char *)Addr8086(regs->cs, eip); + eip++; + goto do_in_out; + + case 0xec: /* inb dx */ + case 0xed: /* inw dx */ + case 0xee: /* outb dx */ + case 0xef: /* outw dx */ + case 0x6c: /* insb */ + case 0x6d: /* insw */ + case 0x6e: /* outsb */ + case 0x6f: /* outsw */ + io_port = regs->edx & 0xffff; + + do_in_out: + if (!data_32) + opcode |= 0x6600; /* word IO */ + + switch (emulate_io(regs, opcode, io_port)) { + case EM_IO_DONE: + /* instruction executed */ + break; + case EM_IO_RETRY: + /* port mapped, retry instruction */ + thread->recover = 0; + return TRUE; + case EM_IO_ERROR: + /* port not mapped */ + thread->recover = 0; + return FALSE; + } + break; + + case 0xfa: /* cli */ + if (!v86_do_sti_cli) { + thread->recover = 0; + return (FALSE); + } + + v86->flags &= ~EFL_IF; + /* disable simulated interrupts */ + cli_count++; + break; + + case 0xfb: /* sti */ + if (!v86_do_sti_cli) { + thread->recover = 0; + return (FALSE); + } + + if ((v86->flags & EFL_IF) == 0) { + if (v86_do_sti_immediate) { + v86->flags |= EFL_IF; + } else { + v86->flags |= V86_IF_PENDING; + regs->efl |= EFL_TF; + } + /* single step to set IF next inst. */ + } + sti_count++; + break; + + case 0x9c: /* pushf */ + { + int flags; + vm_offset_t sp; + int size; + + flags = regs->efl; + if ((v86->flags & EFL_IF) == 0) + flags &= ~EFL_IF; + + if ((v86->flags & EFL_TF) == 0) + flags &= ~EFL_TF; + else flags |= EFL_TF; + + sp = regs->uesp; + if (!addr_32) + sp &= 0xffff; + else if (sp > 0xffff) + goto stack_error; + size = (data_32) ? 4 : 2; + if (sp < size) + goto stack_error; + sp -= size; + if (copyout((char *)&flags, + (char *)Addr8086(regs->ss,sp), + size)) + goto addr_error; + if (addr_32) + regs->uesp = sp; + else + regs->uesp = (regs->uesp & 0xffff0000) | sp; + break; + } + + case 0x9d: /* popf */ + { + vm_offset_t sp; + int nflags; + + sp = regs->uesp; + if (!addr_32) + sp &= 0xffff; + else if (sp > 0xffff) + goto stack_error; + + if (data_32) { + if (sp > 0xffff - sizeof(int)) + goto stack_error; + nflags = *(int *)Addr8086(regs->ss,sp); + sp += sizeof(int); + } + else { + if (sp > 0xffff - sizeof(short)) + goto stack_error; + nflags = *(unsigned short *) + Addr8086(regs->ss,sp); + sp += sizeof(short); + } + if (addr_32) + regs->uesp = sp; + else + regs->uesp = (regs->uesp & 0xffff0000) | sp; + + if (v86->flags & V86_IRET_PENDING) { + v86->flags = nflags & (EFL_TF | EFL_IF); + v86->flags |= V86_IRET_PENDING; + } else { + v86->flags = nflags & (EFL_TF | EFL_IF); + } + regs->efl = (regs->efl & ~EFL_V86_SAFE) + | (nflags & EFL_V86_SAFE); + break; + } + case 0xcf: /* iret */ + { + vm_offset_t sp; + int nflags; + int size; + union iret_struct iret_struct; + + v86->flags &= ~V86_IRET_PENDING; + sp = regs->uesp; + if (!addr_32) + sp &= 0xffff; + else if (sp > 0xffff) + goto stack_error; + + if (data_32) { + if (sp > 0xffff - sizeof(struct iret_32)) + goto stack_error; + iret_struct.iret_32 = + *(struct iret_32 *) Addr8086(regs->ss,sp); + sp += sizeof(struct iret_32); + } + else { + if (sp > 0xffff - sizeof(struct iret_16)) + goto stack_error; + iret_struct.iret_16 = + *(struct iret_16 *) Addr8086(regs->ss,sp); + sp += sizeof(struct iret_16); + } + if (addr_32) + regs->uesp = sp; + else + regs->uesp = (regs->uesp & 0xffff0000) | sp; + + if (data_32) { + eip = iret_struct.iret_32.eip; + regs->cs = iret_struct.iret_32.cs & 0xffff; + nflags = iret_struct.iret_32.eflags; + } + else { + eip = iret_struct.iret_16.ip; + regs->cs = iret_struct.iret_16.cs; + nflags = iret_struct.iret_16.flags; + } + + v86->flags = nflags & (EFL_TF | EFL_IF); + regs->efl = (regs->efl & ~EFL_V86_SAFE) + | (nflags & EFL_V86_SAFE); + break; + } + default: + /* + * Instruction not emulated here. + */ + thread->recover = 0; + return FALSE; + } + break; /* exit from 'while TRUE' */ + } + regs->eip = (regs->eip & 0xffff0000 | eip); + } + else { + /* + * Not a trap we handle. + */ + thread->recover = 0; + return FALSE; + } + + if ((v86->flags & EFL_IF) && ((v86->flags & V86_IRET_PENDING)==0)) { + + struct v86_interrupt_table *int_table; + int int_count; + int vec; + int i; + + int_table = (struct v86_interrupt_table *) v86->int_table; + int_count = v86->int_count; + + vec = 0; + for (i = 0; i < int_count; int_table++, i++) { + if (!int_table->mask && int_table->count > 0) { + int_table->count--; + vec = int_table->vec; + break; + } + } + if (vec != 0) { + /* + * Take this interrupt + */ + vm_offset_t sp; + struct iret_16 iret_16; + struct int_vec int_vec; + + sp = regs->uesp & 0xffff; + if (sp < sizeof(struct iret_16)) + goto stack_error; + sp -= sizeof(struct iret_16); + iret_16.ip = regs->eip; + iret_16.cs = regs->cs; + iret_16.flags = regs->efl & 0xFFFF; + if ((v86->flags & EFL_TF) == 0) + iret_16.flags &= ~EFL_TF; + else iret_16.flags |= EFL_TF; + +#ifdef gcc_1_36_worked + int_vec = ((struct int_vec *)0)[vec]; +#else + bcopy((char *) (sizeof(struct int_vec) * vec), + (char *)&int_vec, + sizeof (struct int_vec)); +#endif + if (copyout((char *)&iret_16, + (char *)Addr8086(regs->ss,sp), + sizeof(struct iret_16))) + goto addr_error; + regs->uesp = (regs->uesp & 0xFFFF0000) | (sp & 0xffff); + regs->eip = int_vec.ip; + regs->cs = int_vec.cs; + regs->efl &= ~EFL_TF; + v86->flags &= ~(EFL_IF | EFL_TF); + v86->flags |= V86_IRET_PENDING; + } + } + + thread->recover = 0; + return TRUE; + + /* + * On address error, report a page fault. + * XXX report GP fault - we don`t save + * the faulting address. + */ + addr_error: + asm("addr_error:;"); + thread->recover = 0; + return FALSE; + + /* + * On stack address error, return stack fault (12). + */ + stack_error: + thread->recover = 0; + regs->trapno = T_STACK_FAULT; + return FALSE; +} + +/* + * Handle AST traps for i386. + * Check for delayed floating-point exception from + * AT-bus machines. + */ +void +i386_astintr() +{ + int mycpu = cpu_number(); + + (void) splsched(); /* block interrupts to check reasons */ + if (need_ast[mycpu] & AST_I386_FP) { + /* + * AST was for delayed floating-point exception - + * FP interrupt occured while in kernel. + * Turn off this AST reason and handle the FPU error. + */ + ast_off(mycpu, AST_I386_FP); + (void) spl0(); + + fpexterrflt(); + } + else { + /* + * 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(exc, code, subcode) + int exc; + int code; + int subcode; +{ + spl_t s; + + /* + * Turn off delayed FPU error handling. + */ + s = splsched(); + ast_off(cpu_number(), AST_I386_FP); + splx(s); + +#if FPE + fpe_exception_fixup(exc, code, subcode); +#else + exception(exc, code, subcode); +#endif + /*NOTREACHED*/ +} + +boolean_t +check_io_fault(regs) + struct i386_saved_state *regs; +{ + int eip, opcode, io_port; + boolean_t data_16 = FALSE; + + /* + * Get the instruction. + */ + eip = regs->eip; + + for (;;) { + opcode = inst_fetch(eip, regs->cs); + eip++; + switch (opcode) { + case 0x66: /* data-size prefix */ + data_16 = TRUE; + continue; + + case 0xf3: /* rep prefix */ + case 0x26: /* es */ + case 0x2e: /* cs */ + case 0x36: /* ss */ + case 0x3e: /* ds */ + case 0x64: /* fs */ + case 0x65: /* gs */ + continue; + + case 0xE4: /* inb imm */ + case 0xE5: /* inl imm */ + case 0xE6: /* outb imm */ + case 0xE7: /* outl imm */ + /* port is immediate byte */ + io_port = inst_fetch(eip, regs->cs); + eip++; + break; + + case 0xEC: /* inb dx */ + case 0xED: /* inl dx */ + case 0xEE: /* outb dx */ + case 0xEF: /* outl dx */ + case 0x6C: /* insb */ + case 0x6D: /* insl */ + case 0x6E: /* outsb */ + case 0x6F: /* outsl */ + /* port is in DX register */ + io_port = regs->edx & 0xFFFF; + break; + + default: + return FALSE; + } + break; + } + + if (data_16) + opcode |= 0x6600; /* word IO */ + + switch (emulate_io(regs, opcode, io_port)) { + case EM_IO_DONE: + /* instruction executed */ + regs->eip = eip; + return TRUE; + + case EM_IO_RETRY: + /* port mapped, retry instruction */ + return TRUE; + + case EM_IO_ERROR: + /* port not mapped */ + return FALSE; + } +} + +#if MACH_PCSAMPLE > 0 +/* + * return saved state for interrupted user thread + */ +unsigned +interrupted_pc(t) + thread_t t; +{ + register struct i386_saved_state *iss; + + iss = USER_REGS(t); + return iss->eip; +} +#endif /* MACH_PCSAMPLE > 0*/ + diff --git a/i386/i386/trap.h b/i386/i386/trap.h new file mode 100644 index 00000000..f4dcbd57 --- /dev/null +++ b/i386/i386/trap.h @@ -0,0 +1,38 @@ +/* + * 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 + +char *trap_name(unsigned int trapnum); + +#endif !ASSEMBLER + +#endif _I386_TRAP_H_ diff --git a/i386/i386/tss.h b/i386/i386/tss.h new file mode 100644 index 00000000..0d02f703 --- /dev/null +++ b/i386/i386/tss.h @@ -0,0 +1,76 @@ +/* + * 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 <mach/inline.h> + +/* + * i386 Task State Segment + */ +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 */ +}; + +/* Load the current task register. */ +MACH_INLINE void +ltr(unsigned short segment) +{ + __asm volatile("ltr %0" : : "r" (segment)); +} + +#endif /* _I386_TSS_H_ */ diff --git a/i386/i386/user_ldt.c b/i386/i386/user_ldt.c new file mode 100644 index 00000000..71ca08da --- /dev/null +++ b/i386/i386/user_ldt.c @@ -0,0 +1,389 @@ +/* + * 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 <kern/kalloc.h> +#include <kern/thread.h> + +#include <vm/vm_kern.h> + +#include <i386/seg.h> +#include <i386/thread.h> +#include <i386/user_ldt.h> +#include "ldt.h" + +char acc_type[8][3] = { + /* code stack data */ + { 0, 0, 1 }, /* data */ + { 0, 1, 1 }, /* data, writable */ + { 0, 0, 1 }, /* data, expand-down */ + { 0, 1, 1 }, /* data, writable, expand-down */ + { 1, 0, 0 }, /* code */ + { 1, 0, 1 }, /* code, readable */ + { 1, 0, 0 }, /* code, conforming */ + { 1, 0, 1 }, /* code, readable, conforming */ +}; + +boolean_t selector_check(thread, sel, type) + thread_t thread; + int sel; + int type; /* code, stack, data */ +{ + struct user_ldt *ldt; + int access; + + ldt = thread->pcb->ims.ldt; + if (ldt == 0) { + switch (type) { + case S_CODE: + return sel == USER_CS; + case S_STACK: + return sel == USER_DS; + case S_DATA: + return sel == 0 || + sel == USER_CS || + sel == USER_DS; + } + } + + if (type != S_DATA && sel == 0) + return FALSE; + if ((sel & (SEL_LDT|SEL_PL)) != (SEL_LDT|SEL_PL_U) + || sel > ldt->desc.limit_low) + return FALSE; + + access = ldt->ldt[sel_idx(sel)].access; + + if ((access & (ACC_P|ACC_PL|ACC_TYPE_USER)) + != (ACC_P|ACC_PL_U|ACC_TYPE_USER)) + return FALSE; + /* present, pl == pl.user, not system */ + + return acc_type[(access & 0xe)>>1][type]; +} + +/* + * Add the descriptors to the LDT, starting with + * the descriptor for 'first_selector'. + */ +kern_return_t +i386_set_ldt(thread, first_selector, desc_list, count, desc_list_inline) + thread_t thread; + int first_selector; + struct real_descriptor *desc_list; + unsigned int count; + boolean_t desc_list_inline; +{ + user_ldt_t new_ldt, old_ldt, cur_ldt; + struct real_descriptor *dp; + int i; + pcb_t pcb; + vm_size_t ldt_size_needed; + int first_desc = sel_idx(first_selector); + vm_map_copy_t old_copy_object; + + if (thread == THREAD_NULL) + return KERN_INVALID_ARGUMENT; + if (first_desc < 0 || 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); + 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 - 1); + + pcb = thread->pcb; + old_ldt = 0; /* the one to throw away */ + new_ldt = 0; /* the one to allocate */ + Retry: + simple_lock(&pcb->lock); + cur_ldt = pcb->ims.ldt; + if (cur_ldt == 0 || + cur_ldt->desc.limit_low + 1 < ldt_size_needed) + { + /* + * No current LDT, or not big enough + */ + if (new_ldt == 0) { + simple_unlock(&pcb->lock); + + new_ldt = (user_ldt_t) + kalloc(ldt_size_needed + + sizeof(struct real_descriptor)); + /* + * Build a descriptor that describes the + * LDT itself + */ + { + vm_offset_t ldt_base; + + ldt_base = (vm_offset_t) &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. Copy descriptors from current to new. + */ + if (cur_ldt) + bcopy((char *) &cur_ldt->ldt[0], + (char *) &new_ldt->ldt[0], + cur_ldt->desc.limit_low + 1); + + old_ldt = cur_ldt; /* discard old LDT */ + cur_ldt = new_ldt; /* use new LDT from now on */ + new_ldt = 0; /* keep new LDT */ + + pcb->ims.ldt = cur_ldt; /* set LDT for thread */ + } + + /* + * Install new descriptors. + */ + bcopy((char *) desc_list, + (char *) &cur_ldt->ldt[first_desc], + count * sizeof(struct real_descriptor)); + + simple_unlock(&pcb->lock); + + /* + * Discard old LDT if it was replaced + */ + if (old_ldt) + kfree((vm_offset_t)old_ldt, + old_ldt->desc.limit_low + 1 + + sizeof(struct real_descriptor)); + + /* + * Discard new LDT if it was not used + */ + if (new_ldt) + kfree((vm_offset_t)new_ldt, + new_ldt->desc.limit_low + 1 + + sizeof(struct real_descriptor)); + + /* + * 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(thread, first_selector, selector_count, desc_list, count) + thread_t thread; + int first_selector; + int selector_count; /* number wanted */ + struct real_descriptor **desc_list; /* in/out */ + unsigned int *count; /* in/out */ +{ + struct user_ldt *user_ldt; + pcb_t pcb = thread->pcb; + int first_desc = sel_idx(first_selector); + unsigned int 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; + + 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 + */ + bcopy((char *)&user_ldt[first_desc], + (char *)*desc_list, + 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) + bzero((char *)addr + ldt_size, 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) + user_ldt_t user_ldt; +{ + kfree((vm_offset_t)user_ldt, + user_ldt->desc.limit_low + 1 + + sizeof(struct real_descriptor)); +} diff --git a/i386/i386/user_ldt.h b/i386/i386/user_ldt.h new file mode 100644 index 00000000..9267ac7e --- /dev/null +++ b/i386/i386/user_ldt.h @@ -0,0 +1,55 @@ +/* + * 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 { + struct real_descriptor desc; /* descriptor for self */ + struct real_descriptor ldt[1]; /* descriptor table (variable) */ +}; +typedef struct user_ldt * user_ldt_t; + +/* + * Check code/stack/data selector values against LDT if present. + */ +#define S_CODE 0 /* code segment */ +#define S_STACK 1 /* stack segment */ +#define S_DATA 2 /* data segment */ + +extern boolean_t selector_check(/* thread_t thread, + int sel, + int type */); + +#endif /* _I386_USER_LDT_H_ */ diff --git a/i386/i386/vm_param.h b/i386/i386/vm_param.h new file mode 100644 index 00000000..30e9418e --- /dev/null +++ b/i386/i386/vm_param.h @@ -0,0 +1,64 @@ +/* + * 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_ + +/* XXX use xu/vm_param.h */ +#include <mach/vm_param.h> + +/* The kernel address space is 1GB, starting at virtual address 0. */ +#define VM_MIN_KERNEL_ADDRESS ((vm_offset_t) 0x00000000) +#define VM_MAX_KERNEL_ADDRESS ((vm_offset_t) 0x40000000) + +/* The kernel virtual address space is actually located + at high linear addresses. + This is the kernel address range in linear addresses. */ +#define LINEAR_MIN_KERNEL_ADDRESS ((vm_offset_t) 0xc0000000) +#define LINEAR_MAX_KERNEL_ADDRESS ((vm_offset_t) 0xffffffff) + +#define KERNEL_STACK_SIZE (1*I386_PGBYTES) +#define INTSTACK_SIZE (1*I386_PGBYTES) + /* 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 phys_mem_va. + */ +extern vm_offset_t phys_mem_va; +#define phystokv(a) ((vm_offset_t)(a) + phys_mem_va) + +/* + * Kernel virtual memory is actually at 0xc0000000 in linear addresses. + */ +#define kvtolin(a) ((vm_offset_t)(a) + LINEAR_MIN_KERNEL_ADDRESS) +#define lintokv(a) ((vm_offset_t)(a) - LINEAR_MIN_KERNEL_ADDRESS) + +#endif _I386_KERNEL_I386_VM_PARAM_ diff --git a/i386/i386/vm_tuning.h b/i386/i386/vm_tuning.h new file mode 100644 index 00000000..a5091fb7 --- /dev/null +++ b/i386/i386/vm_tuning.h @@ -0,0 +1,35 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * File: i386/vm_tuning.h + * + * VM tuning parameters for the i386 (without reference bits). + */ + +#ifndef _I386_VM_TUNING_H_ +#define _I386_VM_TUNING_H_ + +#endif _I386_VM_TUNING_H_ diff --git a/i386/i386/xpr.h b/i386/i386/xpr.h new file mode 100644 index 00000000..19ef026a --- /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/i386/zalloc.h b/i386/i386/zalloc.h new file mode 100644 index 00000000..bf7cf6b2 --- /dev/null +++ b/i386/i386/zalloc.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 1996-1994 The University of Utah and + * the Computer Systems Laboratory (CSL). All rights reserved. + * + * Permission to use, copy, modify and distribute this software is hereby + * granted provided that (1) source code retains these copyright, permission, + * and disclaimer notices, and (2) redistributions including binaries + * reproduce the notices in supporting documentation, and (3) all advertising + * materials mentioning features or use of this software display the following + * acknowledgement: ``This product includes software developed by the + * Computer Systems Laboratory at the University of Utah.'' + * + * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS + * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF + * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * CSL requests users of this software to return to csl-dist@cs.utah.edu any + * improvements that they make and grant CSL redistribution rights. + * + * Utah $Hdr: zalloc.h 1.4 94/12/16$ + * Author: Bryan Ford + */ + +#ifndef _I386_ZALLOC_H_ +#define _I386_ZALLOC_H_ + +#include <kern/zalloc.h> + +#endif /* _I386_ZALLOC_H_ */ |