diff --git a/sys/i386/i386/db_disasm.c b/sys/i386/i386/db_disasm.c index 2d5a65fe31d3..46b0a1b8a895 100644 --- a/sys/i386/i386/db_disasm.c +++ b/sys/i386/i386/db_disasm.c @@ -1,1513 +1,1503 @@ /*- * Mach Operating System * Copyright (c) 1991,1990 Carnegie Mellon University * All Rights Reserved. * * Permission to use, copy, modify and distribute this software and its * documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. */ #include __FBSDID("$FreeBSD$"); /* * Instruction disassembler. */ #include #include #include #include #include /* * Size attributes */ #define BYTE 0 #define WORD 1 #define LONG 2 #define QUAD 3 #define SNGL 4 #define DBLR 5 #define EXTR 6 #define SDEP 7 #define NONE 8 /* * Addressing modes */ #define E 1 /* general effective address */ #define Eind 2 /* indirect address (jump, call) */ #define Ew 3 /* address, word size */ #define Eb 4 /* address, byte size */ #define R 5 /* register, in 'reg' field */ #define Rw 6 /* word register, in 'reg' field */ #define Ri 7 /* register in instruction */ #define S 8 /* segment reg, in 'reg' field */ #define Si 9 /* segment reg, in instruction */ #define A 10 /* accumulator */ #define BX 11 /* (bx) */ #define CL 12 /* cl, for shifts */ #define DX 13 /* dx, for IO */ #define SI 14 /* si */ #define DI 15 /* di */ #define CR 16 /* control register */ #define DR 17 /* debug register */ #define TR 18 /* test register */ #define I 19 /* immediate, unsigned */ #define Is 20 /* immediate, signed */ #define Ib 21 /* byte immediate, unsigned */ #define Ibs 22 /* byte immediate, signed */ #define Iw 23 /* word immediate, unsigned */ #define O 25 /* direct address */ #define Db 26 /* byte displacement from EIP */ #define Dl 27 /* long displacement from EIP */ #define o1 28 /* constant 1 */ #define o3 29 /* constant 3 */ #define OS 30 /* immediate offset/segment */ #define ST 31 /* FP stack top */ #define STI 32 /* FP stack */ #define X 33 /* extended FP op */ #define XA 34 /* for 'fstcw %ax' */ #define El 35 /* address, long size */ #define Ril 36 /* long register in instruction */ #define Iba 37 /* byte immediate, don't print if 0xa */ struct inst { const char * i_name; /* name */ short i_has_modrm; /* has regmodrm byte */ short i_size; /* operand size */ int i_mode; /* addressing modes */ const void * 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 { const char * f_name; /* name for memory instruction */ int f_size; /* size for memory instruction */ int f_rrmode; /* mode for rr instruction */ const void * f_rrname; /* name for rr instruction (or pointer to table) */ }; static const char * const db_Grp6[] = { "sldt", "str", "lldt", "ltr", "verr", "verw", "", "" }; static const char * const db_Grp7[] = { "sgdt", "sidt", "lgdt", "lidt", "smsw", "", "lmsw", "invlpg" }; static const char * const db_Grp8[] = { "", "", "", "", "bt", "bts", "btr", "btc" }; static const char * const db_Grp9[] = { "", "cmpxchg8b", "", "", "", "", "", "" }; static const char * const db_Grp15[] = { "fxsave", "fxrstor", "ldmxcsr", "stmxcsr", "", "", "", "clflush" }; static const char * const db_Grp15b[] = { "", "", "", "", "", "lfence", "mfence", "sfence" }; static const struct inst db_inst_0f0x[] = { /*00*/ { "", TRUE, NONE, op1(Ew), db_Grp6 }, /*01*/ { "", TRUE, NONE, op1(Ew), db_Grp7 }, /*02*/ { "lar", TRUE, LONG, op2(E,R), 0 }, /*03*/ { "lsl", TRUE, LONG, op2(E,R), 0 }, /*04*/ { "", FALSE, NONE, 0, 0 }, /*05*/ { "syscall",FALSE,NONE, 0, 0 }, /*06*/ { "clts", FALSE, NONE, 0, 0 }, /*07*/ { "sysret",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 }, }; static const struct inst db_inst_0f1x[] = { /*10*/ { "", FALSE, NONE, 0, 0 }, /*11*/ { "", FALSE, NONE, 0, 0 }, /*12*/ { "", FALSE, NONE, 0, 0 }, /*13*/ { "", FALSE, NONE, 0, 0 }, /*14*/ { "", FALSE, NONE, 0, 0 }, /*15*/ { "", FALSE, NONE, 0, 0 }, /*16*/ { "", FALSE, NONE, 0, 0 }, /*17*/ { "", FALSE, NONE, 0, 0 }, /*18*/ { "", FALSE, NONE, 0, 0 }, /*19*/ { "", FALSE, NONE, 0, 0 }, /*1a*/ { "", FALSE, NONE, 0, 0 }, /*1b*/ { "", FALSE, NONE, 0, 0 }, /*1c*/ { "", FALSE, NONE, 0, 0 }, /*1d*/ { "", FALSE, NONE, 0, 0 }, /*1e*/ { "", FALSE, NONE, 0, 0 }, /*1f*/ { "nopl", TRUE, SDEP, 0, "nopw" }, }; static const struct inst db_inst_0f2x[] = { /*20*/ { "mov", TRUE, LONG, op2(CR,El), 0 }, /*21*/ { "mov", TRUE, LONG, op2(DR,El), 0 }, /*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 }, }; static const struct inst db_inst_0f3x[] = { /*30*/ { "wrmsr", FALSE, NONE, 0, 0 }, /*31*/ { "rdtsc", FALSE, NONE, 0, 0 }, /*32*/ { "rdmsr", FALSE, NONE, 0, 0 }, /*33*/ { "rdpmc", FALSE, NONE, 0, 0 }, /*34*/ { "sysenter",FALSE,NONE, 0, 0 }, /*35*/ { "sysexit",FALSE,NONE, 0, 0 }, /*36*/ { "", FALSE, NONE, 0, 0 }, /*37*/ { "getsec",FALSE, NONE, 0, 0 }, /*38*/ { "", FALSE, NONE, 0, 0 }, /*39*/ { "", FALSE, NONE, 0, 0 }, /*3a*/ { "", FALSE, NONE, 0, 0 }, /*3b*/ { "", FALSE, NONE, 0, 0 }, /*3c*/ { "", FALSE, NONE, 0, 0 }, /*3d*/ { "", FALSE, NONE, 0, 0 }, /*3e*/ { "", FALSE, NONE, 0, 0 }, /*3f*/ { "", FALSE, NONE, 0, 0 }, }; static const struct inst db_inst_0f4x[] = { /*40*/ { "cmovo", TRUE, NONE, op2(E, R), 0 }, /*41*/ { "cmovno", TRUE, NONE, op2(E, R), 0 }, /*42*/ { "cmovb", TRUE, NONE, op2(E, R), 0 }, /*43*/ { "cmovnb", TRUE, NONE, op2(E, R), 0 }, /*44*/ { "cmovz", TRUE, NONE, op2(E, R), 0 }, /*45*/ { "cmovnz", TRUE, NONE, op2(E, R), 0 }, /*46*/ { "cmovbe", TRUE, NONE, op2(E, R), 0 }, /*47*/ { "cmovnbe",TRUE, NONE, op2(E, R), 0 }, /*48*/ { "cmovs", TRUE, NONE, op2(E, R), 0 }, /*49*/ { "cmovns", TRUE, NONE, op2(E, R), 0 }, /*4a*/ { "cmovp", TRUE, NONE, op2(E, R), 0 }, /*4b*/ { "cmovnp", TRUE, NONE, op2(E, R), 0 }, /*4c*/ { "cmovl", TRUE, NONE, op2(E, R), 0 }, /*4d*/ { "cmovnl", TRUE, NONE, op2(E, R), 0 }, /*4e*/ { "cmovle", TRUE, NONE, op2(E, R), 0 }, /*4f*/ { "cmovnle",TRUE, NONE, op2(E, R), 0 }, }; static const 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 }, }; static const 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 }, }; static const struct inst db_inst_0fax[] = { /*a0*/ { "push", FALSE, NONE, op1(Si), 0 }, /*a1*/ { "pop", FALSE, NONE, op1(Si), 0 }, /*a2*/ { "cpuid", FALSE, NONE, 0, 0 }, /*a3*/ { "bt", TRUE, LONG, op2(R,E), 0 }, /*a4*/ { "shld", TRUE, LONG, op3(Ib,R,E), 0 }, /*a5*/ { "shld", TRUE, LONG, op3(CL,R,E), 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*/ { "rsm", FALSE, NONE, 0, 0 }, /*ab*/ { "bts", TRUE, LONG, op2(R,E), 0 }, /*ac*/ { "shrd", TRUE, LONG, op3(Ib,R,E), 0 }, /*ad*/ { "shrd", TRUE, LONG, op3(CL,R,E), 0 }, /*ae*/ { "", TRUE, LONG, op1(E), db_Grp15 }, /*af*/ { "imul", TRUE, LONG, op2(E,R), 0 }, }; static const struct inst db_inst_0fbx[] = { /*b0*/ { "cmpxchg",TRUE, BYTE, op2(R, E), 0 }, /*b0*/ { "cmpxchg",TRUE, LONG, op2(R, E), 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(Ib, E), 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 }, }; static const 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*/ { "", TRUE, NONE, op1(E), db_Grp9 }, /*c8*/ { "bswap", FALSE, LONG, op1(Ril), 0 }, /*c9*/ { "bswap", FALSE, LONG, op1(Ril), 0 }, /*ca*/ { "bswap", FALSE, LONG, op1(Ril), 0 }, /*cb*/ { "bswap", FALSE, LONG, op1(Ril), 0 }, /*cc*/ { "bswap", FALSE, LONG, op1(Ril), 0 }, /*cd*/ { "bswap", FALSE, LONG, op1(Ril), 0 }, /*ce*/ { "bswap", FALSE, LONG, op1(Ril), 0 }, /*cf*/ { "bswap", FALSE, LONG, op1(Ril), 0 }, }; static const struct inst * const db_inst_0f[] = { db_inst_0f0x, db_inst_0f1x, db_inst_0f2x, db_inst_0f3x, db_inst_0f4x, 0, 0, 0, db_inst_0f8x, db_inst_0f9x, db_inst_0fax, db_inst_0fbx, db_inst_0fcx, 0, 0, 0 }; static const char * const db_Esc92[] = { "fnop", "", "", "", "", "", "", "" }; static const char * const db_Esc94[] = { "fchs", "fabs", "", "", "ftst", "fxam", "", "" }; static const char * const db_Esc95[] = { "fld1", "fldl2t","fldl2e","fldpi","fldlg2","fldln2","fldz","" }; static const char * const db_Esc96[] = { "f2xm1","fyl2x","fptan","fpatan","fxtract","fprem1","fdecstp", "fincstp" }; static const char * const db_Esc97[] = { "fprem","fyl2xp1","fsqrt","fsincos","frndint","fscale","fsin","fcos" }; static const char * const db_Esca5[] = { "", "fucompp","", "", "", "", "", "" }; static const char * const db_Escb4[] = { "fneni","fndisi", "fnclex","fninit","fsetpm", "", "", "" }; static const char * const db_Esce3[] = { "", "fcompp","", "", "", "", "", "" }; static const char * const db_Escf4[] = { "fnstsw","", "", "", "", "", "", "" }; static const 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 }, }; static const struct finst db_Esc9[] = { /*0*/ { "fld", SNGL, op1(STI), 0 }, /*1*/ { "", NONE, op1(STI), "fxch" }, /*2*/ { "fst", SNGL, op1(X), db_Esc92 }, /*3*/ { "fstp", SNGL, 0, 0 }, /*4*/ { "fldenv", NONE, op1(X), db_Esc94 }, /*5*/ { "fldcw", NONE, op1(X), db_Esc95 }, /*6*/ { "fnstenv",NONE, op1(X), db_Esc96 }, /*7*/ { "fnstcw", NONE, op1(X), db_Esc97 }, }; static const struct finst db_Esca[] = { /*0*/ { "fiadd", LONG, 0, 0 }, /*1*/ { "fimul", LONG, 0, 0 }, /*2*/ { "ficom", LONG, 0, 0 }, /*3*/ { "ficomp", LONG, 0, 0 }, /*4*/ { "fisub", LONG, 0, 0 }, /*5*/ { "fisubr", LONG, op1(X), db_Esca5 }, /*6*/ { "fidiv", LONG, 0, 0 }, /*7*/ { "fidivr", LONG, 0, 0 } }; static const struct finst db_Escb[] = { /*0*/ { "fild", LONG, 0, 0 }, /*1*/ { "", NONE, 0, 0 }, /*2*/ { "fist", LONG, 0, 0 }, /*3*/ { "fistp", LONG, 0, 0 }, /*4*/ { "", WORD, op1(X), db_Escb4 }, /*5*/ { "fld", EXTR, 0, 0 }, /*6*/ { "", WORD, 0, 0 }, /*7*/ { "fstp", EXTR, 0, 0 }, }; static const struct finst db_Escc[] = { /*0*/ { "fadd", DBLR, op2(ST,STI), 0 }, /*1*/ { "fmul", DBLR, op2(ST,STI), 0 }, /*2*/ { "fcom", DBLR, 0, 0 }, /*3*/ { "fcomp", DBLR, 0, 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" }, }; static const 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 }, }; static const struct finst db_Esce[] = { /*0*/ { "fiadd", WORD, op2(ST,STI), "faddp" }, /*1*/ { "fimul", WORD, op2(ST,STI), "fmulp" }, /*2*/ { "ficom", WORD, 0, 0 }, /*3*/ { "ficomp", WORD, op1(X), db_Esce3 }, /*4*/ { "fisub", WORD, op2(ST,STI), "fsubrp" }, /*5*/ { "fisubr", WORD, op2(ST,STI), "fsubp" }, /*6*/ { "fidiv", WORD, op2(ST,STI), "fdivrp" }, /*7*/ { "fidivr", WORD, op2(ST,STI), "fdivp" }, }; static const struct finst db_Escf[] = { /*0*/ { "fild", WORD, 0, 0 }, /*1*/ { "", NONE, 0, 0 }, /*2*/ { "fist", WORD, 0, 0 }, /*3*/ { "fistp", WORD, 0, 0 }, /*4*/ { "fbld", NONE, op1(XA), db_Escf4 }, /*5*/ { "fild", QUAD, 0, 0 }, /*6*/ { "fbstp", NONE, 0, 0 }, /*7*/ { "fistp", QUAD, 0, 0 }, }; static const struct finst * const db_Esc_inst[] = { db_Esc8, db_Esc9, db_Esca, db_Escb, db_Escc, db_Escd, db_Esce, db_Escf }; static const char * const db_Grp1[] = { "add", "or", "adc", "sbb", "and", "sub", "xor", "cmp" }; static const char * const db_Grp2[] = { "rol", "ror", "rcl", "rcr", "shl", "shr", "shl", "sar" }; static const 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 }, }; static const 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 } }; static const struct inst db_Grp5[] = { { "inc", TRUE, LONG, op1(E), 0 }, { "dec", TRUE, LONG, op1(E), 0 }, { "call", TRUE, LONG, op1(Eind),0 }, { "lcall", TRUE, LONG, op1(Eind),0 }, { "jmp", TRUE, LONG, op1(Eind),0 }, { "ljmp", TRUE, LONG, op1(Eind),0 }, { "push", TRUE, LONG, op1(E), 0 }, { "", TRUE, NONE, 0, 0 } }; static const 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(I, 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(I, 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(I, 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*/ { "daa", 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(I, 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*/ { "aaa", 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(I, 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(Rw,Ew), 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(Ibs), 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), db_Grp1 }, /*81*/ { "", TRUE, LONG, op2(I, E), db_Grp1 }, /*82*/ { "", TRUE, BYTE, op2(I, E), db_Grp1 }, /*83*/ { "", TRUE, LONG, op2(Ibs,E), db_Grp1 }, /*84*/ { "test", TRUE, BYTE, op2(R, E), 0 }, /*85*/ { "test", TRUE, LONG, op2(R, E), 0 }, /*86*/ { "xchg", TRUE, BYTE, op2(R, E), 0 }, /*87*/ { "xchg", TRUE, LONG, op2(R, E), 0 }, /*88*/ { "mov", TRUE, BYTE, op2(R, E), 0 }, /*89*/ { "mov", TRUE, LONG, op2(R, E), 0 }, /*8a*/ { "mov", TRUE, BYTE, op2(E, R), 0 }, /*8b*/ { "mov", TRUE, LONG, op2(E, R), 0 }, /*8c*/ { "mov", TRUE, NONE, op2(S, Ew), 0 }, /*8d*/ { "lea", TRUE, LONG, op2(E, R), 0 }, /*8e*/ { "mov", TRUE, NONE, op2(Ew, S), 0 }, /*8f*/ { "pop", TRUE, LONG, op1(E), 0 }, /*90*/ { "nop", FALSE, NONE, 0, 0 }, /*91*/ { "xchg", FALSE, LONG, op2(A, Ri), 0 }, /*92*/ { "xchg", FALSE, LONG, op2(A, Ri), 0 }, /*93*/ { "xchg", FALSE, LONG, op2(A, Ri), 0 }, /*94*/ { "xchg", FALSE, LONG, op2(A, Ri), 0 }, /*95*/ { "xchg", FALSE, LONG, op2(A, Ri), 0 }, /*96*/ { "xchg", FALSE, LONG, op2(A, Ri), 0 }, /*97*/ { "xchg", FALSE, LONG, op2(A, Ri), 0 }, /*98*/ { "cbw", FALSE, SDEP, 0, "cwde" }, /* cbw/cwde */ /*99*/ { "cwd", FALSE, SDEP, 0, "cdq" }, /* cwd/cdq */ /*9a*/ { "lcall", FALSE, NONE, op1(OS), 0 }, /*9b*/ { "wait", FALSE, NONE, 0, 0 }, /*9c*/ { "pushf", FALSE, LONG, 0, 0 }, /*9d*/ { "popf", FALSE, LONG, 0, 0 }, /*9e*/ { "sahf", FALSE, NONE, 0, 0 }, /*9f*/ { "lahf", FALSE, NONE, 0, 0 }, /*a0*/ { "mov", FALSE, BYTE, op2(O, A), 0 }, /*a1*/ { "mov", FALSE, LONG, op2(O, A), 0 }, /*a2*/ { "mov", FALSE, BYTE, op2(A, O), 0 }, /*a3*/ { "mov", FALSE, LONG, op2(A, O), 0 }, /*a4*/ { "movs", FALSE, BYTE, op2(SI,DI), 0 }, /*a5*/ { "movs", FALSE, LONG, op2(SI,DI), 0 }, /*a6*/ { "cmps", FALSE, BYTE, op2(SI,DI), 0 }, /*a7*/ { "cmps", FALSE, LONG, op2(SI,DI), 0 }, /*a8*/ { "test", FALSE, BYTE, op2(I, A), 0 }, /*a9*/ { "test", FALSE, LONG, op2(I, A), 0 }, /*aa*/ { "stos", FALSE, BYTE, op1(DI), 0 }, /*ab*/ { "stos", FALSE, LONG, op1(DI), 0 }, /*ac*/ { "lods", FALSE, BYTE, op1(SI), 0 }, /*ad*/ { "lods", FALSE, LONG, op1(SI), 0 }, /*ae*/ { "scas", FALSE, BYTE, op1(SI), 0 }, /*af*/ { "scas", FALSE, LONG, op1(SI), 0 }, /*b0*/ { "mov", FALSE, BYTE, op2(I, Ri), 0 }, /*b1*/ { "mov", FALSE, BYTE, op2(I, Ri), 0 }, /*b2*/ { "mov", FALSE, BYTE, op2(I, Ri), 0 }, /*b3*/ { "mov", FALSE, BYTE, op2(I, Ri), 0 }, /*b4*/ { "mov", FALSE, BYTE, op2(I, Ri), 0 }, /*b5*/ { "mov", FALSE, BYTE, op2(I, Ri), 0 }, /*b6*/ { "mov", FALSE, BYTE, op2(I, Ri), 0 }, /*b7*/ { "mov", FALSE, BYTE, op2(I, Ri), 0 }, /*b8*/ { "mov", FALSE, LONG, op2(I, Ri), 0 }, /*b9*/ { "mov", FALSE, LONG, op2(I, Ri), 0 }, /*ba*/ { "mov", FALSE, LONG, op2(I, Ri), 0 }, /*bb*/ { "mov", FALSE, LONG, op2(I, Ri), 0 }, /*bc*/ { "mov", FALSE, LONG, op2(I, Ri), 0 }, /*bd*/ { "mov", FALSE, LONG, op2(I, Ri), 0 }, /*be*/ { "mov", FALSE, LONG, op2(I, Ri), 0 }, /*bf*/ { "mov", FALSE, LONG, op2(I, Ri), 0 }, /*c0*/ { "", TRUE, BYTE, op2(Ib, E), db_Grp2 }, /*c1*/ { "", TRUE, LONG, op2(Ib, E), 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(Iw, Ib), 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), db_Grp2 }, /*d1*/ { "", TRUE, LONG, op2(o1, E), db_Grp2 }, /*d2*/ { "", TRUE, BYTE, op2(CL, E), db_Grp2 }, /*d3*/ { "", TRUE, LONG, op2(CL, E), db_Grp2 }, /*d4*/ { "aam", FALSE, NONE, op1(Iba), 0 }, /*d5*/ { "aad", FALSE, NONE, op1(Iba), 0 }, /*d6*/ { ".byte\t0xd6", FALSE, NONE, 0, 0 }, /*d7*/ { "xlat", FALSE, BYTE, op1(BX), 0 }, /*d8*/ { "", TRUE, NONE, 0, db_Esc8 }, /*d9*/ { "", TRUE, NONE, 0, db_Esc9 }, /*da*/ { "", TRUE, NONE, 0, db_Esca }, /*db*/ { "", TRUE, NONE, 0, db_Escb }, /*dc*/ { "", TRUE, NONE, 0, db_Escc }, /*dd*/ { "", TRUE, NONE, 0, db_Escd }, /*de*/ { "", TRUE, NONE, 0, db_Esce }, /*df*/ { "", TRUE, NONE, 0, 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*/ { ".byte\t0xf1", 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, db_Grp3 }, /*f7*/ { "", TRUE, LONG, 0, 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, db_Grp4 }, /*ff*/ { "", TRUE, NONE, 0, db_Grp5 }, }; static const 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; const char * base; const char * index; int ss; bool defss; /* set if %ss is the default segment */ }; static const char * const db_index_reg_16[8] = { "%bx,%si", "%bx,%di", "%bp,%si", "%bp,%di", "%si", "%di", "%bp", "%bx" }; static const char * const 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" } }; static const char * const db_seg_reg[8] = { "%es", "%cs", "%ss", "%ds", "%fs", "%gs", "", "" }; /* * lengths for size attributes */ static const int db_lengths[] = { 1, /* BYTE */ 2, /* WORD */ 4, /* LONG */ 8, /* QUAD */ 4, /* SNGL */ 8, /* DBLR */ 10, /* EXTR */ }; #define get_value_inc(result, loc, size, is_signed) \ result = db_get_value((loc), (size), (is_signed)); \ (loc) += (size); static db_addr_t db_disasm_esc(db_addr_t loc, int inst, int short_addr, int size, const char *seg); static void db_print_address(const char *seg, int size, struct i_addr *addrp); static db_addr_t db_read_address(db_addr_t loc, int short_addr, int regmodrm, struct i_addr *addrp); /* * Read address at location and return updated location. */ static db_addr_t -db_read_address(loc, short_addr, regmodrm, addrp) - db_addr_t loc; - int short_addr; - int regmodrm; - struct i_addr * addrp; /* out */ +db_read_address(db_addr_t loc, int short_addr, int regmodrm, + struct i_addr *addrp) { 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 = NULL; addrp->ss = 0; addrp->defss = FALSE; if (short_addr) { if (rm == 2 || rm == 3 || (rm == 6 && mod != 0)) addrp->defss = TRUE; switch (mod) { case 0: if (rm == 6) { get_value_inc(disp, loc, 2, FALSE); addrp->disp = disp; addrp->base = NULL; } else { addrp->disp = 0; addrp->base = db_index_reg_16[rm]; } break; case 1: get_value_inc(disp, loc, 1, TRUE); disp &= 0xFFFF; addrp->disp = disp; addrp->base = db_index_reg_16[rm]; break; case 2: get_value_inc(disp, loc, 2, FALSE); addrp->disp = disp; addrp->base = db_index_reg_16[rm]; break; } } else { if (rm == 4) { get_value_inc(sib, loc, 1, FALSE); rm = sib_base(sib); index = sib_index(sib); if (index != 4) addrp->index = db_reg[LONG][index]; addrp->ss = sib_ss(sib); } switch (mod) { case 0: if (rm == 5) { get_value_inc(addrp->disp, loc, 4, FALSE); addrp->base = NULL; } else { addrp->disp = 0; addrp->base = db_reg[LONG][rm]; } break; case 1: get_value_inc(disp, loc, 1, TRUE); addrp->disp = disp; addrp->base = db_reg[LONG][rm]; break; case 2: get_value_inc(disp, loc, 4, FALSE); addrp->disp = disp; addrp->base = db_reg[LONG][rm]; break; } } return (loc); } static void -db_print_address(seg, size, addrp) - const char * seg; - int size; - struct i_addr * addrp; +db_print_address(const char *seg, int size, struct i_addr *addrp) { if (addrp->is_reg) { db_printf("%s", db_reg[size][addrp->disp]); return; } if (seg) { db_printf("%s:", seg); } else if (addrp->defss) { db_printf("%%ss:"); } db_printsym((db_addr_t)addrp->disp, DB_STGY_ANY); if (addrp->base != NULL || addrp->index != NULL) { db_printf("("); if (addrp->base) db_printf("%s", addrp->base); if (addrp->index) db_printf(",%s,%d", addrp->index, 1<ss); db_printf(")"); } } /* * Disassemble floating-point ("escape") instruction * and return updated location. */ static db_addr_t -db_disasm_esc(loc, inst, short_addr, size, seg) - db_addr_t loc; - int inst; - int short_addr; - int size; - const char * seg; +db_disasm_esc(db_addr_t loc, int inst, int short_addr, int size, + const char *seg) { int regmodrm; const struct finst * fp; int mod; struct i_addr address; const char * name; get_value_inc(regmodrm, loc, 1, FALSE); fp = &db_Esc_inst[inst - 0xd8][f_reg(regmodrm)]; mod = f_mod(regmodrm); if (mod != 3) { if (*fp->f_name == '\0') { db_printf(""); return (loc); } /* * Normal address modes. */ loc = db_read_address(loc, short_addr, regmodrm, &address); db_printf("%s", fp->f_name); switch(fp->f_size) { case SNGL: db_printf("s"); break; case DBLR: db_printf("l"); break; case EXTR: db_printf("t"); break; case WORD: db_printf("s"); break; case LONG: db_printf("l"); break; case QUAD: db_printf("q"); break; default: break; } db_printf("\t"); db_print_address(seg, BYTE, &address); } else { /* * 'reg-reg' - special formats */ switch (fp->f_rrmode) { case op2(ST,STI): name = (fp->f_rrname) ? fp->f_rrname : fp->f_name; db_printf("%s\t%%st,%%st(%d)",name,f_rm(regmodrm)); break; case op2(STI,ST): name = (fp->f_rrname) ? fp->f_rrname : fp->f_name; db_printf("%s\t%%st(%d),%%st",name, f_rm(regmodrm)); break; case op1(STI): name = (fp->f_rrname) ? fp->f_rrname : fp->f_name; db_printf("%s\t%%st(%d)",name, f_rm(regmodrm)); break; case op1(X): name = ((const char * const *)fp->f_rrname)[f_rm(regmodrm)]; if (*name == '\0') goto bad; db_printf("%s", name); break; case op1(XA): name = ((const char * const *)fp->f_rrname)[f_rm(regmodrm)]; if (*name == '\0') goto bad; db_printf("%s\t%%ax", name); break; default: bad: db_printf(""); break; } } return (loc); } /* * Disassemble instruction at 'loc'. 'altfmt' specifies an * (optional) alternate format. Return address of start of * next instruction. */ db_addr_t db_disasm(db_addr_t loc, bool altfmt) { int inst; int size; int short_addr; const char * seg; const struct inst * ip; const char * i_name; int i_size; int i_mode; int regmodrm = 0; bool first; int displ; int prefix; int rep; int imm; int imm2; int len; struct i_addr address; if (db_segsize(kdb_frame) == 16) altfmt = !altfmt; get_value_inc(inst, loc, 1, FALSE); if (altfmt) { short_addr = TRUE; size = WORD; } else { short_addr = FALSE; size = LONG; } seg = NULL; /* * Get prefixes */ rep = FALSE; prefix = TRUE; do { switch (inst) { case 0x66: size = (altfmt ? LONG : WORD); break; case 0x67: short_addr = !altfmt; 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: rep = TRUE; break; default: prefix = FALSE; break; } if (prefix) { get_value_inc(inst, loc, 1, FALSE); } if (rep == TRUE) { if (inst == 0x90) { db_printf("pause\n"); return (loc); } db_printf("repe "); /* XXX repe VS rep */ rep = FALSE; } } while (prefix); if (inst >= 0xd8 && inst <= 0xdf) { loc = db_disasm_esc(loc, inst, short_addr, size, seg); db_printf("\n"); return (loc); } if (inst == 0x0f) { get_value_inc(inst, loc, 1, FALSE); ip = db_inst_0f[inst>>4]; if (ip == NULL) { ip = &db_bad_inst; } else { ip = &ip[inst&0xf]; } } else ip = &db_inst_table[inst]; if (ip->i_has_modrm) { get_value_inc(regmodrm, loc, 1, FALSE); loc = db_read_address(loc, short_addr, regmodrm, &address); } i_name = ip->i_name; i_size = ip->i_size; i_mode = ip->i_mode; if (ip->i_extra == db_Grp1 || ip->i_extra == db_Grp2 || ip->i_extra == db_Grp6 || ip->i_extra == db_Grp7 || ip->i_extra == db_Grp8 || ip->i_extra == db_Grp9 || ip->i_extra == db_Grp15) { i_name = ((const char * const *)ip->i_extra)[f_reg(regmodrm)]; } else if (ip->i_extra == db_Grp3) { ip = ip->i_extra; ip = &ip[f_reg(regmodrm)]; i_name = ip->i_name; i_mode = ip->i_mode; } else if (ip->i_extra == db_Grp4 || ip->i_extra == db_Grp5) { ip = ip->i_extra; ip = &ip[f_reg(regmodrm)]; i_name = ip->i_name; i_mode = ip->i_mode; i_size = ip->i_size; } /* Special cases that don't fit well in the tables. */ if (ip->i_extra == db_Grp7 && f_mod(regmodrm) == 3) { switch (regmodrm) { case 0xc8: i_name = "monitor"; i_size = NONE; i_mode = 0; break; case 0xc9: i_name = "mwait"; i_size = NONE; i_mode = 0; break; } } if (ip->i_extra == db_Grp15 && f_mod(regmodrm) == 3) { i_name = db_Grp15b[f_reg(regmodrm)]; i_size = NONE; i_mode = 0; } if (i_size == SDEP) { if (size == WORD) db_printf("%s", i_name); else db_printf("%s", (const char *)ip->i_extra); } else { db_printf("%s", i_name); if (i_size != NONE) { if (i_size == BYTE) { db_printf("b"); size = BYTE; } else if (i_size == WORD) { db_printf("w"); size = WORD; } else if (size == WORD) db_printf("w"); else db_printf("l"); } } db_printf("\t"); for (first = true; i_mode != 0; i_mode >>= 8, first = false) { if (!first) db_printf(","); switch (i_mode & 0xFF) { case E: db_print_address(seg, size, &address); break; case Eind: db_printf("*"); db_print_address(seg, size, &address); break; case El: db_print_address(seg, LONG, &address); break; case Ew: db_print_address(seg, WORD, &address); break; case Eb: db_print_address(seg, BYTE, &address); break; case R: db_printf("%s", db_reg[size][f_reg(regmodrm)]); break; case Rw: db_printf("%s", db_reg[WORD][f_reg(regmodrm)]); break; case Ri: db_printf("%s", db_reg[size][f_rm(inst)]); break; case Ril: db_printf("%s", db_reg[LONG][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); db_printf("$%#r", imm); break; case Is: len = db_lengths[size]; get_value_inc(imm, loc, len, FALSE); db_printf("$%+#r", imm); break; case Ib: get_value_inc(imm, loc, 1, FALSE); db_printf("$%#r", imm); break; case Iba: get_value_inc(imm, loc, 1, FALSE); if (imm != 0x0a) db_printf("$%#r", imm); break; case Ibs: get_value_inc(imm, loc, 1, TRUE); if (size == WORD) imm &= 0xFFFF; db_printf("$%+#r", imm); break; case Iw: get_value_inc(imm, loc, 2, FALSE); db_printf("$%#r", imm); break; case O: len = (short_addr ? 2 : 4); get_value_inc(displ, loc, len, FALSE); if (seg) db_printf("%s:%+#r",seg, displ); else db_printsym((db_addr_t)displ, DB_STGY_ANY); break; case Db: get_value_inc(displ, loc, 1, TRUE); displ += loc; if (size == WORD) displ &= 0xFFFF; db_printsym((db_addr_t)displ, DB_STGY_XTRN); break; case Dl: len = db_lengths[size]; get_value_inc(displ, loc, len, FALSE); displ += loc; if (size == WORD) displ &= 0xFFFF; db_printsym((db_addr_t)displ, DB_STGY_XTRN); break; case o1: db_printf("$1"); break; case o3: db_printf("$3"); break; case OS: len = db_lengths[size]; get_value_inc(imm, loc, len, FALSE); /* offset */ get_value_inc(imm2, loc, 2, FALSE); /* segment */ db_printf("$%#r,%#r", imm2, imm); break; } } db_printf("\n"); return (loc); } diff --git a/sys/i386/i386/db_trace.c b/sys/i386/i386/db_trace.c index 47057906fa58..cc386cb88a8c 100644 --- a/sys/i386/i386/db_trace.c +++ b/sys/i386/i386/db_trace.c @@ -1,596 +1,590 @@ /*- * Mach Operating System * Copyright (c) 1991,1990 Carnegie Mellon University * All Rights Reserved. * * Permission to use, copy, modify and distribute this software and its * documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static db_varfcn_t db_esp; static db_varfcn_t db_frame; static db_varfcn_t db_frame_seg; static db_varfcn_t db_gs; static db_varfcn_t db_ss; /* * Machine register set. */ #define DB_OFFSET(x) (db_expr_t *)offsetof(struct trapframe, x) struct db_variable db_regs[] = { { "cs", DB_OFFSET(tf_cs), db_frame_seg }, { "ds", DB_OFFSET(tf_ds), db_frame_seg }, { "es", DB_OFFSET(tf_es), db_frame_seg }, { "fs", DB_OFFSET(tf_fs), db_frame_seg }, { "gs", NULL, db_gs }, { "ss", NULL, db_ss }, { "eax", DB_OFFSET(tf_eax), db_frame }, { "ecx", DB_OFFSET(tf_ecx), db_frame }, { "edx", DB_OFFSET(tf_edx), db_frame }, { "ebx", DB_OFFSET(tf_ebx), db_frame }, { "esp", NULL, db_esp }, { "ebp", DB_OFFSET(tf_ebp), db_frame }, { "esi", DB_OFFSET(tf_esi), db_frame }, { "edi", DB_OFFSET(tf_edi), db_frame }, { "eip", DB_OFFSET(tf_eip), db_frame }, { "efl", DB_OFFSET(tf_eflags), db_frame }, }; struct db_variable *db_eregs = db_regs + nitems(db_regs); static __inline int get_esp(struct trapframe *tf) { return (TF_HAS_STACKREGS(tf) ? tf->tf_esp : (intptr_t)&tf->tf_esp); } static int db_frame(struct db_variable *vp, db_expr_t *valuep, int op) { int *reg; if (kdb_frame == NULL) return (0); reg = (int *)((uintptr_t)kdb_frame + (db_expr_t)vp->valuep); if (op == DB_VAR_GET) *valuep = *reg; else *reg = *valuep; return (1); } static int db_frame_seg(struct db_variable *vp, db_expr_t *valuep, int op) { struct trapframe_vm86 *tfp; int off; uint16_t *reg; if (kdb_frame == NULL) return (0); off = (intptr_t)vp->valuep; if (kdb_frame->tf_eflags & PSL_VM) { tfp = (void *)kdb_frame; switch ((intptr_t)vp->valuep) { case (intptr_t)DB_OFFSET(tf_cs): reg = (uint16_t *)&tfp->tf_cs; break; case (intptr_t)DB_OFFSET(tf_ds): reg = (uint16_t *)&tfp->tf_vm86_ds; break; case (intptr_t)DB_OFFSET(tf_es): reg = (uint16_t *)&tfp->tf_vm86_es; break; case (intptr_t)DB_OFFSET(tf_fs): reg = (uint16_t *)&tfp->tf_vm86_fs; break; } } else reg = (uint16_t *)((uintptr_t)kdb_frame + off); if (op == DB_VAR_GET) *valuep = *reg; else *reg = *valuep; return (1); } static int db_esp(struct db_variable *vp, db_expr_t *valuep, int op) { if (kdb_frame == NULL) return (0); if (op == DB_VAR_GET) *valuep = get_esp(kdb_frame); else if (TF_HAS_STACKREGS(kdb_frame)) kdb_frame->tf_esp = *valuep; return (1); } static int db_gs(struct db_variable *vp, db_expr_t *valuep, int op) { struct trapframe_vm86 *tfp; if (kdb_frame != NULL && kdb_frame->tf_eflags & PSL_VM) { tfp = (void *)kdb_frame; if (op == DB_VAR_GET) *valuep = tfp->tf_vm86_gs; else tfp->tf_vm86_gs = *valuep; return (1); } if (op == DB_VAR_GET) *valuep = rgs(); else load_gs(*valuep); return (1); } static int db_ss(struct db_variable *vp, db_expr_t *valuep, int op) { if (kdb_frame == NULL) return (0); if (op == DB_VAR_GET) *valuep = TF_HAS_STACKREGS(kdb_frame) ? kdb_frame->tf_ss : rss(); else if (TF_HAS_STACKREGS(kdb_frame)) kdb_frame->tf_ss = *valuep; return (1); } #define NORMAL 0 #define TRAP 1 #define INTERRUPT 2 #define SYSCALL 3 #define DOUBLE_FAULT 4 static void db_nextframe(struct i386_frame **, db_addr_t *, struct thread *); static int db_numargs(struct i386_frame *); static void db_print_stack_entry(const char *, int, char **, int *, db_addr_t, void *); /* * Figure out how many arguments were passed into the frame at "fp". */ static int -db_numargs(fp) - struct i386_frame *fp; +db_numargs(struct i386_frame *fp) { char *argp; int inst; int args; argp = (char *)db_get_value((int)&fp->f_retaddr, 4, false); /* * XXX etext is wrong for LKMs. We should attempt to interpret * the instruction at the return address in all cases. This * may require better fault handling. */ if (argp < btext || argp >= etext) { args = -1; } else { retry: inst = db_get_value((int)argp, 4, false); if ((inst & 0xff) == 0x59) /* popl %ecx */ args = 1; else if ((inst & 0xffff) == 0xc483) /* addl $Ibs, %esp */ args = ((inst >> 16) & 0xff) / 4; else if ((inst & 0xf8ff) == 0xc089) { /* movl %eax, %Reg */ argp += 2; goto retry; } else args = -1; } return (args); } static void -db_print_stack_entry(name, narg, argnp, argp, callpc, frame) - const char *name; - int narg; - char **argnp; - int *argp; - db_addr_t callpc; - void *frame; +db_print_stack_entry(const char *name, int narg, char **argnp, int *argp, + db_addr_t callpc, void *frame) { int n = narg >= 0 ? narg : 5; db_printf("%s(", name); while (n) { if (argnp) db_printf("%s=", *argnp++); db_printf("%r", db_get_value((int)argp, 4, false)); argp++; if (--n != 0) db_printf(","); } if (narg < 0) db_printf(",..."); db_printf(") at "); db_printsym(callpc, DB_STGY_PROC); if (frame != NULL) db_printf("/frame 0x%r", (register_t)frame); db_printf("\n"); } /* * Figure out the next frame up in the call stack. */ static void db_nextframe(struct i386_frame **fp, db_addr_t *ip, struct thread *td) { struct trapframe *tf; int frame_type; int eip, esp, ebp; db_expr_t offset; c_db_sym_t sym; const char *name; eip = db_get_value((int) &(*fp)->f_retaddr, 4, false); ebp = db_get_value((int) &(*fp)->f_frame, 4, false); /* * Figure out frame type. We look at the address just before * the saved instruction pointer as the saved EIP is after the * call function, and if the function being called is marked as * dead (such as panic() at the end of dblfault_handler()), then * the instruction at the saved EIP will be part of a different * function (syscall() in this example) rather than the one that * actually made the call. */ frame_type = NORMAL; if (eip >= PMAP_TRM_MIN_ADDRESS) { sym = db_search_symbol(eip - 1 - setidt_disp, DB_STGY_ANY, &offset); } else { sym = db_search_symbol(eip - 1, DB_STGY_ANY, &offset); } db_symbol_values(sym, &name, NULL); if (name != NULL) { if (strcmp(name, "calltrap") == 0 || strcmp(name, "fork_trampoline") == 0) frame_type = TRAP; else if (strncmp(name, "Xatpic_intr", 11) == 0 || strncmp(name, "Xapic_isr", 9) == 0) { frame_type = INTERRUPT; } else if (strcmp(name, "Xlcall_syscall") == 0 || strcmp(name, "Xint0x80_syscall") == 0) frame_type = SYSCALL; else if (strcmp(name, "dblfault_handler") == 0) frame_type = DOUBLE_FAULT; else if (strcmp(name, "Xtimerint") == 0 || strcmp(name, "Xxen_intr_upcall") == 0) frame_type = INTERRUPT; else if (strcmp(name, "Xcpustop") == 0 || strcmp(name, "Xrendezvous") == 0 || strcmp(name, "Xipi_intr_bitmap_handler") == 0) { /* No arguments. */ frame_type = INTERRUPT; } } /* * Normal frames need no special processing. */ if (frame_type == NORMAL) { *ip = (db_addr_t) eip; *fp = (struct i386_frame *) ebp; return; } db_print_stack_entry(name, 0, 0, 0, eip, &(*fp)->f_frame); /* * For a double fault, we have to snag the values from the * previous TSS since a double fault uses a task gate to * switch to a known good state. */ if (frame_type == DOUBLE_FAULT) { esp = PCPU_GET(common_tssp)->tss_esp; eip = PCPU_GET(common_tssp)->tss_eip; ebp = PCPU_GET(common_tssp)->tss_ebp; db_printf( "--- trap 0x17, eip = %#r, esp = %#r, ebp = %#r ---\n", eip, esp, ebp); *ip = (db_addr_t) eip; *fp = (struct i386_frame *) ebp; return; } /* * Point to base of trapframe which is just above the current * frame. Pointer to it was put into %ebp by the kernel entry * code. */ tf = (struct trapframe *)(*fp)->f_frame; /* * This can be the case for e.g. fork_trampoline, last frame * of a kernel thread stack. */ if (tf == NULL) { *ip = 0; *fp = 0; db_printf("--- kthread start\n"); return; } esp = get_esp(tf); eip = tf->tf_eip; ebp = tf->tf_ebp; switch (frame_type) { case TRAP: db_printf("--- trap %#r", tf->tf_trapno); break; case SYSCALL: db_printf("--- syscall"); db_decode_syscall(td, tf->tf_eax); break; case INTERRUPT: db_printf("--- interrupt"); break; default: panic("The moon has moved again."); } db_printf(", eip = %#r, esp = %#r, ebp = %#r ---\n", eip, esp, ebp); /* * Detect the last (trap) frame on the kernel stack, where we * entered kernel from usermode. Terminate tracing in this * case. */ switch (frame_type) { case TRAP: case INTERRUPT: if (!TRAPF_USERMODE(tf)) break; /* FALLTHROUGH */ case SYSCALL: ebp = 0; eip = 0; break; } *ip = (db_addr_t) eip; *fp = (struct i386_frame *) ebp; } static int db_backtrace(struct thread *td, struct trapframe *tf, struct i386_frame *frame, db_addr_t pc, register_t sp, int count) { struct i386_frame *actframe; #define MAXNARG 16 char *argnames[MAXNARG], **argnp = NULL; const char *name; int *argp; db_expr_t offset; c_db_sym_t sym; int instr, narg; bool first; if (db_segsize(tf) == 16) { db_printf( "--- 16-bit%s, cs:eip = %#x:%#x, ss:esp = %#x:%#x, ebp = %#x, tf = %p ---\n", (tf->tf_eflags & PSL_VM) ? " (vm86)" : "", tf->tf_cs, tf->tf_eip, TF_HAS_STACKREGS(tf) ? tf->tf_ss : rss(), TF_HAS_STACKREGS(tf) ? tf->tf_esp : (intptr_t)&tf->tf_esp, tf->tf_ebp, tf); return (0); } /* 'frame' can be null initially. Just print the pc then. */ if (frame == NULL) goto out; /* * If an indirect call via an invalid pointer caused a trap, * %pc contains the invalid address while the return address * of the unlucky caller has been saved by CPU on the stack * just before the trap frame. In this case, try to recover * the caller's address so that the first frame is assigned * to the right spot in the right function, for that is where * the failure actually happened. * * This trick depends on the fault address stashed in tf_err * by trap_fatal() before entering KDB. */ if (kdb_frame && pc == kdb_frame->tf_err) { /* * Find where the trap frame actually ends. * It won't contain tf_esp or tf_ss unless crossing rings. */ if (TF_HAS_STACKREGS(kdb_frame)) instr = (int)(kdb_frame + 1); else instr = (int)&kdb_frame->tf_esp; pc = db_get_value(instr, 4, false); } if (count == -1) count = 1024; first = true; while (count-- && !db_pager_quit) { sym = db_search_symbol(pc, DB_STGY_ANY, &offset); db_symbol_values(sym, &name, NULL); /* * Attempt to determine a (possibly fake) frame that gives * the caller's pc. It may differ from `frame' if the * current function never sets up a standard frame or hasn't * set one up yet or has just discarded one. The last two * cases can be guessed fairly reliably for code generated * by gcc. The first case is too much trouble to handle in * general because the amount of junk on the stack depends * on the pc (the special handling of "calltrap", etc. in * db_nextframe() works because the `next' pc is special). */ actframe = frame; if (first) { first = false; if (sym == C_DB_SYM_NULL && sp != 0) { /* * If a symbol couldn't be found, we've probably * jumped to a bogus location, so try and use * the return address to find our caller. */ db_print_stack_entry(name, 0, 0, 0, pc, NULL); pc = db_get_value(sp, 4, false); if (db_search_symbol(pc, DB_STGY_PROC, &offset) == C_DB_SYM_NULL) break; continue; } else if (tf != NULL) { instr = db_get_value(pc, 4, false); if ((instr & 0xffffff) == 0x00e58955) { /* pushl %ebp; movl %esp, %ebp */ actframe = (void *)(get_esp(tf) - 4); } else if ((instr & 0xffff) == 0x0000e589) { /* movl %esp, %ebp */ actframe = (void *)get_esp(tf); if (tf->tf_ebp == 0) { /* Fake frame better. */ frame = actframe; } } else if ((instr & 0xff) == 0x000000c3) { /* ret */ actframe = (void *)(get_esp(tf) - 4); } else if (offset == 0) { /* Probably an assembler symbol. */ actframe = (void *)(get_esp(tf) - 4); } } else if (strcmp(name, "fork_trampoline") == 0) { /* * Don't try to walk back on a stack for a * process that hasn't actually been run yet. */ db_print_stack_entry(name, 0, 0, 0, pc, actframe); break; } } argp = &actframe->f_arg0; narg = MAXNARG; if (sym != NULL && db_sym_numargs(sym, &narg, argnames)) { argnp = argnames; } else { narg = db_numargs(frame); } db_print_stack_entry(name, narg, argnp, argp, pc, actframe); if (actframe != frame) { /* `frame' belongs to caller. */ pc = (db_addr_t) db_get_value((int)&actframe->f_retaddr, 4, false); continue; } db_nextframe(&frame, &pc, td); out: /* * 'frame' can be null here, either because it was initially * null or because db_nextframe() found no frame. * db_nextframe() may also have found a non-kernel frame. * !INKERNEL() classifies both. Stop tracing if either, * after printing the pc if it is the kernel. */ if (frame == NULL || frame <= actframe) { if (pc != 0) { sym = db_search_symbol(pc, DB_STGY_ANY, &offset); db_symbol_values(sym, &name, NULL); db_print_stack_entry(name, 0, 0, 0, pc, frame); } break; } } return (0); } void db_trace_self(void) { struct i386_frame *frame; db_addr_t callpc; register_t ebp; __asm __volatile("movl %%ebp,%0" : "=r" (ebp)); frame = (struct i386_frame *)ebp; callpc = (db_addr_t)db_get_value((int)&frame->f_retaddr, 4, false); frame = frame->f_frame; db_backtrace(curthread, NULL, frame, callpc, 0, -1); } int db_trace_thread(struct thread *thr, int count) { struct pcb *ctx; struct trapframe *tf; ctx = kdb_thr_ctx(thr); tf = thr == kdb_thread ? kdb_frame : NULL; return (db_backtrace(thr, tf, (struct i386_frame *)ctx->pcb_ebp, ctx->pcb_eip, ctx->pcb_esp, count)); } void db_md_list_watchpoints(void) { dbreg_list_watchpoints(); } diff --git a/sys/i386/i386/in_cksum_machdep.c b/sys/i386/i386/in_cksum_machdep.c index 31ecd2baa7ca..528ce5ac80ff 100644 --- a/sys/i386/i386/in_cksum_machdep.c +++ b/sys/i386/i386/in_cksum_machdep.c @@ -1,301 +1,298 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from tahoe: in_cksum.c 1.2 86/01/05 * from: @(#)in_cksum.c 1.3 (Berkeley) 1/19/91 */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include /* * Checksum routine for Internet Protocol family headers. * * This routine is very heavily used in the network * code and should be modified for each CPU to be as fast as possible. * * This implementation is 386 version. */ #undef ADDCARRY #define ADDCARRY(x) if ((x) > 0xffff) (x) -= 0xffff #define REDUCE {sum = (sum & 0xffff) + (sum >> 16); ADDCARRY(sum);} /* * These asm statements require __volatile because they pass information * via the condition codes. GCC does not currently provide a way to specify * the condition codes as an input or output operand. * * The LOAD macro below is effectively a prefetch into cache. GCC will * load the value into a register but will not use it. Since modern CPUs * reorder operations, this will generally take place in parallel with * other calculations. */ u_short -in_cksum_skip(m, len, skip) - struct mbuf *m; - int len; - int skip; +in_cksum_skip(struct mbuf *m, int len, int skip) { u_short *w; unsigned sum = 0; int mlen = 0; int byte_swapped = 0; union { char c[2]; u_short s; } su; len -= skip; for (; skip && m; m = m->m_next) { if (m->m_len > skip) { mlen = m->m_len - skip; w = (u_short *)(mtod(m, u_char *) + skip); goto skip_start; } else { skip -= m->m_len; } } for (;m && len; m = m->m_next) { if (m->m_len == 0) continue; w = mtod(m, u_short *); if (mlen == -1) { /* * The first byte of this mbuf is the continuation * of a word spanning between this mbuf and the * last mbuf. */ /* su.c[0] is already saved when scanning previous * mbuf. sum was REDUCEd when we found mlen == -1 */ su.c[1] = *(u_char *)w; sum += su.s; w = (u_short *)((char *)w + 1); mlen = m->m_len - 1; len--; } else mlen = m->m_len; skip_start: if (len < mlen) mlen = len; len -= mlen; /* * Force to long boundary so we do longword aligned * memory operations */ if (3 & (int) w) { REDUCE; if ((1 & (int) w) && (mlen > 0)) { sum <<= 8; su.c[0] = *(char *)w; w = (u_short *)((char *)w + 1); mlen--; byte_swapped = 1; } if ((2 & (int) w) && (mlen >= 2)) { sum += *w++; mlen -= 2; } } /* * Advance to a 486 cache line boundary. */ if (4 & (int) w && mlen >= 4) { __asm __volatile ( "addl %1, %0\n" "adcl $0, %0" : "+r" (sum) : "g" (((const u_int32_t *)w)[0]) ); w += 2; mlen -= 4; } if (8 & (int) w && mlen >= 8) { __asm __volatile ( "addl %1, %0\n" "adcl %2, %0\n" "adcl $0, %0" : "+r" (sum) : "g" (((const u_int32_t *)w)[0]), "g" (((const u_int32_t *)w)[1]) ); w += 4; mlen -= 8; } /* * Do as much of the checksum as possible 32 bits at at time. * In fact, this loop is unrolled to make overhead from * branches &c small. */ mlen -= 1; while ((mlen -= 32) >= 0) { /* * Add with carry 16 words and fold in the last * carry by adding a 0 with carry. * * The early ADD(16) and the LOAD(32) are to load * the next 2 cache lines in advance on 486's. The * 486 has a penalty of 2 clock cycles for loading * a cache line, plus whatever time the external * memory takes to load the first word(s) addressed. * These penalties are unavoidable. Subsequent * accesses to a cache line being loaded (and to * other external memory?) are delayed until the * whole load finishes. These penalties are mostly * avoided by not accessing external memory for * 8 cycles after the ADD(16) and 12 cycles after * the LOAD(32). The loop terminates when mlen * is initially 33 (not 32) to guaranteed that * the LOAD(32) is within bounds. */ __asm __volatile ( "addl %1, %0\n" "adcl %2, %0\n" "adcl %3, %0\n" "adcl %4, %0\n" "adcl %5, %0\n" "mov %6, %%eax\n" "adcl %7, %0\n" "adcl %8, %0\n" "adcl %9, %0\n" "adcl $0, %0" : "+r" (sum) : "g" (((const u_int32_t *)w)[4]), "g" (((const u_int32_t *)w)[0]), "g" (((const u_int32_t *)w)[1]), "g" (((const u_int32_t *)w)[2]), "g" (((const u_int32_t *)w)[3]), "g" (((const u_int32_t *)w)[8]), "g" (((const u_int32_t *)w)[5]), "g" (((const u_int32_t *)w)[6]), "g" (((const u_int32_t *)w)[7]) : "eax" ); w += 16; } mlen += 32 + 1; if (mlen >= 32) { __asm __volatile ( "addl %1, %0\n" "adcl %2, %0\n" "adcl %3, %0\n" "adcl %4, %0\n" "adcl %5, %0\n" "adcl %6, %0\n" "adcl %7, %0\n" "adcl %8, %0\n" "adcl $0, %0" : "+r" (sum) : "g" (((const u_int32_t *)w)[4]), "g" (((const u_int32_t *)w)[0]), "g" (((const u_int32_t *)w)[1]), "g" (((const u_int32_t *)w)[2]), "g" (((const u_int32_t *)w)[3]), "g" (((const u_int32_t *)w)[5]), "g" (((const u_int32_t *)w)[6]), "g" (((const u_int32_t *)w)[7]) ); w += 16; mlen -= 32; } if (mlen >= 16) { __asm __volatile ( "addl %1, %0\n" "adcl %2, %0\n" "adcl %3, %0\n" "adcl %4, %0\n" "adcl $0, %0" : "+r" (sum) : "g" (((const u_int32_t *)w)[0]), "g" (((const u_int32_t *)w)[1]), "g" (((const u_int32_t *)w)[2]), "g" (((const u_int32_t *)w)[3]) ); w += 8; mlen -= 16; } if (mlen >= 8) { __asm __volatile ( "addl %1, %0\n" "adcl %2, %0\n" "adcl $0, %0" : "+r" (sum) : "g" (((const u_int32_t *)w)[0]), "g" (((const u_int32_t *)w)[1]) ); w += 4; mlen -= 8; } if (mlen == 0 && byte_swapped == 0) continue; /* worth 1% maybe ?? */ REDUCE; while ((mlen -= 2) >= 0) { sum += *w++; } if (byte_swapped) { sum <<= 8; byte_swapped = 0; if (mlen == -1) { su.c[1] = *(char *)w; sum += su.s; mlen = 0; } else mlen = -1; } else if (mlen == -1) /* * This mbuf has odd number of bytes. * There could be a word split between * this mbuf and the next mbuf. * Save the last byte (to prepend to next mbuf). */ su.c[0] = *(char *)w; } if (len) printf("%s: out of data by %d\n", __func__, len); if (mlen == -1) { /* The last mbuf has odd # of bytes. Follow the standard (the odd byte is shifted left by 8 bits) */ su.c[1] = 0; sum += su.s; } REDUCE; return (~sum & 0xffff); } diff --git a/sys/i386/i386/machdep.c b/sys/i386/i386/machdep.c index 8e3b21dbe195..d59e95ed1a99 100644 --- a/sys/i386/i386/machdep.c +++ b/sys/i386/i386/machdep.c @@ -1,1878 +1,1875 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 2018 The FreeBSD Foundation * Copyright (c) 1992 Terrence R. Lambert. * Copyright (c) 1982, 1987, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * William Jolitz. * * Portions of this software were developed by A. Joseph Koshy under * sponsorship from the FreeBSD Foundation and Google, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)machdep.c 7.4 (Berkeley) 6/3/91 */ #include __FBSDID("$FreeBSD$"); #include "opt_apic.h" #include "opt_atpic.h" #include "opt_cpu.h" #include "opt_ddb.h" #include "opt_inet.h" #include "opt_isa.h" #include "opt_kstack_pages.h" #include "opt_maxmem.h" #include "opt_perfmon.h" #include "opt_platform.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DDB #ifndef KDB #error KDB must be enabled in order for DDB to work! #endif #include #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef PERFMON #include #endif #ifdef SMP #include #endif #ifdef FDT #include #endif #ifdef DEV_APIC #include #endif #ifdef DEV_ISA #include #endif /* Sanity check for __curthread() */ CTASSERT(offsetof(struct pcpu, pc_curthread) == 0); register_t init386(int first); void dblfault_handler(void); void identify_cpu(void); static void cpu_startup(void *); SYSINIT(cpu, SI_SUB_CPU, SI_ORDER_FIRST, cpu_startup, NULL); /* Intel ICH registers */ #define ICH_PMBASE 0x400 #define ICH_SMI_EN ICH_PMBASE + 0x30 int _udatasel, _ucodesel; u_int basemem; static int above4g_allow = 1; static int above24g_allow = 0; int cold = 1; long Maxmem = 0; long realmem = 0; int late_console = 1; #ifdef PAE FEATURE(pae, "Physical Address Extensions"); #endif struct kva_md_info kmi; static struct trapframe proc0_tf; struct pcpu __pcpu[MAXCPU]; static void i386_clock_source_init(void); struct mtx icu_lock; struct mem_range_softc mem_range_softc; extern char start_exceptions[], end_exceptions[]; extern struct sysentvec elf32_freebsd_sysvec; /* Default init_ops implementation. */ struct init_ops init_ops = { .early_clock_source_init = i386_clock_source_init, .early_delay = i8254_delay, }; static void i386_clock_source_init(void) { i8254_init(); } static void -cpu_startup(dummy) - void *dummy; +cpu_startup(void *dummy) { uintmax_t memsize; char *sysenv; /* * On MacBooks, we need to disallow the legacy USB circuit to * generate an SMI# because this can cause several problems, * namely: incorrect CPU frequency detection and failure to * start the APs. * We do this by disabling a bit in the SMI_EN (SMI Control and * Enable register) of the Intel ICH LPC Interface Bridge. */ sysenv = kern_getenv("smbios.system.product"); if (sysenv != NULL) { if (strncmp(sysenv, "MacBook1,1", 10) == 0 || strncmp(sysenv, "MacBook3,1", 10) == 0 || strncmp(sysenv, "MacBook4,1", 10) == 0 || strncmp(sysenv, "MacBookPro1,1", 13) == 0 || strncmp(sysenv, "MacBookPro1,2", 13) == 0 || strncmp(sysenv, "MacBookPro3,1", 13) == 0 || strncmp(sysenv, "MacBookPro4,1", 13) == 0 || strncmp(sysenv, "Macmini1,1", 10) == 0) { if (bootverbose) printf("Disabling LEGACY_USB_EN bit on " "Intel ICH.\n"); outl(ICH_SMI_EN, inl(ICH_SMI_EN) & ~0x8); } freeenv(sysenv); } /* * Good {morning,afternoon,evening,night}. */ startrtclock(); printcpuinfo(); panicifcpuunsupported(); #ifdef PERFMON perfmon_init(); #endif /* * Display physical memory if SMBIOS reports reasonable amount. */ memsize = 0; sysenv = kern_getenv("smbios.memory.enabled"); if (sysenv != NULL) { memsize = (uintmax_t)strtoul(sysenv, (char **)NULL, 10) << 10; freeenv(sysenv); } if (memsize < ptoa((uintmax_t)vm_free_count())) memsize = ptoa((uintmax_t)Maxmem); printf("real memory = %ju (%ju MB)\n", memsize, memsize >> 20); realmem = atop(memsize); /* * Display any holes after the first chunk of extended memory. */ if (bootverbose) { int indx; printf("Physical memory chunk(s):\n"); for (indx = 0; phys_avail[indx + 1] != 0; indx += 2) { vm_paddr_t size; size = phys_avail[indx + 1] - phys_avail[indx]; printf( "0x%016jx - 0x%016jx, %ju bytes (%ju pages)\n", (uintmax_t)phys_avail[indx], (uintmax_t)phys_avail[indx + 1] - 1, (uintmax_t)size, (uintmax_t)size / PAGE_SIZE); } } vm_ksubmap_init(&kmi); printf("avail memory = %ju (%ju MB)\n", ptoa((uintmax_t)vm_free_count()), ptoa((uintmax_t)vm_free_count()) / 1048576); /* * Set up buffers, so they can be used to read disk labels. */ bufinit(); vm_pager_bufferinit(); cpu_setregs(); } void cpu_setregs(void) { unsigned int cr0; cr0 = rcr0(); /* * CR0_MP, CR0_NE and CR0_TS are set for NPX (FPU) support: * * Prepare to trap all ESC (i.e., NPX) instructions and all WAIT * instructions. We must set the CR0_MP bit and use the CR0_TS * bit to control the trap, because setting the CR0_EM bit does * not cause WAIT instructions to trap. It's important to trap * WAIT instructions - otherwise the "wait" variants of no-wait * control instructions would degenerate to the "no-wait" variants * after FP context switches but work correctly otherwise. It's * particularly important to trap WAITs when there is no NPX - * otherwise the "wait" variants would always degenerate. * * Try setting CR0_NE to get correct error reporting on 486DX's. * Setting it should fail or do nothing on lesser processors. */ cr0 |= CR0_MP | CR0_NE | CR0_TS | CR0_WP | CR0_AM; load_cr0(cr0); load_gs(_udatasel); } u_long bootdev; /* not a struct cdev *- encoding is different */ SYSCTL_ULONG(_machdep, OID_AUTO, guessed_bootdev, CTLFLAG_RD, &bootdev, 0, "Maybe the Boot device (not in struct cdev *format)"); /* * Initialize 386 and configure to run kernel */ /* * Initialize segments & interrupt table */ int _default_ldt; struct mtx dt_lock; /* lock for GDT and LDT */ union descriptor gdt0[NGDT]; /* initial global descriptor table */ union descriptor *gdt = gdt0; /* global descriptor table */ union descriptor *ldt; /* local descriptor table */ static struct gate_descriptor idt0[NIDT]; struct gate_descriptor *idt = &idt0[0]; /* interrupt descriptor table */ static struct i386tss *dblfault_tss; static char *dblfault_stack; static struct i386tss common_tss0; vm_offset_t proc0kstack; /* * software prototypes -- in more palatable form. * * GCODE_SEL through GUDATA_SEL must be in this order for syscall/sysret * GUFS_SEL and GUGS_SEL must be in this order (swtch.s knows it) */ struct soft_segment_descriptor gdt_segs[] = { /* GNULL_SEL 0 Null Descriptor */ { .ssd_base = 0x0, .ssd_limit = 0x0, .ssd_type = 0, .ssd_dpl = SEL_KPL, .ssd_p = 0, .ssd_xx = 0, .ssd_xx1 = 0, .ssd_def32 = 0, .ssd_gran = 0 }, /* GPRIV_SEL 1 SMP Per-Processor Private Data Descriptor */ { .ssd_base = 0x0, .ssd_limit = 0xfffff, .ssd_type = SDT_MEMRWA, .ssd_dpl = SEL_KPL, .ssd_p = 1, .ssd_xx = 0, .ssd_xx1 = 0, .ssd_def32 = 1, .ssd_gran = 1 }, /* GUFS_SEL 2 %fs Descriptor for user */ { .ssd_base = 0x0, .ssd_limit = 0xfffff, .ssd_type = SDT_MEMRWA, .ssd_dpl = SEL_UPL, .ssd_p = 1, .ssd_xx = 0, .ssd_xx1 = 0, .ssd_def32 = 1, .ssd_gran = 1 }, /* GUGS_SEL 3 %gs Descriptor for user */ { .ssd_base = 0x0, .ssd_limit = 0xfffff, .ssd_type = SDT_MEMRWA, .ssd_dpl = SEL_UPL, .ssd_p = 1, .ssd_xx = 0, .ssd_xx1 = 0, .ssd_def32 = 1, .ssd_gran = 1 }, /* GCODE_SEL 4 Code Descriptor for kernel */ { .ssd_base = 0x0, .ssd_limit = 0xfffff, .ssd_type = SDT_MEMERA, .ssd_dpl = SEL_KPL, .ssd_p = 1, .ssd_xx = 0, .ssd_xx1 = 0, .ssd_def32 = 1, .ssd_gran = 1 }, /* GDATA_SEL 5 Data Descriptor for kernel */ { .ssd_base = 0x0, .ssd_limit = 0xfffff, .ssd_type = SDT_MEMRWA, .ssd_dpl = SEL_KPL, .ssd_p = 1, .ssd_xx = 0, .ssd_xx1 = 0, .ssd_def32 = 1, .ssd_gran = 1 }, /* GUCODE_SEL 6 Code Descriptor for user */ { .ssd_base = 0x0, .ssd_limit = 0xfffff, .ssd_type = SDT_MEMERA, .ssd_dpl = SEL_UPL, .ssd_p = 1, .ssd_xx = 0, .ssd_xx1 = 0, .ssd_def32 = 1, .ssd_gran = 1 }, /* GUDATA_SEL 7 Data Descriptor for user */ { .ssd_base = 0x0, .ssd_limit = 0xfffff, .ssd_type = SDT_MEMRWA, .ssd_dpl = SEL_UPL, .ssd_p = 1, .ssd_xx = 0, .ssd_xx1 = 0, .ssd_def32 = 1, .ssd_gran = 1 }, /* GBIOSLOWMEM_SEL 8 BIOS access to realmode segment 0x40, must be #8 in GDT */ { .ssd_base = 0x400, .ssd_limit = 0xfffff, .ssd_type = SDT_MEMRWA, .ssd_dpl = SEL_KPL, .ssd_p = 1, .ssd_xx = 0, .ssd_xx1 = 0, .ssd_def32 = 1, .ssd_gran = 1 }, /* GPROC0_SEL 9 Proc 0 Tss Descriptor */ { .ssd_base = 0x0, .ssd_limit = sizeof(struct i386tss)-1, .ssd_type = SDT_SYS386TSS, .ssd_dpl = 0, .ssd_p = 1, .ssd_xx = 0, .ssd_xx1 = 0, .ssd_def32 = 0, .ssd_gran = 0 }, /* GLDT_SEL 10 LDT Descriptor */ { .ssd_base = 0, .ssd_limit = sizeof(union descriptor) * NLDT - 1, .ssd_type = SDT_SYSLDT, .ssd_dpl = SEL_UPL, .ssd_p = 1, .ssd_xx = 0, .ssd_xx1 = 0, .ssd_def32 = 0, .ssd_gran = 0 }, /* GUSERLDT_SEL 11 User LDT Descriptor per process */ { .ssd_base = 0, .ssd_limit = (512 * sizeof(union descriptor)-1), .ssd_type = SDT_SYSLDT, .ssd_dpl = 0, .ssd_p = 1, .ssd_xx = 0, .ssd_xx1 = 0, .ssd_def32 = 0, .ssd_gran = 0 }, /* GPANIC_SEL 12 Panic Tss Descriptor */ { .ssd_base = 0, .ssd_limit = sizeof(struct i386tss)-1, .ssd_type = SDT_SYS386TSS, .ssd_dpl = 0, .ssd_p = 1, .ssd_xx = 0, .ssd_xx1 = 0, .ssd_def32 = 0, .ssd_gran = 0 }, /* GBIOSCODE32_SEL 13 BIOS 32-bit interface (32bit Code) */ { .ssd_base = 0, .ssd_limit = 0xfffff, .ssd_type = SDT_MEMERA, .ssd_dpl = 0, .ssd_p = 1, .ssd_xx = 0, .ssd_xx1 = 0, .ssd_def32 = 0, .ssd_gran = 1 }, /* GBIOSCODE16_SEL 14 BIOS 32-bit interface (16bit Code) */ { .ssd_base = 0, .ssd_limit = 0xfffff, .ssd_type = SDT_MEMERA, .ssd_dpl = 0, .ssd_p = 1, .ssd_xx = 0, .ssd_xx1 = 0, .ssd_def32 = 0, .ssd_gran = 1 }, /* GBIOSDATA_SEL 15 BIOS 32-bit interface (Data) */ { .ssd_base = 0, .ssd_limit = 0xfffff, .ssd_type = SDT_MEMRWA, .ssd_dpl = 0, .ssd_p = 1, .ssd_xx = 0, .ssd_xx1 = 0, .ssd_def32 = 1, .ssd_gran = 1 }, /* GBIOSUTIL_SEL 16 BIOS 16-bit interface (Utility) */ { .ssd_base = 0, .ssd_limit = 0xfffff, .ssd_type = SDT_MEMRWA, .ssd_dpl = 0, .ssd_p = 1, .ssd_xx = 0, .ssd_xx1 = 0, .ssd_def32 = 0, .ssd_gran = 1 }, /* GBIOSARGS_SEL 17 BIOS 16-bit interface (Arguments) */ { .ssd_base = 0, .ssd_limit = 0xfffff, .ssd_type = SDT_MEMRWA, .ssd_dpl = 0, .ssd_p = 1, .ssd_xx = 0, .ssd_xx1 = 0, .ssd_def32 = 0, .ssd_gran = 1 }, /* GNDIS_SEL 18 NDIS Descriptor */ { .ssd_base = 0x0, .ssd_limit = 0x0, .ssd_type = 0, .ssd_dpl = 0, .ssd_p = 0, .ssd_xx = 0, .ssd_xx1 = 0, .ssd_def32 = 0, .ssd_gran = 0 }, }; static struct soft_segment_descriptor ldt_segs[] = { /* Null Descriptor - overwritten by call gate */ { .ssd_base = 0x0, .ssd_limit = 0x0, .ssd_type = 0, .ssd_dpl = 0, .ssd_p = 0, .ssd_xx = 0, .ssd_xx1 = 0, .ssd_def32 = 0, .ssd_gran = 0 }, /* Null Descriptor - overwritten by call gate */ { .ssd_base = 0x0, .ssd_limit = 0x0, .ssd_type = 0, .ssd_dpl = 0, .ssd_p = 0, .ssd_xx = 0, .ssd_xx1 = 0, .ssd_def32 = 0, .ssd_gran = 0 }, /* Null Descriptor - overwritten by call gate */ { .ssd_base = 0x0, .ssd_limit = 0x0, .ssd_type = 0, .ssd_dpl = 0, .ssd_p = 0, .ssd_xx = 0, .ssd_xx1 = 0, .ssd_def32 = 0, .ssd_gran = 0 }, /* Code Descriptor for user */ { .ssd_base = 0x0, .ssd_limit = 0xfffff, .ssd_type = SDT_MEMERA, .ssd_dpl = SEL_UPL, .ssd_p = 1, .ssd_xx = 0, .ssd_xx1 = 0, .ssd_def32 = 1, .ssd_gran = 1 }, /* Null Descriptor - overwritten by call gate */ { .ssd_base = 0x0, .ssd_limit = 0x0, .ssd_type = 0, .ssd_dpl = 0, .ssd_p = 0, .ssd_xx = 0, .ssd_xx1 = 0, .ssd_def32 = 0, .ssd_gran = 0 }, /* Data Descriptor for user */ { .ssd_base = 0x0, .ssd_limit = 0xfffff, .ssd_type = SDT_MEMRWA, .ssd_dpl = SEL_UPL, .ssd_p = 1, .ssd_xx = 0, .ssd_xx1 = 0, .ssd_def32 = 1, .ssd_gran = 1 }, }; size_t setidt_disp; void setidt(int idx, inthand_t *func, int typ, int dpl, int selec) { uintptr_t off; off = func != NULL ? (uintptr_t)func + setidt_disp : 0; setidt_nodisp(idx, off, typ, dpl, selec); } void setidt_nodisp(int idx, uintptr_t off, int typ, int dpl, int selec) { struct gate_descriptor *ip; ip = idt + idx; ip->gd_looffset = off; ip->gd_selector = selec; ip->gd_stkcpy = 0; ip->gd_xx = 0; ip->gd_type = typ; ip->gd_dpl = dpl; ip->gd_p = 1; ip->gd_hioffset = ((u_int)off) >> 16 ; } extern inthand_t IDTVEC(div), IDTVEC(dbg), IDTVEC(nmi), IDTVEC(bpt), IDTVEC(ofl), IDTVEC(bnd), IDTVEC(ill), IDTVEC(dna), IDTVEC(fpusegm), IDTVEC(tss), IDTVEC(missing), IDTVEC(stk), IDTVEC(prot), IDTVEC(page), IDTVEC(mchk), IDTVEC(rsvd), IDTVEC(fpu), IDTVEC(align), IDTVEC(xmm), #ifdef KDTRACE_HOOKS IDTVEC(dtrace_ret), #endif #ifdef XENHVM IDTVEC(xen_intr_upcall), #endif IDTVEC(int0x80_syscall); #ifdef DDB /* * Display the index and function name of any IDT entries that don't use * the default 'rsvd' entry point. */ DB_SHOW_COMMAND_FLAGS(idt, db_show_idt, DB_CMD_MEMSAFE) { struct gate_descriptor *ip; int idx; uintptr_t func, func_trm; bool trm; ip = idt; for (idx = 0; idx < NIDT && !db_pager_quit; idx++) { if (ip->gd_type == SDT_SYSTASKGT) { db_printf("%3d\t\n", idx); } else { func = (ip->gd_hioffset << 16 | ip->gd_looffset); if (func >= PMAP_TRM_MIN_ADDRESS) { func_trm = func; func -= setidt_disp; trm = true; } else trm = false; if (func != (uintptr_t)&IDTVEC(rsvd)) { db_printf("%3d\t", idx); db_printsym(func, DB_STGY_PROC); if (trm) db_printf(" (trampoline %#x)", func_trm); db_printf("\n"); } } ip++; } } /* Show privileged registers. */ DB_SHOW_COMMAND_FLAGS(sysregs, db_show_sysregs, DB_CMD_MEMSAFE) { uint64_t idtr, gdtr; idtr = ridt(); db_printf("idtr\t0x%08x/%04x\n", (u_int)(idtr >> 16), (u_int)idtr & 0xffff); gdtr = rgdt(); db_printf("gdtr\t0x%08x/%04x\n", (u_int)(gdtr >> 16), (u_int)gdtr & 0xffff); db_printf("ldtr\t0x%04x\n", rldt()); db_printf("tr\t0x%04x\n", rtr()); db_printf("cr0\t0x%08x\n", rcr0()); db_printf("cr2\t0x%08x\n", rcr2()); db_printf("cr3\t0x%08x\n", rcr3()); db_printf("cr4\t0x%08x\n", rcr4()); if (rcr4() & CR4_XSAVE) db_printf("xcr0\t0x%016llx\n", rxcr(0)); if (amd_feature & (AMDID_NX | AMDID_LM)) db_printf("EFER\t0x%016llx\n", rdmsr(MSR_EFER)); if (cpu_feature2 & (CPUID2_VMX | CPUID2_SMX)) db_printf("FEATURES_CTL\t0x%016llx\n", rdmsr(MSR_IA32_FEATURE_CONTROL)); if (((cpu_vendor_id == CPU_VENDOR_INTEL || cpu_vendor_id == CPU_VENDOR_AMD) && CPUID_TO_FAMILY(cpu_id) >= 6) || cpu_vendor_id == CPU_VENDOR_HYGON) db_printf("DEBUG_CTL\t0x%016llx\n", rdmsr(MSR_DEBUGCTLMSR)); if (cpu_feature & CPUID_PAT) db_printf("PAT\t0x%016llx\n", rdmsr(MSR_PAT)); } DB_SHOW_COMMAND_FLAGS(dbregs, db_show_dbregs, DB_CMD_MEMSAFE) { db_printf("dr0\t0x%08x\n", rdr0()); db_printf("dr1\t0x%08x\n", rdr1()); db_printf("dr2\t0x%08x\n", rdr2()); db_printf("dr3\t0x%08x\n", rdr3()); db_printf("dr6\t0x%08x\n", rdr6()); db_printf("dr7\t0x%08x\n", rdr7()); } DB_SHOW_COMMAND(frame, db_show_frame) { struct trapframe *frame; frame = have_addr ? (struct trapframe *)addr : curthread->td_frame; printf("ss %#x esp %#x efl %#x cs %#x eip %#x\n", frame->tf_ss, frame->tf_esp, frame->tf_eflags, frame->tf_cs, frame->tf_eip); printf("err %#x trapno %d\n", frame->tf_err, frame->tf_trapno); printf("ds %#x es %#x fs %#x\n", frame->tf_ds, frame->tf_es, frame->tf_fs); printf("eax %#x ecx %#x edx %#x ebx %#x\n", frame->tf_eax, frame->tf_ecx, frame->tf_edx, frame->tf_ebx); printf("ebp %#x esi %#x edi %#x\n", frame->tf_ebp, frame->tf_esi, frame->tf_edi); } #endif void -sdtossd(sd, ssd) - struct segment_descriptor *sd; - struct soft_segment_descriptor *ssd; +sdtossd(struct segment_descriptor *sd, struct soft_segment_descriptor *ssd) { ssd->ssd_base = (sd->sd_hibase << 24) | sd->sd_lobase; ssd->ssd_limit = (sd->sd_hilimit << 16) | sd->sd_lolimit; ssd->ssd_type = sd->sd_type; ssd->ssd_dpl = sd->sd_dpl; ssd->ssd_p = sd->sd_p; ssd->ssd_def32 = sd->sd_def32; ssd->ssd_gran = sd->sd_gran; } static int add_physmap_entry(uint64_t base, uint64_t length, vm_paddr_t *physmap, int *physmap_idxp) { uint64_t lim, ign; int i, insert_idx, physmap_idx; physmap_idx = *physmap_idxp; if (length == 0) return (1); lim = 0x100000000; /* 4G */ if (pae_mode && above4g_allow) lim = above24g_allow ? -1ULL : 0x600000000; /* 24G */ if (base >= lim) { printf("%uK of memory above %uGB ignored, pae %d " "above4g_allow %d above24g_allow %d\n", (u_int)(length / 1024), (u_int)(lim >> 30), pae_mode, above4g_allow, above24g_allow); return (1); } if (base + length >= lim) { ign = base + length - lim; length -= ign; printf("%uK of memory above %uGB ignored, pae %d " "above4g_allow %d above24g_allow %d\n", (u_int)(ign / 1024), (u_int)(lim >> 30), pae_mode, above4g_allow, above24g_allow); } /* * Find insertion point while checking for overlap. Start off by * assuming the new entry will be added to the end. */ insert_idx = physmap_idx + 2; for (i = 0; i <= physmap_idx; i += 2) { if (base < physmap[i + 1]) { if (base + length <= physmap[i]) { insert_idx = i; break; } if (boothowto & RB_VERBOSE) printf( "Overlapping memory regions, ignoring second region\n"); return (1); } } /* See if we can prepend to the next entry. */ if (insert_idx <= physmap_idx && base + length == physmap[insert_idx]) { physmap[insert_idx] = base; return (1); } /* See if we can append to the previous entry. */ if (insert_idx > 0 && base == physmap[insert_idx - 1]) { physmap[insert_idx - 1] += length; return (1); } physmap_idx += 2; *physmap_idxp = physmap_idx; if (physmap_idx == PHYS_AVAIL_ENTRIES) { printf( "Too many segments in the physical address map, giving up\n"); return (0); } /* * Move the last 'N' entries down to make room for the new * entry if needed. */ for (i = physmap_idx; i > insert_idx; i -= 2) { physmap[i] = physmap[i - 2]; physmap[i + 1] = physmap[i - 1]; } /* Insert the new entry. */ physmap[insert_idx] = base; physmap[insert_idx + 1] = base + length; return (1); } static int add_smap_entry(struct bios_smap *smap, vm_paddr_t *physmap, int *physmap_idxp) { if (boothowto & RB_VERBOSE) printf("SMAP type=%02x base=%016llx len=%016llx\n", smap->type, smap->base, smap->length); if (smap->type != SMAP_TYPE_MEMORY) return (1); return (add_physmap_entry(smap->base, smap->length, physmap, physmap_idxp)); } static void add_smap_entries(struct bios_smap *smapbase, vm_paddr_t *physmap, int *physmap_idxp) { struct bios_smap *smap, *smapend; u_int32_t smapsize; /* * Memory map from INT 15:E820. * * subr_module.c says: * "Consumer may safely assume that size value precedes data." * ie: an int32_t immediately precedes SMAP. */ smapsize = *((u_int32_t *)smapbase - 1); smapend = (struct bios_smap *)((uintptr_t)smapbase + smapsize); for (smap = smapbase; smap < smapend; smap++) if (!add_smap_entry(smap, physmap, physmap_idxp)) break; } static void basemem_setup(void) { if (basemem > 640) { printf("Preposterous BIOS basemem of %uK, truncating to 640K\n", basemem); basemem = 640; } pmap_basemem_setup(basemem); } /* * Populate the (physmap) array with base/bound pairs describing the * available physical memory in the system, then test this memory and * build the phys_avail array describing the actually-available memory. * * If we cannot accurately determine the physical memory map, then use * value from the 0xE801 call, and failing that, the RTC. * * Total memory size may be set by the kernel environment variable * hw.physmem or the compile-time define MAXMEM. * * XXX first should be vm_paddr_t. */ static void getmemsize(int first) { int has_smap, off, physmap_idx, pa_indx, da_indx; u_long memtest; vm_paddr_t physmap[PHYS_AVAIL_ENTRIES]; quad_t dcons_addr, dcons_size, physmem_tunable; int hasbrokenint12, i, res __diagused; u_int extmem; struct vm86frame vmf; struct vm86context vmc; vm_paddr_t pa; struct bios_smap *smap, *smapbase; caddr_t kmdp; has_smap = 0; bzero(&vmf, sizeof(vmf)); bzero(physmap, sizeof(physmap)); basemem = 0; /* * Tell the physical memory allocator about pages used to store * the kernel and preloaded data. See kmem_bootstrap_free(). */ vm_phys_early_add_seg((vm_paddr_t)KERNLOAD, trunc_page(first)); TUNABLE_INT_FETCH("hw.above4g_allow", &above4g_allow); TUNABLE_INT_FETCH("hw.above24g_allow", &above24g_allow); /* * Check if the loader supplied an SMAP memory map. If so, * use that and do not make any VM86 calls. */ physmap_idx = 0; kmdp = preload_search_by_type("elf kernel"); if (kmdp == NULL) kmdp = preload_search_by_type("elf32 kernel"); smapbase = (struct bios_smap *)preload_search_info(kmdp, MODINFO_METADATA | MODINFOMD_SMAP); if (smapbase != NULL) { add_smap_entries(smapbase, physmap, &physmap_idx); has_smap = 1; goto have_smap; } /* * Some newer BIOSes have a broken INT 12H implementation * which causes a kernel panic immediately. In this case, we * need use the SMAP to determine the base memory size. */ hasbrokenint12 = 0; TUNABLE_INT_FETCH("hw.hasbrokenint12", &hasbrokenint12); if (hasbrokenint12 == 0) { /* Use INT12 to determine base memory size. */ vm86_intcall(0x12, &vmf); basemem = vmf.vmf_ax; basemem_setup(); } /* * Fetch the memory map with INT 15:E820. Map page 1 R/W into * the kernel page table so we can use it as a buffer. The * kernel will unmap this page later. */ vmc.npages = 0; smap = (void *)vm86_addpage(&vmc, 1, PMAP_MAP_LOW + ptoa(1)); res = vm86_getptr(&vmc, (vm_offset_t)smap, &vmf.vmf_es, &vmf.vmf_di); KASSERT(res != 0, ("vm86_getptr() failed: address not found")); vmf.vmf_ebx = 0; do { vmf.vmf_eax = 0xE820; vmf.vmf_edx = SMAP_SIG; vmf.vmf_ecx = sizeof(struct bios_smap); i = vm86_datacall(0x15, &vmf, &vmc); if (i || vmf.vmf_eax != SMAP_SIG) break; has_smap = 1; if (!add_smap_entry(smap, physmap, &physmap_idx)) break; } while (vmf.vmf_ebx != 0); have_smap: /* * If we didn't fetch the "base memory" size from INT12, * figure it out from the SMAP (or just guess). */ if (basemem == 0) { for (i = 0; i <= physmap_idx; i += 2) { if (physmap[i] == 0x00000000) { basemem = physmap[i + 1] / 1024; break; } } /* XXX: If we couldn't find basemem from SMAP, just guess. */ if (basemem == 0) basemem = 640; basemem_setup(); } if (physmap[1] != 0) goto physmap_done; /* * If we failed to find an SMAP, figure out the extended * memory size. We will then build a simple memory map with * two segments, one for "base memory" and the second for * "extended memory". Note that "extended memory" starts at a * physical address of 1MB and that both basemem and extmem * are in units of 1KB. * * First, try to fetch the extended memory size via INT 15:E801. */ vmf.vmf_ax = 0xE801; if (vm86_intcall(0x15, &vmf) == 0) { extmem = vmf.vmf_cx + vmf.vmf_dx * 64; } else { /* * If INT15:E801 fails, this is our last ditch effort * to determine the extended memory size. Currently * we prefer the RTC value over INT15:88. */ #if 0 vmf.vmf_ah = 0x88; vm86_intcall(0x15, &vmf); extmem = vmf.vmf_ax; #else extmem = rtcin(RTC_EXTLO) + (rtcin(RTC_EXTHI) << 8); #endif } /* * Special hack for chipsets that still remap the 384k hole when * there's 16MB of memory - this really confuses people that * are trying to use bus mastering ISA controllers with the * "16MB limit"; they only have 16MB, but the remapping puts * them beyond the limit. * * If extended memory is between 15-16MB (16-17MB phys address range), * chop it to 15MB. */ if ((extmem > 15 * 1024) && (extmem < 16 * 1024)) extmem = 15 * 1024; physmap[0] = 0; physmap[1] = basemem * 1024; physmap_idx = 2; physmap[physmap_idx] = 0x100000; physmap[physmap_idx + 1] = physmap[physmap_idx] + extmem * 1024; physmap_done: /* * Now, physmap contains a map of physical memory. */ #ifdef SMP /* make hole for AP bootstrap code */ alloc_ap_trampoline(physmap, &physmap_idx); #endif /* * Maxmem isn't the "maximum memory", it's one larger than the * highest page of the physical address space. It should be * called something like "Maxphyspage". We may adjust this * based on ``hw.physmem'' and the results of the memory test. * * This is especially confusing when it is much larger than the * memory size and is displayed as "realmem". */ Maxmem = atop(physmap[physmap_idx + 1]); #ifdef MAXMEM Maxmem = MAXMEM / 4; #endif if (TUNABLE_QUAD_FETCH("hw.physmem", &physmem_tunable)) Maxmem = atop(physmem_tunable); /* * If we have an SMAP, don't allow MAXMEM or hw.physmem to extend * the amount of memory in the system. */ if (has_smap && Maxmem > atop(physmap[physmap_idx + 1])) Maxmem = atop(physmap[physmap_idx + 1]); /* * The boot memory test is disabled by default, as it takes a * significant amount of time on large-memory systems, and is * unfriendly to virtual machines as it unnecessarily touches all * pages. * * A general name is used as the code may be extended to support * additional tests beyond the current "page present" test. */ memtest = 0; TUNABLE_ULONG_FETCH("hw.memtest.tests", &memtest); if (atop(physmap[physmap_idx + 1]) != Maxmem && (boothowto & RB_VERBOSE)) printf("Physical memory use set to %ldK\n", Maxmem * 4); /* * If Maxmem has been increased beyond what the system has detected, * extend the last memory segment to the new limit. */ if (atop(physmap[physmap_idx + 1]) < Maxmem) physmap[physmap_idx + 1] = ptoa((vm_paddr_t)Maxmem); /* call pmap initialization to make new kernel address space */ pmap_bootstrap(first); /* * Size up each available chunk of physical memory. */ physmap[0] = PAGE_SIZE; /* mask off page 0 */ pa_indx = 0; da_indx = 1; phys_avail[pa_indx++] = physmap[0]; phys_avail[pa_indx] = physmap[0]; dump_avail[da_indx] = physmap[0]; /* * Get dcons buffer address */ if (getenv_quad("dcons.addr", &dcons_addr) == 0 || getenv_quad("dcons.size", &dcons_size) == 0) dcons_addr = 0; /* * physmap is in bytes, so when converting to page boundaries, * round up the start address and round down the end address. */ for (i = 0; i <= physmap_idx; i += 2) { vm_paddr_t end; end = ptoa((vm_paddr_t)Maxmem); if (physmap[i + 1] < end) end = trunc_page(physmap[i + 1]); for (pa = round_page(physmap[i]); pa < end; pa += PAGE_SIZE) { int tmp, page_bad, full; int *ptr; full = FALSE; /* * block out kernel memory as not available. */ if (pa >= KERNLOAD && pa < first) goto do_dump_avail; /* * block out dcons buffer */ if (dcons_addr > 0 && pa >= trunc_page(dcons_addr) && pa < dcons_addr + dcons_size) goto do_dump_avail; page_bad = FALSE; if (memtest == 0) goto skip_memtest; /* * map page into kernel: valid, read/write,non-cacheable */ ptr = (int *)pmap_cmap3(pa, PG_V | PG_RW | PG_N); tmp = *(int *)ptr; /* * Test for alternating 1's and 0's */ *(volatile int *)ptr = 0xaaaaaaaa; if (*(volatile int *)ptr != 0xaaaaaaaa) page_bad = TRUE; /* * Test for alternating 0's and 1's */ *(volatile int *)ptr = 0x55555555; if (*(volatile int *)ptr != 0x55555555) page_bad = TRUE; /* * Test for all 1's */ *(volatile int *)ptr = 0xffffffff; if (*(volatile int *)ptr != 0xffffffff) page_bad = TRUE; /* * Test for all 0's */ *(volatile int *)ptr = 0x0; if (*(volatile int *)ptr != 0x0) page_bad = TRUE; /* * Restore original value. */ *(int *)ptr = tmp; skip_memtest: /* * Adjust array of valid/good pages. */ if (page_bad == TRUE) continue; /* * If this good page is a continuation of the * previous set of good pages, then just increase * the end pointer. Otherwise start a new chunk. * Note that "end" points one higher than end, * making the range >= start and < end. * If we're also doing a speculative memory * test and we at or past the end, bump up Maxmem * so that we keep going. The first bad page * will terminate the loop. */ if (phys_avail[pa_indx] == pa) { phys_avail[pa_indx] += PAGE_SIZE; } else { pa_indx++; if (pa_indx == PHYS_AVAIL_ENTRIES) { printf( "Too many holes in the physical address space, giving up\n"); pa_indx--; full = TRUE; goto do_dump_avail; } phys_avail[pa_indx++] = pa; /* start */ phys_avail[pa_indx] = pa + PAGE_SIZE; /* end */ } physmem++; do_dump_avail: if (dump_avail[da_indx] == pa) { dump_avail[da_indx] += PAGE_SIZE; } else { da_indx++; if (da_indx == PHYS_AVAIL_ENTRIES) { da_indx--; goto do_next; } dump_avail[da_indx++] = pa; /* start */ dump_avail[da_indx] = pa + PAGE_SIZE; /* end */ } do_next: if (full) break; } } pmap_cmap3(0, 0); /* * XXX * The last chunk must contain at least one page plus the message * buffer to avoid complicating other code (message buffer address * calculation, etc.). */ while (phys_avail[pa_indx - 1] + PAGE_SIZE + round_page(msgbufsize) >= phys_avail[pa_indx]) { physmem -= atop(phys_avail[pa_indx] - phys_avail[pa_indx - 1]); phys_avail[pa_indx--] = 0; phys_avail[pa_indx--] = 0; } Maxmem = atop(phys_avail[pa_indx]); /* Trim off space for the message buffer. */ phys_avail[pa_indx] -= round_page(msgbufsize); /* Map the message buffer. */ for (off = 0; off < round_page(msgbufsize); off += PAGE_SIZE) pmap_kenter((vm_offset_t)msgbufp + off, phys_avail[pa_indx] + off); } static void i386_kdb_init(void) { #ifdef DDB db_fetch_ksymtab(bootinfo.bi_symtab, bootinfo.bi_esymtab, 0); #endif kdb_init(); #ifdef KDB if (boothowto & RB_KDB) kdb_enter(KDB_WHY_BOOTFLAGS, "Boot flags requested debugger"); #endif } static void fixup_idt(void) { struct gate_descriptor *ip; uintptr_t off; int x; for (x = 0; x < NIDT; x++) { ip = &idt[x]; if (ip->gd_type != SDT_SYS386IGT && ip->gd_type != SDT_SYS386TGT) continue; off = ip->gd_looffset + (((u_int)ip->gd_hioffset) << 16); KASSERT(off >= (uintptr_t)start_exceptions && off < (uintptr_t)end_exceptions, ("IDT[%d] type %d off %#x", x, ip->gd_type, off)); off += setidt_disp; MPASS(off >= PMAP_TRM_MIN_ADDRESS && off < PMAP_TRM_MAX_ADDRESS); ip->gd_looffset = off; ip->gd_hioffset = off >> 16; } } static void i386_setidt1(void) { int x; /* exceptions */ for (x = 0; x < NIDT; x++) setidt(x, &IDTVEC(rsvd), SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_DE, &IDTVEC(div), SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_DB, &IDTVEC(dbg), SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_NMI, &IDTVEC(nmi), SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_BP, &IDTVEC(bpt), SDT_SYS386IGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_OF, &IDTVEC(ofl), SDT_SYS386IGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_BR, &IDTVEC(bnd), SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_UD, &IDTVEC(ill), SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_NM, &IDTVEC(dna), SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_DF, 0, SDT_SYSTASKGT, SEL_KPL, GSEL(GPANIC_SEL, SEL_KPL)); setidt(IDT_FPUGP, &IDTVEC(fpusegm), SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_TS, &IDTVEC(tss), SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_NP, &IDTVEC(missing), SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_SS, &IDTVEC(stk), SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_GP, &IDTVEC(prot), SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_PF, &IDTVEC(page), SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_MF, &IDTVEC(fpu), SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_AC, &IDTVEC(align), SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_MC, &IDTVEC(mchk), SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_XF, &IDTVEC(xmm), SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_SYSCALL, &IDTVEC(int0x80_syscall), SDT_SYS386IGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL)); #ifdef KDTRACE_HOOKS setidt(IDT_DTRACE_RET, &IDTVEC(dtrace_ret), SDT_SYS386IGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL)); #endif #ifdef XENHVM setidt(IDT_EVTCHN, &IDTVEC(xen_intr_upcall), SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); #endif } static void i386_setidt2(void) { setidt(IDT_UD, &IDTVEC(ill), SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_GP, &IDTVEC(prot), SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); } #if defined(DEV_ISA) && !defined(DEV_ATPIC) static void i386_setidt3(void) { setidt(IDT_IO_INTS + 7, IDTVEC(spuriousint), SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(IDT_IO_INTS + 15, IDTVEC(spuriousint), SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); } #endif register_t init386(int first) { struct region_descriptor r_gdt, r_idt; /* table descriptors */ int gsel_tss, metadata_missing, x, pa; struct pcpu *pc; struct xstate_hdr *xhdr; caddr_t kmdp; vm_offset_t addend; size_t ucode_len; thread0.td_kstack = proc0kstack; thread0.td_kstack_pages = TD0_KSTACK_PAGES; /* * This may be done better later if it gets more high level * components in it. If so just link td->td_proc here. */ proc_linkup0(&proc0, &thread0); if (bootinfo.bi_modulep) { metadata_missing = 0; addend = (vm_paddr_t)bootinfo.bi_modulep < KERNBASE ? PMAP_MAP_LOW : 0; preload_metadata = (caddr_t)bootinfo.bi_modulep + addend; preload_bootstrap_relocate(addend); } else { metadata_missing = 1; } if (bootinfo.bi_envp != 0) { addend = (vm_paddr_t)bootinfo.bi_envp < KERNBASE ? PMAP_MAP_LOW : 0; init_static_kenv((char *)bootinfo.bi_envp + addend, 0); } else { init_static_kenv(NULL, 0); } /* * Re-evaluate CPU features if we loaded a microcode update. */ ucode_len = ucode_load_bsp(first); if (ucode_len != 0) { identify_cpu(); first = roundup2(first + ucode_len, PAGE_SIZE); } identify_hypervisor(); identify_hypervisor_smbios(); /* Init basic tunables, hz etc */ init_param1(); /* Set bootmethod to BIOS: it's the only supported on i386. */ strlcpy(bootmethod, "BIOS", sizeof(bootmethod)); /* * Make gdt memory segments. All segments cover the full 4GB * of address space and permissions are enforced at page level. */ gdt_segs[GCODE_SEL].ssd_limit = atop(0 - 1); gdt_segs[GDATA_SEL].ssd_limit = atop(0 - 1); gdt_segs[GUCODE_SEL].ssd_limit = atop(0 - 1); gdt_segs[GUDATA_SEL].ssd_limit = atop(0 - 1); gdt_segs[GUFS_SEL].ssd_limit = atop(0 - 1); gdt_segs[GUGS_SEL].ssd_limit = atop(0 - 1); pc = &__pcpu[0]; gdt_segs[GPRIV_SEL].ssd_limit = atop(0 - 1); gdt_segs[GPRIV_SEL].ssd_base = (int)pc; gdt_segs[GPROC0_SEL].ssd_base = (int)&common_tss0; for (x = 0; x < NGDT; x++) ssdtosd(&gdt_segs[x], &gdt0[x].sd); r_gdt.rd_limit = NGDT * sizeof(gdt0[0]) - 1; r_gdt.rd_base = (int)gdt0; mtx_init(&dt_lock, "descriptor tables", NULL, MTX_SPIN); lgdt(&r_gdt); pcpu_init(pc, 0, sizeof(struct pcpu)); for (pa = first; pa < first + DPCPU_SIZE; pa += PAGE_SIZE) pmap_kenter(pa, pa); dpcpu_init((void *)first, 0); first += DPCPU_SIZE; PCPU_SET(prvspace, pc); PCPU_SET(curthread, &thread0); /* Non-late cninit() and printf() can be moved up to here. */ /* * Initialize mutexes. * * icu_lock: in order to allow an interrupt to occur in a critical * section, to set pcpu->ipending (etc...) properly, we * must be able to get the icu lock, so it can't be * under witness. */ mutex_init(); mtx_init(&icu_lock, "icu", NULL, MTX_SPIN | MTX_NOWITNESS | MTX_NOPROFILE); i386_setidt1(); r_idt.rd_limit = sizeof(idt0) - 1; r_idt.rd_base = (int) idt; lidt(&r_idt); finishidentcpu(); /* Final stage of CPU initialization */ /* * Initialize the clock before the console so that console * initialization can use DELAY(). */ clock_init(); i386_setidt2(); pmap_set_nx(); initializecpu(); /* Initialize CPU registers */ initializecpucache(); /* pointer to selector slot for %fs/%gs */ PCPU_SET(fsgs_gdt, &gdt[GUFS_SEL].sd); /* Initialize the tss (except for the final esp0) early for vm86. */ common_tss0.tss_esp0 = thread0.td_kstack + thread0.td_kstack_pages * PAGE_SIZE - VM86_STACK_SPACE; common_tss0.tss_ss0 = GSEL(GDATA_SEL, SEL_KPL); common_tss0.tss_ioopt = sizeof(struct i386tss) << 16; gsel_tss = GSEL(GPROC0_SEL, SEL_KPL); PCPU_SET(tss_gdt, &gdt[GPROC0_SEL].sd); PCPU_SET(common_tssd, *PCPU_GET(tss_gdt)); ltr(gsel_tss); /* Initialize the PIC early for vm86 calls. */ #ifdef DEV_ISA #ifdef DEV_ATPIC elcr_probe(); atpic_startup(); #else /* Reset and mask the atpics and leave them shut down. */ atpic_reset(); /* * Point the ICU spurious interrupt vectors at the APIC spurious * interrupt handler. */ i386_setidt3(); #endif #endif /* * The console and kdb should be initialized even earlier than here, * but some console drivers don't work until after getmemsize(). * Default to late console initialization to support these drivers. * This loses mainly printf()s in getmemsize() and early debugging. */ TUNABLE_INT_FETCH("debug.late_console", &late_console); if (!late_console) { cninit(); i386_kdb_init(); } kmdp = preload_search_by_type("elf kernel"); link_elf_ireloc(kmdp); vm86_initialize(); getmemsize(first); init_param2(physmem); /* now running on new page tables, configured,and u/iom is accessible */ if (late_console) cninit(); if (metadata_missing) printf("WARNING: loader(8) metadata is missing!\n"); if (late_console) i386_kdb_init(); msgbufinit(msgbufp, msgbufsize); npxinit(true); /* * Set up thread0 pcb after npxinit calculated pcb + fpu save * area size. Zero out the extended state header in fpu save * area. */ thread0.td_pcb = get_pcb_td(&thread0); thread0.td_pcb->pcb_save = get_pcb_user_save_td(&thread0); bzero(get_pcb_user_save_td(&thread0), cpu_max_ext_state_size); if (use_xsave) { xhdr = (struct xstate_hdr *)(get_pcb_user_save_td(&thread0) + 1); xhdr->xstate_bv = xsave_mask; } PCPU_SET(curpcb, thread0.td_pcb); /* Move esp0 in the tss to its final place. */ /* Note: -16 is so we can grow the trapframe if we came from vm86 */ common_tss0.tss_esp0 = (vm_offset_t)thread0.td_pcb - VM86_STACK_SPACE; PCPU_SET(kesp0, common_tss0.tss_esp0); gdt[GPROC0_SEL].sd.sd_type = SDT_SYS386TSS; /* clear busy bit */ ltr(gsel_tss); /* transfer to user mode */ _ucodesel = GSEL(GUCODE_SEL, SEL_UPL); _udatasel = GSEL(GUDATA_SEL, SEL_UPL); /* setup proc 0's pcb */ thread0.td_pcb->pcb_flags = 0; thread0.td_pcb->pcb_cr3 = pmap_get_kcr3(); thread0.td_pcb->pcb_ext = 0; thread0.td_frame = &proc0_tf; #ifdef FDT x86_init_fdt(); #endif /* Location of kernel stack for locore */ return ((register_t)thread0.td_pcb); } static void machdep_init_trampoline(void) { struct region_descriptor r_gdt, r_idt; struct i386tss *tss; char *copyout_buf, *trampoline, *tramp_stack_base; int x; gdt = pmap_trm_alloc(sizeof(union descriptor) * NGDT * mp_ncpus, M_NOWAIT | M_ZERO); bcopy(gdt0, gdt, sizeof(union descriptor) * NGDT); r_gdt.rd_limit = NGDT * sizeof(gdt[0]) - 1; r_gdt.rd_base = (int)gdt; lgdt(&r_gdt); tss = pmap_trm_alloc(sizeof(struct i386tss) * mp_ncpus, M_NOWAIT | M_ZERO); bcopy(&common_tss0, tss, sizeof(struct i386tss)); gdt[GPROC0_SEL].sd.sd_lobase = (int)tss; gdt[GPROC0_SEL].sd.sd_hibase = (u_int)tss >> 24; gdt[GPROC0_SEL].sd.sd_type = SDT_SYS386TSS; PCPU_SET(fsgs_gdt, &gdt[GUFS_SEL].sd); PCPU_SET(tss_gdt, &gdt[GPROC0_SEL].sd); PCPU_SET(common_tssd, *PCPU_GET(tss_gdt)); PCPU_SET(common_tssp, tss); ltr(GSEL(GPROC0_SEL, SEL_KPL)); trampoline = pmap_trm_alloc(end_exceptions - start_exceptions, M_NOWAIT); bcopy(start_exceptions, trampoline, end_exceptions - start_exceptions); tramp_stack_base = pmap_trm_alloc(TRAMP_STACK_SZ, M_NOWAIT); PCPU_SET(trampstk, (uintptr_t)tramp_stack_base + TRAMP_STACK_SZ - VM86_STACK_SPACE); tss[0].tss_esp0 = PCPU_GET(trampstk); idt = pmap_trm_alloc(sizeof(idt0), M_NOWAIT | M_ZERO); bcopy(idt0, idt, sizeof(idt0)); /* Re-initialize new IDT since the handlers were relocated */ setidt_disp = trampoline - start_exceptions; if (bootverbose) printf("Trampoline disposition %#zx\n", setidt_disp); fixup_idt(); r_idt.rd_limit = sizeof(struct gate_descriptor) * NIDT - 1; r_idt.rd_base = (int)idt; lidt(&r_idt); /* dblfault TSS */ dblfault_tss = pmap_trm_alloc(sizeof(struct i386tss), M_NOWAIT | M_ZERO); dblfault_stack = pmap_trm_alloc(PAGE_SIZE, M_NOWAIT); dblfault_tss->tss_esp = dblfault_tss->tss_esp0 = dblfault_tss->tss_esp1 = dblfault_tss->tss_esp2 = (int)dblfault_stack + PAGE_SIZE; dblfault_tss->tss_ss = dblfault_tss->tss_ss0 = dblfault_tss->tss_ss1 = dblfault_tss->tss_ss2 = GSEL(GDATA_SEL, SEL_KPL); dblfault_tss->tss_cr3 = pmap_get_kcr3(); dblfault_tss->tss_eip = (int)dblfault_handler; dblfault_tss->tss_eflags = PSL_KERNEL; dblfault_tss->tss_ds = dblfault_tss->tss_es = dblfault_tss->tss_gs = GSEL(GDATA_SEL, SEL_KPL); dblfault_tss->tss_fs = GSEL(GPRIV_SEL, SEL_KPL); dblfault_tss->tss_cs = GSEL(GCODE_SEL, SEL_KPL); dblfault_tss->tss_ldt = GSEL(GLDT_SEL, SEL_KPL); gdt[GPANIC_SEL].sd.sd_lobase = (int)dblfault_tss; gdt[GPANIC_SEL].sd.sd_hibase = (u_int)dblfault_tss >> 24; /* make ldt memory segments */ ldt = pmap_trm_alloc(sizeof(union descriptor) * NLDT, M_NOWAIT | M_ZERO); gdt[GLDT_SEL].sd.sd_lobase = (int)ldt; gdt[GLDT_SEL].sd.sd_hibase = (u_int)ldt >> 24; ldt_segs[LUCODE_SEL].ssd_limit = atop(0 - 1); ldt_segs[LUDATA_SEL].ssd_limit = atop(0 - 1); for (x = 0; x < nitems(ldt_segs); x++) ssdtosd(&ldt_segs[x], &ldt[x].sd); _default_ldt = GSEL(GLDT_SEL, SEL_KPL); lldt(_default_ldt); PCPU_SET(currentldt, _default_ldt); copyout_buf = pmap_trm_alloc(TRAMP_COPYOUT_SZ, M_NOWAIT); PCPU_SET(copyout_buf, copyout_buf); copyout_init_tramp(); } SYSINIT(vm_mem, SI_SUB_VM, SI_ORDER_SECOND, machdep_init_trampoline, NULL); #ifdef COMPAT_43 static void i386_setup_lcall_gate(void) { struct sysentvec *sv; struct user_segment_descriptor desc; u_int lcall_addr; sv = &elf32_freebsd_sysvec; lcall_addr = (uintptr_t)sv->sv_psstrings - sz_lcall_tramp; bzero(&desc, sizeof(desc)); desc.sd_type = SDT_MEMERA; desc.sd_dpl = SEL_UPL; desc.sd_p = 1; desc.sd_def32 = 1; desc.sd_gran = 1; desc.sd_lolimit = 0xffff; desc.sd_hilimit = 0xf; desc.sd_lobase = lcall_addr; desc.sd_hibase = lcall_addr >> 24; bcopy(&desc, &ldt[LSYS5CALLS_SEL], sizeof(desc)); } SYSINIT(elf32, SI_SUB_EXEC, SI_ORDER_ANY, i386_setup_lcall_gate, NULL); #endif void cpu_pcpu_init(struct pcpu *pcpu, int cpuid, size_t size) { pcpu->pc_acpi_id = 0xffffffff; } static int smap_sysctl_handler(SYSCTL_HANDLER_ARGS) { struct bios_smap *smapbase; struct bios_smap_xattr smap; caddr_t kmdp; uint32_t *smapattr; int count, error, i; /* Retrieve the system memory map from the loader. */ kmdp = preload_search_by_type("elf kernel"); if (kmdp == NULL) kmdp = preload_search_by_type("elf32 kernel"); smapbase = (struct bios_smap *)preload_search_info(kmdp, MODINFO_METADATA | MODINFOMD_SMAP); if (smapbase == NULL) return (0); smapattr = (uint32_t *)preload_search_info(kmdp, MODINFO_METADATA | MODINFOMD_SMAP_XATTR); count = *((u_int32_t *)smapbase - 1) / sizeof(*smapbase); error = 0; for (i = 0; i < count; i++) { smap.base = smapbase[i].base; smap.length = smapbase[i].length; smap.type = smapbase[i].type; if (smapattr != NULL) smap.xattr = smapattr[i]; else smap.xattr = 0; error = SYSCTL_OUT(req, &smap, sizeof(smap)); } return (error); } SYSCTL_PROC(_machdep, OID_AUTO, smap, CTLTYPE_OPAQUE | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0, smap_sysctl_handler, "S,bios_smap_xattr", "Raw BIOS SMAP data"); void spinlock_enter(void) { struct thread *td; register_t flags; td = curthread; if (td->td_md.md_spinlock_count == 0) { flags = intr_disable(); td->td_md.md_spinlock_count = 1; td->td_md.md_saved_flags = flags; critical_enter(); } else td->td_md.md_spinlock_count++; } void spinlock_exit(void) { struct thread *td; register_t flags; td = curthread; flags = td->td_md.md_saved_flags; td->td_md.md_spinlock_count--; if (td->td_md.md_spinlock_count == 0) { critical_exit(); intr_restore(flags); } } #if defined(I586_CPU) && !defined(NO_F00F_HACK) static void f00f_hack(void *unused); SYSINIT(f00f_hack, SI_SUB_INTRINSIC, SI_ORDER_FIRST, f00f_hack, NULL); static void f00f_hack(void *unused) { struct region_descriptor r_idt; struct gate_descriptor *new_idt; vm_offset_t tmp; if (!has_f00f_bug) return; printf("Intel Pentium detected, installing workaround for F00F bug\n"); tmp = (vm_offset_t)pmap_trm_alloc(PAGE_SIZE * 3, M_NOWAIT | M_ZERO); if (tmp == 0) panic("kmem_malloc returned 0"); tmp = round_page(tmp); /* Put the problematic entry (#6) at the end of the lower page. */ new_idt = (struct gate_descriptor *) (tmp + PAGE_SIZE - 7 * sizeof(struct gate_descriptor)); bcopy(idt, new_idt, sizeof(idt0)); r_idt.rd_base = (u_int)new_idt; r_idt.rd_limit = sizeof(idt0) - 1; lidt(&r_idt); /* SMP machines do not need the F00F hack. */ idt = new_idt; pmap_protect(kernel_pmap, tmp, tmp + PAGE_SIZE, VM_PROT_READ); } #endif /* defined(I586_CPU) && !NO_F00F_HACK */ /* * Construct a PCB from a trapframe. This is called from kdb_trap() where * we want to start a backtrace from the function that caused us to enter * the debugger. We have the context in the trapframe, but base the trace * on the PCB. The PCB doesn't have to be perfect, as long as it contains * enough for a backtrace. */ void makectx(struct trapframe *tf, struct pcb *pcb) { pcb->pcb_edi = tf->tf_edi; pcb->pcb_esi = tf->tf_esi; pcb->pcb_ebp = tf->tf_ebp; pcb->pcb_ebx = tf->tf_ebx; pcb->pcb_eip = tf->tf_eip; pcb->pcb_esp = (ISPL(tf->tf_cs)) ? tf->tf_esp : (int)(tf + 1) - 8; pcb->pcb_gs = rgs(); } #ifdef KDB /* * Provide inb() and outb() as functions. They are normally only available as * inline functions, thus cannot be called from the debugger. */ /* silence compiler warnings */ u_char inb_(u_short); void outb_(u_short, u_char); u_char inb_(u_short port) { return inb(port); } void outb_(u_short port, u_char data) { outb(port, data); } #endif /* KDB */ diff --git a/sys/i386/i386/sys_machdep.c b/sys/i386/i386/sys_machdep.c index a0a1c273f467..eeff4b280696 100644 --- a/sys/i386/i386/sys_machdep.c +++ b/sys/i386/i386/sys_machdep.c @@ -1,811 +1,807 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)sys_machdep.c 5.5 (Berkeley) 1/19/91 */ #include __FBSDID("$FreeBSD$"); #include "opt_capsicum.h" #include "opt_kstack_pages.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for kernel_map */ #define MAX_LD 8192 #define LD_PER_PAGE 512 #define NEW_MAX_LD(num) rounddown2(num + LD_PER_PAGE, LD_PER_PAGE) #define SIZE_FROM_LARGEST_LD(num) (NEW_MAX_LD(num) << 3) #define NULL_LDT_BASE ((caddr_t)NULL) #ifdef SMP static void set_user_ldt_rv(void *arg); #endif static int i386_set_ldt_data(struct thread *, int start, int num, union descriptor *descs); static int i386_ldt_grow(struct thread *td, int len); void fill_based_sd(struct segment_descriptor *sdp, uint32_t base) { sdp->sd_lobase = base & 0xffffff; sdp->sd_hibase = (base >> 24) & 0xff; sdp->sd_lolimit = 0xffff; /* 4GB limit, wraps around */ sdp->sd_hilimit = 0xf; sdp->sd_type = SDT_MEMRWA; sdp->sd_dpl = SEL_UPL; sdp->sd_p = 1; sdp->sd_xx = 0; sdp->sd_def32 = 1; sdp->sd_gran = 1; } /* * Construct special descriptors for "base" selectors. Store them in * the PCB for later use by cpu_switch(). Store them in the GDT for * more immediate use. The GDT entries are part of the current * context. Callers must load related segment registers to complete * setting up the current context. */ void set_fsbase(struct thread *td, uint32_t base) { struct segment_descriptor sd; fill_based_sd(&sd, base); critical_enter(); td->td_pcb->pcb_fsd = sd; if (td == curthread) PCPU_GET(fsgs_gdt)[0] = sd; critical_exit(); } void set_gsbase(struct thread *td, uint32_t base) { struct segment_descriptor sd; fill_based_sd(&sd, base); critical_enter(); td->td_pcb->pcb_gsd = sd; if (td == curthread) PCPU_GET(fsgs_gdt)[1] = sd; critical_exit(); } #ifndef _SYS_SYSPROTO_H_ struct sysarch_args { int op; char *parms; }; #endif int sysarch(struct thread *td, struct sysarch_args *uap) { int error; union descriptor *lp; union { struct i386_ldt_args largs; struct i386_ioperm_args iargs; struct i386_get_xfpustate xfpu; } kargs; uint32_t base; struct segment_descriptor *sdp; AUDIT_ARG_CMD(uap->op); #ifdef CAPABILITY_MODE /* * When adding new operations, add a new case statement here to * explicitly indicate whether or not the operation is safe to * perform in capability mode. */ if (IN_CAPABILITY_MODE(td)) { switch (uap->op) { case I386_GET_LDT: case I386_SET_LDT: case I386_GET_IOPERM: case I386_GET_FSBASE: case I386_SET_FSBASE: case I386_GET_GSBASE: case I386_SET_GSBASE: case I386_GET_XFPUSTATE: break; case I386_SET_IOPERM: default: #ifdef KTRACE if (KTRPOINT(td, KTR_CAPFAIL)) ktrcapfail(CAPFAIL_SYSCALL, NULL, NULL); #endif return (ECAPMODE); } } #endif switch (uap->op) { case I386_GET_IOPERM: case I386_SET_IOPERM: if ((error = copyin(uap->parms, &kargs.iargs, sizeof(struct i386_ioperm_args))) != 0) return (error); break; case I386_GET_LDT: case I386_SET_LDT: if ((error = copyin(uap->parms, &kargs.largs, sizeof(struct i386_ldt_args))) != 0) return (error); break; case I386_GET_XFPUSTATE: if ((error = copyin(uap->parms, &kargs.xfpu, sizeof(struct i386_get_xfpustate))) != 0) return (error); break; default: break; } switch (uap->op) { case I386_GET_LDT: error = i386_get_ldt(td, &kargs.largs); break; case I386_SET_LDT: if (kargs.largs.descs != NULL) { if (kargs.largs.num > MAX_LD) return (EINVAL); lp = malloc(kargs.largs.num * sizeof(union descriptor), M_TEMP, M_WAITOK); error = copyin(kargs.largs.descs, lp, kargs.largs.num * sizeof(union descriptor)); if (error == 0) error = i386_set_ldt(td, &kargs.largs, lp); free(lp, M_TEMP); } else { error = i386_set_ldt(td, &kargs.largs, NULL); } break; case I386_GET_IOPERM: error = i386_get_ioperm(td, &kargs.iargs); if (error == 0) error = copyout(&kargs.iargs, uap->parms, sizeof(struct i386_ioperm_args)); break; case I386_SET_IOPERM: error = i386_set_ioperm(td, &kargs.iargs); break; case I386_VM86: error = vm86_sysarch(td, uap->parms); break; case I386_GET_FSBASE: sdp = &td->td_pcb->pcb_fsd; base = sdp->sd_hibase << 24 | sdp->sd_lobase; error = copyout(&base, uap->parms, sizeof(base)); break; case I386_SET_FSBASE: error = copyin(uap->parms, &base, sizeof(base)); if (error == 0) { /* * Construct the special descriptor for fsbase * and arrange for doreti to load its selector * soon enough. */ set_fsbase(td, base); td->td_frame->tf_fs = GSEL(GUFS_SEL, SEL_UPL); } break; case I386_GET_GSBASE: sdp = &td->td_pcb->pcb_gsd; base = sdp->sd_hibase << 24 | sdp->sd_lobase; error = copyout(&base, uap->parms, sizeof(base)); break; case I386_SET_GSBASE: error = copyin(uap->parms, &base, sizeof(base)); if (error == 0) { /* * Construct the special descriptor for gsbase. * The selector is loaded immediately, since we * normally only reload %gs on context switches. */ set_gsbase(td, base); load_gs(GSEL(GUGS_SEL, SEL_UPL)); } break; case I386_GET_XFPUSTATE: if (kargs.xfpu.len > cpu_max_ext_state_size - sizeof(union savefpu)) return (EINVAL); npxgetregs(td); error = copyout((char *)(get_pcb_user_save_td(td) + 1), kargs.xfpu.addr, kargs.xfpu.len); break; default: error = EINVAL; break; } return (error); } int i386_extend_pcb(struct thread *td) { int i, offset; u_long *addr; struct pcb_ext *ext; struct soft_segment_descriptor ssd = { 0, /* segment base address (overwritten) */ ctob(IOPAGES + 1) - 1, /* length */ SDT_SYS386TSS, /* segment type */ 0, /* priority level */ 1, /* descriptor present */ 0, 0, 0, /* default 32 size */ 0 /* granularity */ }; ext = pmap_trm_alloc(ctob(IOPAGES + 1), M_WAITOK | M_ZERO); /* -16 is so we can convert a trapframe into vm86trapframe inplace */ ext->ext_tss.tss_ss0 = GSEL(GDATA_SEL, SEL_KPL); /* * The last byte of the i/o map must be followed by an 0xff byte. * We arbitrarily allocate 16 bytes here, to keep the starting * address on a doubleword boundary. */ offset = PAGE_SIZE - 16; ext->ext_tss.tss_ioopt = (offset - ((unsigned)&ext->ext_tss - (unsigned)ext)) << 16; ext->ext_iomap = (caddr_t)ext + offset; ext->ext_vm86.vm86_intmap = (caddr_t)ext + offset - 32; addr = (u_long *)ext->ext_vm86.vm86_intmap; for (i = 0; i < (ctob(IOPAGES) + 32 + 16) / sizeof(u_long); i++) *addr++ = ~0; ssd.ssd_base = (unsigned)&ext->ext_tss; ssd.ssd_limit -= ((unsigned)&ext->ext_tss - (unsigned)ext); ssdtosd(&ssd, &ext->ext_tssd); KASSERT(td == curthread, ("giving TSS to !curthread")); KASSERT(td->td_pcb->pcb_ext == 0, ("already have a TSS!")); /* Switch to the new TSS. */ critical_enter(); ext->ext_tss.tss_esp0 = PCPU_GET(trampstk); td->td_pcb->pcb_ext = ext; PCPU_SET(private_tss, 1); *PCPU_GET(tss_gdt) = ext->ext_tssd; ltr(GSEL(GPROC0_SEL, SEL_KPL)); critical_exit(); return 0; } int -i386_set_ioperm(td, uap) - struct thread *td; - struct i386_ioperm_args *uap; +i386_set_ioperm(struct thread *td, struct i386_ioperm_args *uap) { char *iomap; u_int i; int error; if ((error = priv_check(td, PRIV_IO)) != 0) return (error); if ((error = securelevel_gt(td->td_ucred, 0)) != 0) return (error); /* * XXX * While this is restricted to root, we should probably figure out * whether any other driver is using this i/o address, as so not to * cause confusion. This probably requires a global 'usage registry'. */ if (td->td_pcb->pcb_ext == 0) if ((error = i386_extend_pcb(td)) != 0) return (error); iomap = (char *)td->td_pcb->pcb_ext->ext_iomap; if (uap->start > uap->start + uap->length || uap->start + uap->length > IOPAGES * PAGE_SIZE * NBBY) return (EINVAL); for (i = uap->start; i < uap->start + uap->length; i++) { if (uap->enable) iomap[i >> 3] &= ~(1 << (i & 7)); else iomap[i >> 3] |= (1 << (i & 7)); } return (error); } int -i386_get_ioperm(td, uap) - struct thread *td; - struct i386_ioperm_args *uap; +i386_get_ioperm(struct thread *td, struct i386_ioperm_args *uap) { int i, state; char *iomap; if (uap->start >= IOPAGES * PAGE_SIZE * NBBY) return (EINVAL); if (td->td_pcb->pcb_ext == 0) { uap->length = 0; goto done; } iomap = (char *)td->td_pcb->pcb_ext->ext_iomap; i = uap->start; state = (iomap[i >> 3] >> (i & 7)) & 1; uap->enable = !state; uap->length = 1; for (i = uap->start + 1; i < IOPAGES * PAGE_SIZE * NBBY; i++) { if (state != ((iomap[i >> 3] >> (i & 7)) & 1)) break; uap->length++; } done: return (0); } /* * Update the GDT entry pointing to the LDT to point to the LDT of the * current process. Manage dt_lock holding/unholding autonomously. */ static void set_user_ldt_locked(struct mdproc *mdp) { struct proc_ldt *pldt; int gdt_idx; mtx_assert(&dt_lock, MA_OWNED); pldt = mdp->md_ldt; gdt_idx = GUSERLDT_SEL; gdt_idx += PCPU_GET(cpuid) * NGDT; /* always 0 on UP */ gdt[gdt_idx].sd = pldt->ldt_sd; lldt(GSEL(GUSERLDT_SEL, SEL_KPL)); PCPU_SET(currentldt, GSEL(GUSERLDT_SEL, SEL_KPL)); } void set_user_ldt(struct mdproc *mdp) { mtx_lock_spin(&dt_lock); set_user_ldt_locked(mdp); mtx_unlock_spin(&dt_lock); } #ifdef SMP static void set_user_ldt_rv(void *arg) { struct proc *p; p = curproc; if (arg == p->p_vmspace) set_user_ldt(&p->p_md); } #endif /* * dt_lock must be held. Returns with dt_lock held. */ struct proc_ldt * user_ldt_alloc(struct mdproc *mdp, int len) { struct proc_ldt *pldt, *new_ldt; mtx_assert(&dt_lock, MA_OWNED); mtx_unlock_spin(&dt_lock); new_ldt = malloc(sizeof(struct proc_ldt), M_SUBPROC, M_WAITOK); new_ldt->ldt_len = len = NEW_MAX_LD(len); new_ldt->ldt_base = pmap_trm_alloc(len * sizeof(union descriptor), M_WAITOK | M_ZERO); new_ldt->ldt_refcnt = 1; new_ldt->ldt_active = 0; mtx_lock_spin(&dt_lock); gdt_segs[GUSERLDT_SEL].ssd_base = (unsigned)new_ldt->ldt_base; gdt_segs[GUSERLDT_SEL].ssd_limit = len * sizeof(union descriptor) - 1; ssdtosd(&gdt_segs[GUSERLDT_SEL], &new_ldt->ldt_sd); if ((pldt = mdp->md_ldt) != NULL) { if (len > pldt->ldt_len) len = pldt->ldt_len; bcopy(pldt->ldt_base, new_ldt->ldt_base, len * sizeof(union descriptor)); } else bcopy(ldt, new_ldt->ldt_base, sizeof(union descriptor) * NLDT); return (new_ldt); } /* * Must be called with dt_lock held. Returns with dt_lock unheld. */ void user_ldt_free(struct thread *td) { struct mdproc *mdp; struct proc_ldt *pldt; mtx_assert(&dt_lock, MA_OWNED); mdp = &td->td_proc->p_md; if ((pldt = mdp->md_ldt) == NULL) { mtx_unlock_spin(&dt_lock); return; } if (td == curthread) { lldt(_default_ldt); PCPU_SET(currentldt, _default_ldt); } mdp->md_ldt = NULL; user_ldt_deref(pldt); } void user_ldt_deref(struct proc_ldt *pldt) { mtx_assert(&dt_lock, MA_OWNED); if (--pldt->ldt_refcnt == 0) { mtx_unlock_spin(&dt_lock); pmap_trm_free(pldt->ldt_base, pldt->ldt_len * sizeof(union descriptor)); free(pldt, M_SUBPROC); } else mtx_unlock_spin(&dt_lock); } /* * Note for the authors of compat layers (linux, etc): copyout() in * the function below is not a problem since it presents data in * arch-specific format (i.e. i386-specific in this case), not in * the OS-specific one. */ int i386_get_ldt(struct thread *td, struct i386_ldt_args *uap) { struct proc_ldt *pldt; char *data; u_int nldt, num; int error; #ifdef DEBUG printf("i386_get_ldt: start=%u num=%u descs=%p\n", uap->start, uap->num, (void *)uap->descs); #endif num = min(uap->num, MAX_LD); data = malloc(num * sizeof(union descriptor), M_TEMP, M_WAITOK); mtx_lock_spin(&dt_lock); pldt = td->td_proc->p_md.md_ldt; nldt = pldt != NULL ? pldt->ldt_len : NLDT; if (uap->start >= nldt) { num = 0; } else { num = min(num, nldt - uap->start); bcopy(pldt != NULL ? &((union descriptor *)(pldt->ldt_base))[uap->start] : &ldt[uap->start], data, num * sizeof(union descriptor)); } mtx_unlock_spin(&dt_lock); error = copyout(data, uap->descs, num * sizeof(union descriptor)); if (error == 0) td->td_retval[0] = num; free(data, M_TEMP); return (error); } int i386_set_ldt(struct thread *td, struct i386_ldt_args *uap, union descriptor *descs) { struct mdproc *mdp; struct proc_ldt *pldt; union descriptor *dp; u_int largest_ld, i; int error; #ifdef DEBUG printf("i386_set_ldt: start=%u num=%u descs=%p\n", uap->start, uap->num, (void *)uap->descs); #endif error = 0; mdp = &td->td_proc->p_md; if (descs == NULL) { /* Free descriptors */ if (uap->start == 0 && uap->num == 0) { /* * Treat this as a special case, so userland needn't * know magic number NLDT. */ uap->start = NLDT; uap->num = MAX_LD - NLDT; } mtx_lock_spin(&dt_lock); if ((pldt = mdp->md_ldt) == NULL || uap->start >= pldt->ldt_len) { mtx_unlock_spin(&dt_lock); return (0); } largest_ld = uap->start + uap->num; if (largest_ld > pldt->ldt_len) largest_ld = pldt->ldt_len; for (i = uap->start; i < largest_ld; i++) atomic_store_rel_64(&((uint64_t *)(pldt->ldt_base))[i], 0); mtx_unlock_spin(&dt_lock); return (0); } if (uap->start != LDT_AUTO_ALLOC || uap->num != 1) { /* verify range of descriptors to modify */ largest_ld = uap->start + uap->num; if (uap->start >= MAX_LD || largest_ld > MAX_LD) return (EINVAL); } /* Check descriptors for access violations */ for (i = 0; i < uap->num; i++) { dp = &descs[i]; switch (dp->sd.sd_type) { case SDT_SYSNULL: /* system null */ dp->sd.sd_p = 0; break; case SDT_SYS286TSS: /* system 286 TSS available */ case SDT_SYSLDT: /* system local descriptor table */ case SDT_SYS286BSY: /* system 286 TSS busy */ case SDT_SYSTASKGT: /* system task gate */ case SDT_SYS286IGT: /* system 286 interrupt gate */ case SDT_SYS286TGT: /* system 286 trap gate */ case SDT_SYSNULL2: /* undefined by Intel */ case SDT_SYS386TSS: /* system 386 TSS available */ case SDT_SYSNULL3: /* undefined by Intel */ case SDT_SYS386BSY: /* system 386 TSS busy */ case SDT_SYSNULL4: /* undefined by Intel */ case SDT_SYS386IGT: /* system 386 interrupt gate */ case SDT_SYS386TGT: /* system 386 trap gate */ case SDT_SYS286CGT: /* system 286 call gate */ case SDT_SYS386CGT: /* system 386 call gate */ return (EACCES); /* memory segment types */ case SDT_MEMEC: /* memory execute only conforming */ case SDT_MEMEAC: /* memory execute only accessed conforming */ case SDT_MEMERC: /* memory execute read conforming */ case SDT_MEMERAC: /* memory execute read accessed conforming */ /* Must be "present" if executable and conforming. */ if (dp->sd.sd_p == 0) return (EACCES); break; case SDT_MEMRO: /* memory read only */ case SDT_MEMROA: /* memory read only accessed */ case SDT_MEMRW: /* memory read write */ case SDT_MEMRWA: /* memory read write accessed */ case SDT_MEMROD: /* memory read only expand dwn limit */ case SDT_MEMRODA: /* memory read only expand dwn lim accessed */ case SDT_MEMRWD: /* memory read write expand dwn limit */ case SDT_MEMRWDA: /* memory read write expand dwn lim acessed */ case SDT_MEME: /* memory execute only */ case SDT_MEMEA: /* memory execute only accessed */ case SDT_MEMER: /* memory execute read */ case SDT_MEMERA: /* memory execute read accessed */ break; default: return (EINVAL); } /* Only user (ring-3) descriptors may be present. */ if (dp->sd.sd_p != 0 && dp->sd.sd_dpl != SEL_UPL) return (EACCES); } if (uap->start == LDT_AUTO_ALLOC && uap->num == 1) { /* Allocate a free slot */ mtx_lock_spin(&dt_lock); if ((pldt = mdp->md_ldt) == NULL) { if ((error = i386_ldt_grow(td, NLDT + 1))) { mtx_unlock_spin(&dt_lock); return (error); } pldt = mdp->md_ldt; } again: /* * start scanning a bit up to leave room for NVidia and * Wine, which still user the "Blat" method of allocation. */ dp = &((union descriptor *)(pldt->ldt_base))[NLDT]; for (i = NLDT; i < pldt->ldt_len; ++i) { if (dp->sd.sd_type == SDT_SYSNULL) break; dp++; } if (i >= pldt->ldt_len) { if ((error = i386_ldt_grow(td, pldt->ldt_len+1))) { mtx_unlock_spin(&dt_lock); return (error); } goto again; } uap->start = i; error = i386_set_ldt_data(td, i, 1, descs); mtx_unlock_spin(&dt_lock); } else { largest_ld = uap->start + uap->num; mtx_lock_spin(&dt_lock); if (!(error = i386_ldt_grow(td, largest_ld))) { error = i386_set_ldt_data(td, uap->start, uap->num, descs); } mtx_unlock_spin(&dt_lock); } if (error == 0) td->td_retval[0] = uap->start; return (error); } static int i386_set_ldt_data(struct thread *td, int start, int num, union descriptor *descs) { struct mdproc *mdp; struct proc_ldt *pldt; uint64_t *dst, *src; int i; mtx_assert(&dt_lock, MA_OWNED); mdp = &td->td_proc->p_md; pldt = mdp->md_ldt; dst = (uint64_t *)(pldt->ldt_base); src = (uint64_t *)descs; /* * Atomic(9) is used only to get 64bit atomic store with * cmpxchg8b when available. There is no op without release * semantic. */ for (i = 0; i < num; i++) atomic_store_rel_64(&dst[start + i], src[i]); return (0); } static int i386_ldt_grow(struct thread *td, int len) { struct mdproc *mdp; struct proc_ldt *new_ldt, *pldt; caddr_t old_ldt_base; int old_ldt_len; mtx_assert(&dt_lock, MA_OWNED); if (len > MAX_LD) return (ENOMEM); if (len < NLDT + 1) len = NLDT + 1; mdp = &td->td_proc->p_md; old_ldt_base = NULL_LDT_BASE; old_ldt_len = 0; /* Allocate a user ldt. */ if ((pldt = mdp->md_ldt) == NULL || len > pldt->ldt_len) { new_ldt = user_ldt_alloc(mdp, len); if (new_ldt == NULL) return (ENOMEM); pldt = mdp->md_ldt; if (pldt != NULL) { if (new_ldt->ldt_len <= pldt->ldt_len) { /* * We just lost the race for allocation, so * free the new object and return. */ mtx_unlock_spin(&dt_lock); pmap_trm_free(new_ldt->ldt_base, new_ldt->ldt_len * sizeof(union descriptor)); free(new_ldt, M_SUBPROC); mtx_lock_spin(&dt_lock); return (0); } /* * We have to substitute the current LDT entry for * curproc with the new one since its size grew. */ old_ldt_base = pldt->ldt_base; old_ldt_len = pldt->ldt_len; pldt->ldt_sd = new_ldt->ldt_sd; pldt->ldt_base = new_ldt->ldt_base; pldt->ldt_len = new_ldt->ldt_len; } else mdp->md_ldt = pldt = new_ldt; #ifdef SMP /* * Signal other cpus to reload ldt. We need to unlock dt_lock * here because other CPU will contest on it since their * curthreads won't hold the lock and will block when trying * to acquire it. */ mtx_unlock_spin(&dt_lock); smp_rendezvous(NULL, set_user_ldt_rv, NULL, td->td_proc->p_vmspace); #else set_user_ldt_locked(&td->td_proc->p_md); mtx_unlock_spin(&dt_lock); #endif if (old_ldt_base != NULL_LDT_BASE) { pmap_trm_free(old_ldt_base, old_ldt_len * sizeof(union descriptor)); free(new_ldt, M_SUBPROC); } mtx_lock_spin(&dt_lock); } return (0); }