diff options
Diffstat (limited to 'tools')
63 files changed, 1553 insertions, 209 deletions
diff --git a/tools/lib/api/Build b/tools/lib/api/Build index 954c644f7ad9..6e2373db5598 100644 --- a/tools/lib/api/Build +++ b/tools/lib/api/Build @@ -2,3 +2,8 @@ libapi-y += fd/ libapi-y += fs/ libapi-y += cpu.o libapi-y += debug.o +libapi-y += str_error_r.o + +$(OUTPUT)str_error_r.o: ../str_error_r.c FORCE + $(call rule_mkdir) + $(call if_changed_dep,cc_o_c) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 32e6b6bc6f7d..b699aea9a025 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -37,6 +37,10 @@ #include "libbpf.h" #include "bpf.h" +#ifndef EM_BPF +#define EM_BPF 247 +#endif + #define __printf(a, b) __attribute__((format(printf, a, b))) __printf(1, 2) @@ -439,7 +443,8 @@ static int bpf_object__elf_init(struct bpf_object *obj) } ep = &obj->efile.ehdr; - if ((ep->e_type != ET_REL) || (ep->e_machine != 0)) { + /* Old LLVM set e_machine to EM_NONE */ + if ((ep->e_type != ET_REL) || (ep->e_machine && (ep->e_machine != EM_BPF))) { pr_warning("%s is not an eBPF object file\n", obj->path); err = -LIBBPF_ERRNO__FORMAT; diff --git a/tools/objtool/.gitignore b/tools/objtool/.gitignore index a0b3128bb31f..d3102c865a95 100644 --- a/tools/objtool/.gitignore +++ b/tools/objtool/.gitignore @@ -1,2 +1,3 @@ arch/x86/insn/inat-tables.c objtool +fixdep diff --git a/tools/objtool/Makefile b/tools/objtool/Makefile index 0b437700f688..041b493ad3ab 100644 --- a/tools/objtool/Makefile +++ b/tools/objtool/Makefile @@ -51,7 +51,7 @@ $(OBJTOOL): $(LIBSUBCMD) $(OBJTOOL_IN) diff -I'^#include' arch/x86/insn/insn.h ../../arch/x86/include/asm/insn.h >/dev/null && \ diff -I'^#include' arch/x86/insn/inat.h ../../arch/x86/include/asm/inat.h >/dev/null && \ diff -I'^#include' arch/x86/insn/inat_types.h ../../arch/x86/include/asm/inat_types.h >/dev/null) \ - || echo "Warning: objtool: x86 instruction decoder differs from kernel" >&2 )) || true + || echo "warning: objtool: x86 instruction decoder differs from kernel" >&2 )) || true $(QUIET_LINK)$(CC) $(OBJTOOL_IN) $(LDFLAGS) -o $@ diff --git a/tools/objtool/arch/x86/insn/gen-insn-attr-x86.awk b/tools/objtool/arch/x86/insn/gen-insn-attr-x86.awk index 093a892026f9..a3d2c62fd805 100644 --- a/tools/objtool/arch/x86/insn/gen-insn-attr-x86.awk +++ b/tools/objtool/arch/x86/insn/gen-insn-attr-x86.awk @@ -72,12 +72,14 @@ BEGIN { lprefix_expr = "\\((66|F2|F3)\\)" max_lprefix = 4 - # All opcodes starting with lower-case 'v' or with (v1) superscript + # All opcodes starting with lower-case 'v', 'k' or with (v1) superscript # accepts VEX prefix - vexok_opcode_expr = "^v.*" + vexok_opcode_expr = "^[vk].*" vexok_expr = "\\(v1\\)" # All opcodes with (v) superscript supports *only* VEX prefix vexonly_expr = "\\(v\\)" + # All opcodes with (ev) superscript supports *only* EVEX prefix + evexonly_expr = "\\(ev\\)" prefix_expr = "\\(Prefix\\)" prefix_num["Operand-Size"] = "INAT_PFX_OPNDSZ" @@ -95,6 +97,7 @@ BEGIN { prefix_num["Address-Size"] = "INAT_PFX_ADDRSZ" prefix_num["VEX+1byte"] = "INAT_PFX_VEX2" prefix_num["VEX+2byte"] = "INAT_PFX_VEX3" + prefix_num["EVEX"] = "INAT_PFX_EVEX" clear_vars() } @@ -319,7 +322,9 @@ function convert_operands(count,opnd, i,j,imm,mod) flags = add_flags(flags, "INAT_MODRM") # check VEX codes - if (match(ext, vexonly_expr)) + if (match(ext, evexonly_expr)) + flags = add_flags(flags, "INAT_VEXOK | INAT_EVEXONLY") + else if (match(ext, vexonly_expr)) flags = add_flags(flags, "INAT_VEXOK | INAT_VEXONLY") else if (match(ext, vexok_expr) || match(opcode, vexok_opcode_expr)) flags = add_flags(flags, "INAT_VEXOK") diff --git a/tools/objtool/arch/x86/insn/inat.h b/tools/objtool/arch/x86/insn/inat.h index 611645e903a8..125ecd2a300d 100644 --- a/tools/objtool/arch/x86/insn/inat.h +++ b/tools/objtool/arch/x86/insn/inat.h @@ -48,6 +48,7 @@ /* AVX VEX prefixes */ #define INAT_PFX_VEX2 13 /* 2-bytes VEX prefix */ #define INAT_PFX_VEX3 14 /* 3-bytes VEX prefix */ +#define INAT_PFX_EVEX 15 /* EVEX prefix */ #define INAT_LSTPFX_MAX 3 #define INAT_LGCPFX_MAX 11 @@ -89,6 +90,7 @@ #define INAT_VARIANT (1 << (INAT_FLAG_OFFS + 4)) #define INAT_VEXOK (1 << (INAT_FLAG_OFFS + 5)) #define INAT_VEXONLY (1 << (INAT_FLAG_OFFS + 6)) +#define INAT_EVEXONLY (1 << (INAT_FLAG_OFFS + 7)) /* Attribute making macros for attribute tables */ #define INAT_MAKE_PREFIX(pfx) (pfx << INAT_PFX_OFFS) #define INAT_MAKE_ESCAPE(esc) (esc << INAT_ESC_OFFS) @@ -141,7 +143,13 @@ static inline int inat_last_prefix_id(insn_attr_t attr) static inline int inat_is_vex_prefix(insn_attr_t attr) { attr &= INAT_PFX_MASK; - return attr == INAT_PFX_VEX2 || attr == INAT_PFX_VEX3; + return attr == INAT_PFX_VEX2 || attr == INAT_PFX_VEX3 || + attr == INAT_PFX_EVEX; +} + +static inline int inat_is_evex_prefix(insn_attr_t attr) +{ + return (attr & INAT_PFX_MASK) == INAT_PFX_EVEX; } static inline int inat_is_vex3_prefix(insn_attr_t attr) @@ -216,6 +224,11 @@ static inline int inat_accept_vex(insn_attr_t attr) static inline int inat_must_vex(insn_attr_t attr) { - return attr & INAT_VEXONLY; + return attr & (INAT_VEXONLY | INAT_EVEXONLY); +} + +static inline int inat_must_evex(insn_attr_t attr) +{ + return attr & INAT_EVEXONLY; } #endif diff --git a/tools/objtool/arch/x86/insn/insn.c b/tools/objtool/arch/x86/insn/insn.c index 9f26eae6c9f0..ca983e2bea8b 100644 --- a/tools/objtool/arch/x86/insn/insn.c +++ b/tools/objtool/arch/x86/insn/insn.c @@ -155,14 +155,24 @@ found: /* * In 32-bits mode, if the [7:6] bits (mod bits of * ModRM) on the second byte are not 11b, it is - * LDS or LES. + * LDS or LES or BOUND. */ if (X86_MODRM_MOD(b2) != 3) goto vex_end; } insn->vex_prefix.bytes[0] = b; insn->vex_prefix.bytes[1] = b2; - if (inat_is_vex3_prefix(attr)) { + if (inat_is_evex_prefix(attr)) { + b2 = peek_nbyte_next(insn_byte_t, insn, 2); + insn->vex_prefix.bytes[2] = b2; + b2 = peek_nbyte_next(insn_byte_t, insn, 3); + insn->vex_prefix.bytes[3] = b2; + insn->vex_prefix.nbytes = 4; + insn->next_byte += 4; + if (insn->x86_64 && X86_VEX_W(b2)) + /* VEX.W overrides opnd_size */ + insn->opnd_bytes = 8; + } else if (inat_is_vex3_prefix(attr)) { b2 = peek_nbyte_next(insn_byte_t, insn, 2); insn->vex_prefix.bytes[2] = b2; insn->vex_prefix.nbytes = 3; @@ -221,7 +231,9 @@ void insn_get_opcode(struct insn *insn) m = insn_vex_m_bits(insn); p = insn_vex_p_bits(insn); insn->attr = inat_get_avx_attribute(op, m, p); - if (!inat_accept_vex(insn->attr) && !inat_is_group(insn->attr)) + if ((inat_must_evex(insn->attr) && !insn_is_evex(insn)) || + (!inat_accept_vex(insn->attr) && + !inat_is_group(insn->attr))) insn->attr = 0; /* This instruction is bad */ goto end; /* VEX has only 1 byte for opcode */ } diff --git a/tools/objtool/arch/x86/insn/insn.h b/tools/objtool/arch/x86/insn/insn.h index dd12da0f4593..e23578c7b1be 100644 --- a/tools/objtool/arch/x86/insn/insn.h +++ b/tools/objtool/arch/x86/insn/insn.h @@ -91,6 +91,7 @@ struct insn { #define X86_VEX_B(vex) ((vex) & 0x20) /* VEX3 Byte1 */ #define X86_VEX_L(vex) ((vex) & 0x04) /* VEX3 Byte2, VEX2 Byte1 */ /* VEX bit fields */ +#define X86_EVEX_M(vex) ((vex) & 0x03) /* EVEX Byte1 */ #define X86_VEX3_M(vex) ((vex) & 0x1f) /* VEX3 Byte1 */ #define X86_VEX2_M 1 /* VEX2.M always 1 */ #define X86_VEX_V(vex) (((vex) & 0x78) >> 3) /* VEX3 Byte2, VEX2 Byte1 */ @@ -133,6 +134,13 @@ static inline int insn_is_avx(struct insn *insn) return (insn->vex_prefix.value != 0); } +static inline int insn_is_evex(struct insn *insn) +{ + if (!insn->prefixes.got) + insn_get_prefixes(insn); + return (insn->vex_prefix.nbytes == 4); +} + /* Ensure this instruction is decoded completely */ static inline int insn_complete(struct insn *insn) { @@ -144,8 +152,10 @@ static inline insn_byte_t insn_vex_m_bits(struct insn *insn) { if (insn->vex_prefix.nbytes == 2) /* 2 bytes VEX */ return X86_VEX2_M; - else + else if (insn->vex_prefix.nbytes == 3) /* 3 bytes VEX */ return X86_VEX3_M(insn->vex_prefix.bytes[1]); + else /* EVEX */ + return X86_EVEX_M(insn->vex_prefix.bytes[1]); } static inline insn_byte_t insn_vex_p_bits(struct insn *insn) diff --git a/tools/objtool/arch/x86/insn/x86-opcode-map.txt b/tools/objtool/arch/x86/insn/x86-opcode-map.txt index 28632ee68377..767be7c76034 100644 --- a/tools/objtool/arch/x86/insn/x86-opcode-map.txt +++ b/tools/objtool/arch/x86/insn/x86-opcode-map.txt @@ -13,12 +13,17 @@ # opcode: escape # escaped-name # EndTable # +# mnemonics that begin with lowercase 'v' accept a VEX or EVEX prefix +# mnemonics that begin with lowercase 'k' accept a VEX prefix +# #<group maps> # GrpTable: GrpXXX # reg: mnemonic [operand1[,operand2...]] [(extra1)[,(extra2)...] [| 2nd-mnemonic ...] # EndTable # # AVX Superscripts +# (ev): this opcode requires EVEX prefix. +# (evo): this opcode is changed by EVEX prefix (EVEX opcode) # (v): this opcode requires VEX prefix. # (v1): this opcode only supports 128bit VEX. # @@ -137,7 +142,7 @@ AVXcode: # 0x60 - 0x6f 60: PUSHA/PUSHAD (i64) 61: POPA/POPAD (i64) -62: BOUND Gv,Ma (i64) +62: BOUND Gv,Ma (i64) | EVEX (Prefix) 63: ARPL Ew,Gw (i64) | MOVSXD Gv,Ev (o64) 64: SEG=FS (Prefix) 65: SEG=GS (Prefix) @@ -399,17 +404,17 @@ AVXcode: 1 3f: # 0x0f 0x40-0x4f 40: CMOVO Gv,Ev -41: CMOVNO Gv,Ev -42: CMOVB/C/NAE Gv,Ev +41: CMOVNO Gv,Ev | kandw/q Vk,Hk,Uk | kandb/d Vk,Hk,Uk (66) +42: CMOVB/C/NAE Gv,Ev | kandnw/q Vk,Hk,Uk | kandnb/d Vk,Hk,Uk (66) 43: CMOVAE/NB/NC Gv,Ev -44: CMOVE/Z Gv,Ev -45: CMOVNE/NZ Gv,Ev -46: CMOVBE/NA Gv,Ev -47: CMOVA/NBE Gv,Ev +44: CMOVE/Z Gv,Ev | knotw/q Vk,Uk | knotb/d Vk,Uk (66) +45: CMOVNE/NZ Gv,Ev | korw/q Vk,Hk,Uk | korb/d Vk,Hk,Uk (66) +46: CMOVBE/NA Gv,Ev | kxnorw/q Vk,Hk,Uk | kxnorb/d Vk,Hk,Uk (66) +47: CMOVA/NBE Gv,Ev | kxorw/q Vk,Hk,Uk | kxorb/d Vk,Hk,Uk (66) 48: CMOVS Gv,Ev 49: CMOVNS Gv,Ev -4a: CMOVP/PE Gv,Ev -4b: CMOVNP/PO Gv,Ev +4a: CMOVP/PE Gv,Ev | kaddw/q Vk,Hk,Uk | kaddb/d Vk,Hk,Uk (66) +4b: CMOVNP/PO Gv,Ev | kunpckbw Vk,Hk,Uk (66) | kunpckwd/dq Vk,Hk,Uk 4c: CMOVL/NGE Gv,Ev 4d: CMOVNL/GE Gv,Ev 4e: CMOVLE/NG Gv,Ev @@ -426,7 +431,7 @@ AVXcode: 1 58: vaddps Vps,Hps,Wps | vaddpd Vpd,Hpd,Wpd (66) | vaddss Vss,Hss,Wss (F3),(v1) | vaddsd Vsd,Hsd,Wsd (F2),(v1) 59: vmulps Vps,Hps,Wps | vmulpd Vpd,Hpd,Wpd (66) | vmulss Vss,Hss,Wss (F3),(v1) | vmulsd Vsd,Hsd,Wsd (F2),(v1) 5a: vcvtps2pd Vpd,Wps | vcvtpd2ps Vps,Wpd (66) | vcvtss2sd Vsd,Hx,Wss (F3),(v1) | vcvtsd2ss Vss,Hx,Wsd (F2),(v1) -5b: vcvtdq2ps Vps,Wdq | vcvtps2dq Vdq,Wps (66) | vcvttps2dq Vdq,Wps (F3) +5b: vcvtdq2ps Vps,Wdq | vcvtqq2ps Vps,Wqq (evo) | vcvtps2dq Vdq,Wps (66) | vcvttps2dq Vdq,Wps (F3) 5c: vsubps Vps,Hps,Wps | vsubpd Vpd,Hpd,Wpd (66) | vsubss Vss,Hss,Wss (F3),(v1) | vsubsd Vsd,Hsd,Wsd (F2),(v1) 5d: vminps Vps,Hps,Wps | vminpd Vpd,Hpd,Wpd (66) | vminss Vss,Hss,Wss (F3),(v1) | vminsd Vsd,Hsd,Wsd (F2),(v1) 5e: vdivps Vps,Hps,Wps | vdivpd Vpd,Hpd,Wpd (66) | vdivss Vss,Hss,Wss (F3),(v1) | vdivsd Vsd,Hsd,Wsd (F2),(v1) @@ -447,7 +452,7 @@ AVXcode: 1 6c: vpunpcklqdq Vx,Hx,Wx (66),(v1) 6d: vpunpckhqdq Vx,Hx,Wx (66),(v1) 6e: movd/q Pd,Ey | vmovd/q Vy,Ey (66),(v1) -6f: movq Pq,Qq | vmovdqa Vx,Wx (66) | vmovdqu Vx,Wx (F3) +6f: movq Pq,Qq | vmovdqa Vx,Wx (66) | vmovdqa32/64 Vx,Wx (66),(evo) | vmovdqu Vx,Wx (F3) | vmovdqu32/64 Vx,Wx (F3),(evo) | vmovdqu8/16 Vx,Wx (F2),(ev) # 0x0f 0x70-0x7f 70: pshufw Pq,Qq,Ib | vpshufd Vx,Wx,Ib (66),(v1) | vpshufhw Vx,Wx,Ib (F3),(v1) | vpshuflw Vx,Wx,Ib (F2),(v1) 71: Grp12 (1A) @@ -458,14 +463,14 @@ AVXcode: 1 76: pcmpeqd Pq,Qq | vpcmpeqd Vx,Hx,Wx (66),(v1) # Note: Remove (v), because vzeroall and vzeroupper becomes emms without VEX. 77: emms | vzeroupper | vzeroall -78: VMREAD Ey,Gy -79: VMWRITE Gy,Ey -7a: -7b: +78: VMREAD Ey,Gy | vcvttps2udq/pd2udq Vx,Wpd (evo) | vcvttsd2usi Gv,Wx (F2),(ev) | vcvttss2usi Gv,Wx (F3),(ev) | vcvttps2uqq/pd2uqq Vx,Wx (66),(ev) +79: VMWRITE Gy,Ey | vcvtps2udq/pd2udq Vx,Wpd (evo) | vcvtsd2usi Gv,Wx (F2),(ev) | vcvtss2usi Gv,Wx (F3),(ev) | vcvtps2uqq/pd2uqq Vx,Wx (66),(ev) +7a: vcvtudq2pd/uqq2pd Vpd,Wx (F3),(ev) | vcvtudq2ps/uqq2ps Vpd,Wx (F2),(ev) | vcvttps2qq/pd2qq Vx,Wx (66),(ev) +7b: vcvtusi2sd Vpd,Hpd,Ev (F2),(ev) | vcvtusi2ss Vps,Hps,Ev (F3),(ev) | vcvtps2qq/pd2qq Vx,Wx (66),(ev) 7c: vhaddpd Vpd,Hpd,Wpd (66) | vhaddps Vps,Hps,Wps (F2) 7d: vhsubpd Vpd,Hpd,Wpd (66) | vhsubps Vps,Hps,Wps (F2) 7e: movd/q Ey,Pd | vmovd/q Ey,Vy (66),(v1) | vmovq Vq,Wq (F3),(v1) -7f: movq Qq,Pq | vmovdqa Wx,Vx (66) | vmovdqu Wx,Vx (F3) +7f: movq Qq,Pq | vmovdqa Wx,Vx (66) | vmovdqa32/64 Wx,Vx (66),(evo) | vmovdqu Wx,Vx (F3) | vmovdqu32/64 Wx,Vx (F3),(evo) | vmovdqu8/16 Wx,Vx (F2),(ev) # 0x0f 0x80-0x8f # Note: "forced64" is Intel CPU behavior (see comment about CALL insn). 80: JO Jz (f64) @@ -485,16 +490,16 @@ AVXcode: 1 8e: JLE/JNG Jz (f64) 8f: JNLE/JG Jz (f64) # 0x0f 0x90-0x9f -90: SETO Eb -91: SETNO Eb -92: SETB/C/NAE Eb -93: SETAE/NB/NC Eb +90: SETO Eb | kmovw/q Vk,Wk | kmovb/d Vk,Wk (66) +91: SETNO Eb | kmovw/q Mv,Vk | kmovb/d Mv,Vk (66) +92: SETB/C/NAE Eb | kmovw Vk,Rv | kmovb Vk,Rv (66) | kmovq/d Vk,Rv (F2) +93: SETAE/NB/NC Eb | kmovw Gv,Uk | kmovb Gv,Uk (66) | kmovq/d Gv,Uk (F2) 94: SETE/Z Eb 95: SETNE/NZ Eb 96: SETBE/NA Eb 97: SETA/NBE Eb -98: SETS Eb -99: SETNS Eb +98: SETS Eb | kortestw/q Vk,Uk | kortestb/d Vk,Uk (66) +99: SETNS Eb | ktestw/q Vk,Uk | ktestb/d Vk,Uk (66) 9a: SETP/PE Eb 9b: SETNP/PO Eb 9c: SETL/NGE Eb @@ -564,11 +569,11 @@ d7: pmovmskb Gd,Nq | vpmovmskb Gd,Ux (66),(v1) d8: psubusb Pq,Qq | vpsubusb Vx,Hx,Wx (66),(v1) d9: psubusw Pq,Qq | vpsubusw Vx,Hx,Wx (66),(v1) da: pminub Pq,Qq | vpminub Vx,Hx,Wx (66),(v1) -db: pand Pq,Qq | vpand Vx,Hx,Wx (66),(v1) +db: pand Pq,Qq | vpand Vx,Hx,Wx (66),(v1) | vpandd/q Vx,Hx,Wx (66),(evo) dc: paddusb Pq,Qq | vpaddusb Vx,Hx,Wx (66),(v1) dd: paddusw Pq,Qq | vpaddusw Vx,Hx,Wx (66),(v1) de: pmaxub Pq,Qq | vpmaxub Vx,Hx,Wx (66),(v1) -df: pandn Pq,Qq | vpandn Vx,Hx,Wx (66),(v1) +df: pandn Pq,Qq | vpandn Vx,Hx,Wx (66),(v1) | vpandnd/q Vx,Hx,Wx (66),(evo) # 0x0f 0xe0-0xef e0: pavgb Pq,Qq | vpavgb Vx,Hx,Wx (66),(v1) e1: psraw Pq,Qq | vpsraw Vx,Hx,Wx (66),(v1) @@ -576,16 +581,16 @@ e2: psrad Pq,Qq | vpsrad Vx,Hx,Wx (66),(v1) e3: pavgw Pq,Qq | vpavgw Vx,Hx,Wx (66),(v1) e4: pmulhuw Pq,Qq | vpmulhuw Vx,Hx,Wx (66),(v1) e5: pmulhw Pq,Qq | vpmulhw Vx,Hx,Wx (66),(v1) -e6: vcvttpd2dq Vx,Wpd (66) | vcvtdq2pd Vx,Wdq (F3) | vcvtpd2dq Vx,Wpd (F2) +e6: vcvttpd2dq Vx,Wpd (66) | vcvtdq2pd Vx,Wdq (F3) | vcvtdq2pd/qq2pd Vx,Wdq (F3),(evo) | vcvtpd2dq Vx,Wpd (F2) e7: movntq Mq,Pq | vmovntdq Mx,Vx (66) e8: psubsb Pq,Qq | vpsubsb Vx,Hx,Wx (66),(v1) e9: psubsw Pq,Qq | vpsubsw Vx,Hx,Wx (66),(v1) ea: pminsw Pq,Qq | vpminsw Vx,Hx,Wx (66),(v1) -eb: por Pq,Qq | vpor Vx,Hx,Wx (66),(v1) +eb: por Pq,Qq | vpor Vx,Hx,Wx (66),(v1) | vpord/q Vx,Hx,Wx (66),(evo) ec: paddsb Pq,Qq | vpaddsb Vx,Hx,Wx (66),(v1) ed: paddsw Pq,Qq | vpaddsw Vx,Hx,Wx (66),(v1) ee: pmaxsw Pq,Qq | vpmaxsw Vx,Hx,Wx (66),(v1) -ef: pxor Pq,Qq | vpxor Vx,Hx,Wx (66),(v1) +ef: pxor Pq,Qq | vpxor Vx,Hx,Wx (66),(v1) | vpxord/q Vx,Hx,Wx (66),(evo) # 0x0f 0xf0-0xff f0: vlddqu Vx,Mx (F2) f1: psllw Pq,Qq | vpsllw Vx,Hx,Wx (66),(v1) @@ -626,81 +631,105 @@ AVXcode: 2 0e: vtestps Vx,Wx (66),(v) 0f: vtestpd Vx,Wx (66),(v) # 0x0f 0x38 0x10-0x1f -10: pblendvb Vdq,Wdq (66) -11: -12: -13: vcvtph2ps Vx,Wx,Ib (66),(v) -14: blendvps Vdq,Wdq (66) -15: blendvpd Vdq,Wdq (66) -16: vpermps Vqq,Hqq,Wqq (66),(v) +10: pblendvb Vdq,Wdq (66) | vpsrlvw Vx,Hx,Wx (66),(evo) | vpmovuswb Wx,Vx (F3),(ev) +11: vpmovusdb Wx,Vd (F3),(ev) | vpsravw Vx,Hx,Wx (66),(ev) +12: vpmovusqb Wx,Vq (F3),(ev) | vpsllvw Vx,Hx,Wx (66),(ev) +13: vcvtph2ps Vx,Wx (66),(v) | vpmovusdw Wx,Vd (F3),(ev) +14: blendvps Vdq,Wdq (66) | vpmovusqw Wx,Vq (F3),(ev) | vprorvd/q Vx,Hx,Wx (66),(evo) +15: blendvpd Vdq,Wdq (66) | vpmovusqd Wx,Vq (F3),(ev) | vprolvd/q Vx,Hx,Wx (66),(evo) +16: vpermps Vqq,Hqq,Wqq (66),(v) | vpermps/d Vqq,Hqq,Wqq (66),(evo) 17: vptest Vx,Wx (66) 18: vbroadcastss Vx,Wd (66),(v) -19: vbroadcastsd Vqq,Wq (66),(v) -1a: vbroadcastf128 Vqq,Mdq (66),(v) -1b: +19: vbroadcastsd Vqq,Wq (66),(v) | vbroadcastf32x2 Vqq,Wq (66),(evo) +1a: vbroadcastf128 Vqq,Mdq (66),(v) | vbroadcastf32x4/64x2 Vqq,Wq (66),(evo) +1b: vbroadcastf32x8/64x4 Vqq,Mdq (66),(ev) 1c: pabsb Pq,Qq | vpabsb Vx,Wx (66),(v1) 1d: pabsw Pq,Qq | vpabsw Vx,Wx (66),(v1) 1e: pabsd Pq,Qq | vpabsd Vx,Wx (66),(v1) -1f: +1f: vpabsq Vx,Wx (66),(ev) # 0x0f 0x38 0x20-0x2f -20: vpmovsxbw Vx,Ux/Mq (66),(v1) -21: vpmovsxbd Vx,Ux/Md (66),(v1) -22: vpmovsxbq Vx,Ux/Mw (66),(v1) -23: vpmovsxwd Vx,Ux/Mq (66),(v1) -24: vpmovsxwq Vx,Ux/Md (66),(v1) -25: vpmovsxdq Vx,Ux/Mq (66),(v1) -26: -27: -28: vpmuldq Vx,Hx,Wx (66),(v1) -29: vpcmpeqq Vx,Hx,Wx (66),(v1) -2a: vmovntdqa Vx,Mx (66),(v1) +20: vpmovsxbw Vx,Ux/Mq (66),(v1) | vpmovswb Wx,Vx (F3),(ev) +21: vpmovsxbd Vx,Ux/Md (66),(v1) | vpmovsdb Wx,Vd (F3),(ev) +22: vpmovsxbq Vx,Ux/Mw (66),(v1) | vpmovsqb Wx,Vq (F3),(ev) +23: vpmovsxwd Vx,Ux/Mq (66),(v1) | vpmovsdw Wx,Vd (F3),(ev) +24: vpmovsxwq Vx,Ux/Md (66),(v1) | vpmovsqw Wx,Vq (F3),(ev) +25: vpmovsxdq Vx,Ux/Mq (66),(v1) | vpmovsqd Wx,Vq (F3),(ev) +26: vptestmb/w Vk,Hx,Wx (66),(ev) | vptestnmb/w Vk,Hx,Wx (F3),(ev) +27: vptestmd/q Vk,Hx,Wx (66),(ev) | vptestnmd/q Vk,Hx,Wx (F3),(ev) +28: vpmuldq Vx,Hx,Wx (66),(v1) | vpmovm2b/w Vx,Uk (F3),(ev) +29: vpcmpeqq Vx,Hx,Wx (66),(v1) | vpmovb2m/w2m Vk,Ux (F3),(ev) +2a: vmovntdqa Vx,Mx (66),(v1) | vpbroadcastmb2q Vx,Uk (F3),(ev) 2b: vpackusdw Vx,Hx,Wx (66),(v1) -2c: vmaskmovps Vx,Hx,Mx (66),(v) -2d: vmaskmovpd Vx,Hx,Mx (66),(v) +2c: vmaskmovps Vx,Hx,Mx (66),(v) | vscalefps/d Vx,Hx,Wx (66),(evo) +2d: vmaskmovpd Vx,Hx,Mx (66),(v) | vscalefss/d Vx,Hx,Wx (66),(evo) 2e: vmaskmovps Mx,Hx,Vx (66),(v) 2f: vmaskmovpd Mx,Hx,Vx (66),(v) # 0x0f 0x38 0x30-0x3f -30: vpmovzxbw Vx,Ux/Mq (66),(v1) -31: vpmovzxbd Vx,Ux/Md (66),(v1) -32: vpmovzxbq Vx,Ux/Mw (66),(v1) -33: vpmovzxwd Vx,Ux/Mq (66),(v1) -34: vpmovzxwq Vx,Ux/Md (66),(v1) -35: vpmovzxdq Vx,Ux/Mq (66),(v1) -36: vpermd Vqq,Hqq,Wqq (66),(v) +30: vpmovzxbw Vx,Ux/Mq (66),(v1) | vpmovwb Wx,Vx (F3),(ev) +31: vpmovzxbd Vx,Ux/Md (66),(v1) | vpmovdb Wx,Vd (F3),(ev) +32: vpmovzxbq Vx,Ux/Mw (66),(v1) | vpmovqb Wx,Vq (F3),(ev) +33: vpmovzxwd Vx,Ux/Mq (66),(v1) | vpmovdw Wx,Vd (F3),(ev) +34: vpmovzxwq Vx,Ux/Md (66),(v1) | vpmovqw Wx,Vq (F3),(ev) +35: vpmovzxdq Vx,Ux/Mq (66),(v1) | vpmovqd Wx,Vq (F3),(ev) +36: vpermd Vqq,Hqq,Wqq (66),(v) | vpermd/q Vqq,Hqq,Wqq (66),(evo) 37: vpcmpgtq Vx,Hx,Wx (66),(v1) -38: vpminsb Vx,Hx,Wx (66),(v1) -39: vpminsd Vx,Hx,Wx (66),(v1) -3a: vpminuw Vx,Hx,Wx (66),(v1) -3b: vpminud Vx,Hx,Wx (66),(v1) +38: vpminsb Vx,Hx,Wx (66),(v1) | vpmovm2d/q Vx,Uk (F3),(ev) +39: vpminsd Vx,Hx,Wx (66),(v1) | vpminsd/q Vx,Hx,Wx (66),(evo) | vpmovd2m/q2m Vk,Ux (F3),(ev) +3a: vpminuw Vx,Hx,Wx (66),(v1) | vpbroadcastmw2d Vx,Uk (F3),(ev) +3b: vpminud Vx,Hx,Wx (66),(v1) | vpminud/q Vx,Hx,Wx (66),(evo) 3c: vpmaxsb Vx,Hx,Wx (66),(v1) -3d: vpmaxsd Vx,Hx,Wx (66),(v1) +3d: vpmaxsd Vx,Hx,Wx (66),(v1) | vpmaxsd/q Vx,Hx,Wx (66),(evo) 3e: vpmaxuw Vx,Hx,Wx (66),(v1) -3f: vpmaxud Vx,Hx,Wx (66),(v1) +3f: vpmaxud Vx,Hx,Wx (66),(v1) | vpmaxud/q Vx,Hx,Wx (66),(evo) # 0x0f 0x38 0x40-0x8f -40: vpmulld Vx,Hx,Wx (66),(v1) +40: vpmulld Vx,Hx,Wx (66),(v1) | vpmulld/q Vx,Hx,Wx (66),(evo) 41: vphminposuw Vdq,Wdq (66),(v1) -42: -43: -44: +42: vgetexpps/d Vx,Wx (66),(ev) +43: vgetexpss/d Vx,Hx,Wx (66),(ev) +44: vplzcntd/q Vx,Wx (66),(ev) 45: vpsrlvd/q Vx,Hx,Wx (66),(v) -46: vpsravd Vx,Hx,Wx (66),(v) +46: vpsravd Vx,Hx,Wx (66),(v) | vpsravd/q Vx,Hx,Wx (66),(evo) 47: vpsllvd/q Vx,Hx,Wx (66),(v) -# Skip 0x48-0x57 +# Skip 0x48-0x4b +4c: vrcp14ps/d Vpd,Wpd (66),(ev) +4d: vrcp14ss/d Vsd,Hpd,Wsd (66),(ev) +4e: vrsqrt14ps/d Vpd,Wpd (66),(ev) +4f: vrsqrt14ss/d Vsd,Hsd,Wsd (66),(ev) +# Skip 0x50-0x57 58: vpbroadcastd Vx,Wx (66),(v) -59: vpbroadcastq Vx,Wx (66),(v) -5a: vbroadcasti128 Vqq,Mdq (66),(v) -# Skip 0x5b-0x77 +59: vpbroadcastq Vx,Wx (66),(v) | vbroadcasti32x2 Vx,Wx (66),(evo) +5a: vbroadcasti128 Vqq,Mdq (66),(v) | vbroadcasti32x4/64x2 Vx,Wx (66),(evo) +5b: vbroadcasti32x8/64x4 Vqq,Mdq (66),(ev) +# Skip 0x5c-0x63 +64: vpblendmd/q Vx,Hx,Wx (66),(ev) +65: vblendmps/d Vx,Hx,Wx (66),(ev) +66: vpblendmb/w Vx,Hx,Wx (66),(ev) +# Skip 0x67-0x74 +75: vpermi2b/w Vx,Hx,Wx (66),(ev) +76: vpermi2d/q Vx,Hx,Wx (66),(ev) +77: vpermi2ps/d Vx,Hx,Wx (66),(ev) 78: vpbroadcastb Vx,Wx (66),(v) 79: vpbroadcastw Vx,Wx (66),(v) -# Skip 0x7a-0x7f +7a: vpbroadcastb Vx,Rv (66),(ev) +7b: vpbroadcastw Vx,Rv (66),(ev) +7c: vpbroadcastd/q Vx,Rv (66),(ev) +7d: vpermt2b/w Vx,Hx,Wx (66),(ev) +7e: vpermt2d/q Vx,Hx,Wx (66),(ev) +7f: vpermt2ps/d Vx,Hx,Wx (66),(ev) 80: INVEPT Gy,Mdq (66) 81: INVPID Gy,Mdq (66) 82: INVPCID Gy,Mdq (66) +83: vpmultishiftqb Vx,Hx,Wx (66),(ev) +88: vexpandps/d Vpd,Wpd (66),(ev) +89: vpexpandd/q Vx,Wx (66),(ev) +8a: vcompressps/d Wx,Vx (66),(ev) +8b: vpcompressd/q Wx,Vx (66),(ev) 8c: vpmaskmovd/q Vx,Hx,Mx (66),(v) +8d: vpermb/w Vx,Hx,Wx (66),(ev) 8e: vpmaskmovd/q Mx,Vx,Hx (66),(v) # 0x0f 0x38 0x90-0xbf (FMA) -90: vgatherdd/q Vx,Hx,Wx (66),(v) -91: vgatherqd/q Vx,Hx,Wx (66),(v) +90: vgatherdd/q Vx,Hx,Wx (66),(v) | vpgatherdd/q Vx,Wx (66),(evo) +91: vgatherqd/q Vx,Hx,Wx (66),(v) | vpgatherqd/q Vx,Wx (66),(evo) 92: vgatherdps/d Vx,Hx,Wx (66),(v) 93: vgatherqps/d Vx,Hx,Wx (66),(v) 94: @@ -715,6 +744,10 @@ AVXcode: 2 9d: vfnmadd132ss/d Vx,Hx,Wx (66),(v),(v1) 9e: vfnmsub132ps/d Vx,Hx,Wx (66),(v) 9f: vfnmsub132ss/d Vx,Hx,Wx (66),(v),(v1) +a0: vpscatterdd/q Wx,Vx (66),(ev) +a1: vpscatterqd/q Wx,Vx (66),(ev) +a2: vscatterdps/d Wx,Vx (66),(ev) +a3: vscatterqps/d Wx,Vx (66),(ev) a6: vfmaddsub213ps/d Vx,Hx,Wx (66),(v) a7: vfmsubadd213ps/d Vx,Hx,Wx (66),(v) a8: vfmadd213ps/d Vx,Hx,Wx (66),(v) @@ -725,6 +758,8 @@ ac: vfnmadd213ps/d Vx,Hx,Wx (66),(v) ad: vfnmadd213ss/d Vx,Hx,Wx (66),(v),(v1) ae: vfnmsub213ps/d Vx,Hx,Wx (66),(v) af: vfnmsub213ss/d Vx,Hx,Wx (66),(v),(v1) +b4: vpmadd52luq Vx,Hx,Wx (66),(ev) +b5: vpmadd52huq Vx,Hx,Wx (66),(ev) b6: vfmaddsub231ps/d Vx,Hx,Wx (66),(v) b7: vfmsubadd231ps/d Vx,Hx,Wx (66),(v) b8: vfmadd231ps/d Vx,Hx,Wx (66),(v) @@ -736,12 +771,15 @@ bd: vfnmadd231ss/d Vx,Hx,Wx (66),(v),(v1) be: vfnmsub231ps/d Vx,Hx,Wx (66),(v) bf: vfnmsub231ss/d Vx,Hx,Wx (66),(v),(v1) # 0x0f 0x38 0xc0-0xff -c8: sha1nexte Vdq,Wdq +c4: vpconflictd/q Vx,Wx (66),(ev) +c6: Grp18 (1A) +c7: Grp19 (1A) +c8: sha1nexte Vdq,Wdq | vexp2ps/d Vx,Wx (66),(ev) c9: sha1msg1 Vdq,Wdq -ca: sha1msg2 Vdq,Wdq -cb: sha256rnds2 Vdq,Wdq -cc: sha256msg1 Vdq,Wdq -cd: sha256msg2 Vdq,Wdq +ca: sha1msg2 Vdq,Wdq | vrcp28ps/d Vx,Wx (66),(ev) +cb: sha256rnds2 Vdq,Wdq | vrcp28ss/d Vx,Hx,Wx (66),(ev) +cc: sha256msg1 Vdq,Wdq | vrsqrt28ps/d Vx,Wx (66),(ev) +cd: sha256msg2 Vdq,Wdq | vrsqrt28ss/d Vx,Hx,Wx (66),(ev) db: VAESIMC Vdq,Wdq (66),(v1) dc: VAESENC Vdq,Hdq,Wdq (66),(v1) dd: VAESENCLAST Vdq,Hdq,Wdq (66),(v1) @@ -763,15 +801,15 @@ AVXcode: 3 00: vpermq Vqq,Wqq,Ib (66),(v) 01: vpermpd Vqq,Wqq,Ib (66),(v) 02: vpblendd Vx,Hx,Wx,Ib (66),(v) -03: +03: valignd/q Vx,Hx,Wx,Ib (66),(ev) 04: vpermilps Vx,Wx,Ib (66),(v) 05: vpermilpd Vx,Wx,Ib (66),(v) 06: vperm2f128 Vqq,Hqq,Wqq,Ib (66),(v) 07: -08: vroundps Vx,Wx,Ib (66) -09: vroundpd Vx,Wx,Ib (66) -0a: vroundss Vss,Wss,Ib (66),(v1) -0b: vroundsd Vsd,Wsd,Ib (66),(v1) +08: vroundps Vx,Wx,Ib (66) | vrndscaleps Vx,Wx,Ib (66),(evo) +09: vroundpd Vx,Wx,Ib (66) | vrndscalepd Vx,Wx,Ib (66),(evo) +0a: vroundss Vss,Wss,Ib (66),(v1) | vrndscaless Vx,Hx,Wx,Ib (66),(evo) +0b: vroundsd Vsd,Wsd,Ib (66),(v1) | vrndscalesd Vx,Hx,Wx,Ib (66),(evo) 0c: vblendps Vx,Hx,Wx,Ib (66) 0d: vblendpd Vx,Hx,Wx,Ib (66) 0e: vpblendw Vx,Hx,Wx,Ib (66),(v1) @@ -780,26 +818,51 @@ AVXcode: 3 15: vpextrw Rd/Mw,Vdq,Ib (66),(v1) 16: vpextrd/q Ey,Vdq,Ib (66),(v1) 17: vextractps Ed,Vdq,Ib (66),(v1) -18: vinsertf128 Vqq,Hqq,Wqq,Ib (66),(v) -19: vextractf128 Wdq,Vqq,Ib (66),(v) +18: vinsertf128 Vqq,Hqq,Wqq,Ib (66),(v) | vinsertf32x4/64x2 Vqq,Hqq,Wqq,Ib (66),(evo) +19: vextractf128 Wdq,Vqq,Ib (66),(v) | vextractf32x4/64x2 Wdq,Vqq,Ib (66),(evo) +1a: vinsertf32x8/64x4 Vqq,Hqq,Wqq,Ib (66),(ev) +1b: vextractf32x8/64x4 Wdq,Vqq,Ib (66),(ev) 1d: vcvtps2ph Wx,Vx,Ib (66),(v) +1e: vpcmpud/q Vk,Hd,Wd,Ib (66),(ev) +1f: vpcmpd/q Vk,Hd,Wd,Ib (66),(ev) 20: vpinsrb Vdq,Hdq,Ry/Mb,Ib (66),(v1) 21: vinsertps Vdq,Hdq,Udq/Md,Ib (66),(v1) 22: vpinsrd/q Vdq,Hdq,Ey,Ib (66),(v1) -38: vinserti128 Vqq,Hqq,Wqq,Ib (66),(v) -39: vextracti128 Wdq,Vqq,Ib (66),(v) +23: vshuff32x4/64x2 Vx,Hx,Wx,Ib (66),(ev) +25: vpternlogd/q Vx,Hx,Wx,Ib (66),(ev) +26: vgetmantps/d Vx,Wx,Ib (66),(ev) +27: vgetmantss/d Vx,Hx,Wx,Ib (66),(ev) +30: kshiftrb/w Vk,Uk,Ib (66),(v) +31: kshiftrd/q Vk,Uk,Ib (66),(v) +32: kshiftlb/w Vk,Uk,Ib (66),(v) +33: kshiftld/q Vk,Uk,Ib (66),(v) +38: vinserti128 Vqq,Hqq,Wqq,Ib (66),(v) | vinserti32x4/64x2 Vqq,Hqq,Wqq,Ib (66),(evo) +39: vextracti128 Wdq,Vqq,Ib (66),(v) | vextracti32x4/64x2 Wdq,Vqq,Ib (66),(evo) +3a: vinserti32x8/64x4 Vqq,Hqq,Wqq,Ib (66),(ev) +3b: vextracti32x8/64x4 Wdq,Vqq,Ib (66),(ev) +3e: vpcmpub/w Vk,Hk,Wx,Ib (66),(ev) +3f: vpcmpb/w Vk,Hk,Wx,Ib (66),(ev) 40: vdpps Vx,Hx,Wx,Ib (66) 41: vdppd Vdq,Hdq,Wdq,Ib (66),(v1) -42: vmpsadbw Vx,Hx,Wx,Ib (66),(v1) +42: vmpsadbw Vx,Hx,Wx,Ib (66),(v1) | vdbpsadbw Vx,Hx,Wx,Ib (66),(evo) +43: vshufi32x4/64x2 Vx,Hx,Wx,Ib (66),(ev) 44: vpclmulqdq Vdq,Hdq,Wdq,Ib (66),(v1) 46: vperm2i128 Vqq,Hqq,Wqq,Ib (66),(v) 4a: vblendvps Vx,Hx,Wx,Lx (66),(v) 4b: vblendvpd Vx,Hx,Wx,Lx (66),(v) 4c: vpblendvb Vx,Hx,Wx,Lx (66),(v1) +50: vrangeps/d Vx,Hx,Wx,Ib (66),(ev) +51: vrangess/d Vx,Hx,Wx,Ib (66),(ev) +54: vfixupimmps/d Vx,Hx,Wx,Ib (66),(ev) +55: vfixupimmss/d Vx,Hx,Wx,Ib (66),(ev) +56: vreduceps/d Vx,Wx,Ib (66),(ev) +57: vreducess/d Vx,Hx,Wx,Ib (66),(ev) 60: vpcmpestrm Vdq,Wdq,Ib (66),(v1) 61: vpcmpestri Vdq,Wdq,Ib (66),(v1) 62: vpcmpistrm Vdq,Wdq,Ib (66),(v1) 63: vpcmpistri Vdq,Wdq,Ib (66),(v1) +66: vfpclassps/d Vk,Wx,Ib (66),(ev) +67: vfpclassss/d Vk,Wx,Ib (66),(ev) cc: sha1rnds4 Vdq,Wdq,Ib df: VAESKEYGEN Vdq,Wdq,Ib (66),(v1) f0: RORX Gy,Ey,Ib (F2),(v) @@ -927,8 +990,10 @@ GrpTable: Grp12 EndTable GrpTable: Grp13 +0: vprord/q Hx,Wx,Ib (66),(ev) +1: vprold/q Hx,Wx,Ib (66),(ev) 2: psrld Nq,Ib (11B) | vpsrld Hx,Ux,Ib (66),(11B),(v1) -4: psrad Nq,Ib (11B) | vpsrad Hx,Ux,Ib (66),(11B),(v1) +4: psrad Nq,Ib (11B) | vpsrad Hx,Ux,Ib (66),(11B),(v1) | vpsrad/q Hx,Ux,Ib (66),(evo) 6: pslld Nq,Ib (11B) | vpslld Hx,Ux,Ib (66),(11B),(v1) EndTable @@ -963,6 +1028,20 @@ GrpTable: Grp17 3: BLSI By,Ey (v) EndTable +GrpTable: Grp18 +1: vgatherpf0dps/d Wx (66),(ev) +2: vgatherpf1dps/d Wx (66),(ev) +5: vscatterpf0dps/d Wx (66),(ev) +6: vscatterpf1dps/d Wx (66),(ev) +EndTable + +GrpTable: Grp19 +1: vgatherpf0qps/d Wx (66),(ev) +2: vgatherpf1qps/d Wx (66),(ev) +5: vscatterpf0qps/d Wx (66),(ev) +6: vscatterpf1qps/d Wx (66),(ev) +EndTable + # AMD's Prefetch Group GrpTable: GrpP 0: PREFETCH diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c index 17fa7fc34fdf..bd09d0effef8 100644 --- a/tools/objtool/builtin-check.c +++ b/tools/objtool/builtin-check.c @@ -107,6 +107,12 @@ static struct instruction *next_insn_same_sec(struct objtool_file *file, insn->offset < func->offset + func->len; \ insn = list_next_entry(insn, list)) +#define func_for_each_insn_continue_reverse(file, func, insn) \ + for (insn = list_prev_entry(insn, list); \ + &insn->list != &file->insn_list && \ + insn->sec == func->sec && insn->offset >= func->offset; \ + insn = list_prev_entry(insn, list)) + #define sec_for_each_insn_from(file, insn) \ for (; insn; insn = next_insn_same_sec(file, insn)) @@ -664,65 +670,95 @@ static int add_switch_table(struct objtool_file *file, struct symbol *func, return 0; } -static int add_func_switch_tables(struct objtool_file *file, - struct symbol *func) +/* + * find_switch_table() - Given a dynamic jump, find the switch jump table in + * .rodata associated with it. + * + * There are 3 basic patterns: + * + * 1. jmpq *[rodata addr](,%reg,8) + * + * This is the most common case by far. It jumps to an address in a simple + * jump table which is stored in .rodata. + * + * 2. jmpq *[rodata addr](%rip) + * + * This is caused by a rare GCC quirk, currently only seen in three driver + * functions in the kernel, only with certain obscure non-distro configs. + * + * As part of an optimization, GCC makes a copy of an existing switch jump + * table, modifies it, and then hard-codes the jump (albeit with an indirect + * jump) to use a single entry in the table. The rest of the jump table and + * some of its jump targets remain as dead code. + * + * In such a case we can just crudely ignore all unreachable instruction + * warnings for the entire object file. Ideally we would just ignore them + * for the function, but that would require redesigning the code quite a + * bit. And honestly that's just not worth doing: unreachable instruction + * warnings are of questionable value anyway, and this is such a rare issue. + * + * 3. mov [rodata addr],%reg1 + * ... some instructions ... + * jmpq *(%reg1,%reg2,8) + * + * This is a fairly uncommon pattern which is new for GCC 6. As of this + * writing, there are 11 occurrences of it in the allmodconfig kernel. + * + * TODO: Once we have DWARF CFI and smarter instruction decoding logic, + * ensure the same register is used in the mov and jump instructions. + */ +static struct rela *find_switch_table(struct objtool_file *file, + struct symbol *func, + struct instruction *insn) { - struct instruction *insn, *prev_jump; - struct rela *text_rela, *rodata_rela, *prev_rela = NULL; - int ret; + struct rela *text_rela, *rodata_rela; - prev_jump = NULL; + text_rela = find_rela_by_dest_range(insn->sec, insn->offset, insn->len); + if (text_rela && text_rela->sym == file->rodata->sym) { + /* case 1 */ + rodata_rela = find_rela_by_dest(file->rodata, + text_rela->addend); + if (rodata_rela) + return rodata_rela; - func_for_each_insn(file, func, insn) { - if (insn->type != INSN_JUMP_DYNAMIC) - continue; + /* case 2 */ + rodata_rela = find_rela_by_dest(file->rodata, + text_rela->addend + 4); + if (!rodata_rela) + return NULL; + file->ignore_unreachables = true; + return rodata_rela; + } + + /* case 3 */ + func_for_each_insn_continue_reverse(file, func, insn) { + if (insn->type == INSN_JUMP_UNCONDITIONAL || + insn->type == INSN_JUMP_DYNAMIC) + break; text_rela = find_rela_by_dest_range(insn->sec, insn->offset, insn->len); - if (!text_rela || text_rela->sym != file->rodata->sym) - continue; + if (text_rela && text_rela->sym == file->rodata->sym) + return find_rela_by_dest(file->rodata, + text_rela->addend); + } - /* common case: jmpq *[addr](,%rax,8) */ - rodata_rela = find_rela_by_dest(file->rodata, - text_rela->addend); + return NULL; +} - /* - * rare case: jmpq *[addr](%rip) - * - * This check is for a rare gcc quirk, currently only seen in - * three driver functions in the kernel, only with certain - * obscure non-distro configs. - * - * As part of an optimization, gcc makes a copy of an existing - * switch jump table, modifies it, and then hard-codes the jump - * (albeit with an indirect jump) to use a single entry in the - * table. The rest of the jump table and some of its jump - * targets remain as dead code. - * - * In such a case we can just crudely ignore all unreachable - * instruction warnings for the entire object file. Ideally we - * would just ignore them for the function, but that would - * require redesigning the code quite a bit. And honestly - * that's just not worth doing: unreachable instruction - * warnings are of questionable value anyway, and this is such - * a rare issue. - * - * kbuild reports: - * - https://lkml.kernel.org/r/201603231906.LWcVUpxm%25fengguang.wu@intel.com - * - https://lkml.kernel.org/r/201603271114.K9i45biy%25fengguang.wu@intel.com - * - https://lkml.kernel.org/r/201603291058.zuJ6ben1%25fengguang.wu@intel.com - * - * gcc bug: - * - https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70604 - */ - if (!rodata_rela) { - rodata_rela = find_rela_by_dest(file->rodata, - text_rela->addend + 4); - if (rodata_rela) - file->ignore_unreachables = true; - } +static int add_func_switch_tables(struct objtool_file *file, + struct symbol *func) +{ + struct instruction *insn, *prev_jump = NULL; + struct rela *rela, *prev_rela = NULL; + int ret; - if (!rodata_rela) + func_for_each_insn(file, func, insn) { + if (insn->type != INSN_JUMP_DYNAMIC) + continue; + + rela = find_switch_table(file, func, insn); + if (!rela) continue; /* @@ -732,13 +768,13 @@ static int add_func_switch_tables(struct objtool_file *file, */ if (prev_jump) { ret = add_switch_table(file, func, prev_jump, prev_rela, - rodata_rela); + rela); if (ret) return ret; } prev_jump = insn; - prev_rela = rodata_rela; + prev_rela = rela; } if (prev_jump) { diff --git a/tools/perf/arch/s390/util/Build b/tools/perf/arch/s390/util/Build index 8a61372bb47a..5bd7b9260cc0 100644 --- a/tools/perf/arch/s390/util/Build +++ b/tools/perf/arch/s390/util/Build @@ -2,3 +2,5 @@ libperf-y += header.o libperf-y += kvm-stat.o libperf-$(CONFIG_DWARF) += dwarf-regs.o + +libperf-y += machine.o diff --git a/tools/perf/arch/s390/util/machine.c b/tools/perf/arch/s390/util/machine.c new file mode 100644 index 000000000000..b9a95a1a8e69 --- /dev/null +++ b/tools/perf/arch/s390/util/machine.c @@ -0,0 +1,19 @@ +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include "util.h" +#include "machine.h" +#include "api/fs/fs.h" + +int arch__fix_module_text_start(u64 *start, const char *name) +{ + char path[PATH_MAX]; + + snprintf(path, PATH_MAX, "module/%.*s/sections/.text", + (int)strlen(name) - 2, name + 1); + + if (sysfs__read_ull(path, (unsigned long long *)start) < 0) + return -1; + + return 0; +} diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 2fa7d8b69873..91c5f6e1af59 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -70,7 +70,6 @@ libperf-y += stat.o libperf-y += stat-shadow.o libperf-y += record.o libperf-y += srcline.o -libperf-y += str_error_r.o libperf-y += data.o libperf-y += tsc.o libperf-y += cloexec.o @@ -176,10 +175,6 @@ $(OUTPUT)util/libstring.o: ../lib/string.c FORCE $(call rule_mkdir) $(call if_changed_dep,cc_o_c) -$(OUTPUT)util/str_error_r.o: ../lib/str_error_r.c FORCE - $(call rule_mkdir) - $(call if_changed_dep,cc_o_c) - $(OUTPUT)util/hweight.o: ../lib/hweight.c FORCE $(call rule_mkdir) $(call if_changed_dep,cc_o_c) diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index bc2cdbd09a25..cb6388dbdd98 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -1093,12 +1093,20 @@ static int machine__set_modules_path(struct machine *machine) return map_groups__set_modules_path_dir(&machine->kmaps, modules_path, 0); } +int __weak arch__fix_module_text_start(u64 *start __maybe_unused, + const char *name __maybe_unused) +{ + return 0; +} static int machine__create_module(void *arg, const char *name, u64 start) { struct machine *machine = arg; struct map *map; + if (arch__fix_module_text_start(&start, name) < 0) + return -1; + map = machine__findnew_module_map(machine, start, name); if (map == NULL) return -1; diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index 41ac9cfd416b..20739f746bc4 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h @@ -216,6 +216,7 @@ struct symbol *machine__find_kernel_function_by_name(struct machine *machine, struct map *machine__findnew_module_map(struct machine *machine, u64 start, const char *filename); +int arch__fix_module_text_start(u64 *start, const char *name); int __machine__load_kallsyms(struct machine *machine, const char *filename, enum map_type type, bool no_kcore, symbol_filter_t filter); diff --git a/tools/perf/util/python-ext-sources b/tools/perf/util/python-ext-sources index 5065ec98049c..b7d4f4aeee61 100644 --- a/tools/perf/util/python-ext-sources +++ b/tools/perf/util/python-ext-sources @@ -13,7 +13,6 @@ util/cpumap.c ../lib/bitmap.c ../lib/find_bit.c ../lib/hweight.c -../lib/str_error_r.c ../lib/vsprintf.c util/thread_map.c util/util.c diff --git a/tools/testing/radix-tree/linux/gfp.h b/tools/testing/radix-tree/linux/gfp.h index 0e37f7a760eb..5201b915f631 100644 --- a/tools/testing/radix-tree/linux/gfp.h +++ b/tools/testing/radix-tree/linux/gfp.h @@ -1,7 +1,7 @@ #ifndef _GFP_H #define _GFP_H -#define __GFP_BITS_SHIFT 22 +#define __GFP_BITS_SHIFT 26 #define __GFP_BITS_MASK ((gfp_t)((1 << __GFP_BITS_SHIFT) - 1)) #define __GFP_WAIT 1 #define __GFP_ACCOUNT 0 diff --git a/tools/testing/selftests/exec/Makefile b/tools/testing/selftests/exec/Makefile index 4e400eb83657..d4300602bf37 100644 --- a/tools/testing/selftests/exec/Makefile +++ b/tools/testing/selftests/exec/Makefile @@ -18,7 +18,8 @@ execveat.denatured: execveat $(CC) $(CFLAGS) -o $@ $^ TEST_PROGS := execveat -TEST_FILES := $(DEPS) +# Makefile is a run-time dependency, since it's accessed by the execveat test +TEST_FILES := $(DEPS) Makefile include ../lib.mk diff --git a/tools/testing/selftests/lib/printf.sh b/tools/testing/selftests/lib/printf.sh index 4fdc70fe6980..4fdc70fe6980 100644..100755 --- a/tools/testing/selftests/lib/printf.sh +++ b/tools/testing/selftests/lib/printf.sh diff --git a/tools/testing/selftests/media_tests/.gitignore b/tools/testing/selftests/media_tests/.gitignore index 1c0711708b98..8745eba39012 100644 --- a/tools/testing/selftests/media_tests/.gitignore +++ b/tools/testing/selftests/media_tests/.gitignore @@ -1 +1,3 @@ media_device_test +media_device_open +video_device_test diff --git a/tools/testing/selftests/media_tests/Makefile b/tools/testing/selftests/media_tests/Makefile index 7071bcc1d066..6b34a0199468 100644 --- a/tools/testing/selftests/media_tests/Makefile +++ b/tools/testing/selftests/media_tests/Makefile @@ -1,7 +1,7 @@ -TEST_PROGS := media_device_test +TEST_PROGS := media_device_test media_device_open video_device_test all: $(TEST_PROGS) include ../lib.mk clean: - rm -fr media_device_test + rm -fr media_device_test media_device_open video_device_test diff --git a/tools/testing/selftests/media_tests/bind_unbind_sample.sh b/tools/testing/selftests/media_tests/bind_unbind_sample.sh new file mode 100755 index 000000000000..9f362f10631a --- /dev/null +++ b/tools/testing/selftests/media_tests/bind_unbind_sample.sh @@ -0,0 +1,12 @@ +#!/bin/bash +# Find device number in /sys/bus/usb/drivers/drivername +# Edit this file to update the driver numer and name +# Example test for uvcvideo driver +#i=0 +# while :; do +# i=$((i+1)) +# echo 1-5:1.0 > /sys/bus/usb/drivers/uvcvideo/unbind; +# echo 1-5:1.0 > /sys/bus/usb/drivers/uvcvideo/bind; +# clear +# echo $i +#done diff --git a/tools/testing/selftests/media_tests/media_device_open.c b/tools/testing/selftests/media_tests/media_device_open.c new file mode 100644 index 000000000000..44343c091a20 --- /dev/null +++ b/tools/testing/selftests/media_tests/media_device_open.c @@ -0,0 +1,81 @@ +/* + * media_device_open.c - Media Controller Device Open Test + * + * Copyright (c) 2016 Shuah Khan <shuahkh@osg.samsung.com> + * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * + * This file is released under the GPLv2. + */ + +/* + * This file adds a test for Media Controller API. + * This test should be run as root and should not be + * included in the Kselftest run. This test should be + * run when hardware and driver that makes use Media + * Controller API are present in the system. + * + * This test opens user specified Media Device and calls + * MEDIA_IOC_DEVICE_INFO ioctl, closes the file, and exits. + * + * Usage: + * sudo ./media_device_open -d /dev/mediaX + * + * Run this test is a loop and run bind/unbind on the driver. +*/ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <linux/media.h> + +int main(int argc, char **argv) +{ + int opt; + char media_device[256]; + int count = 0; + struct media_device_info mdi; + int ret; + int fd; + + if (argc < 2) { + printf("Usage: %s [-d </dev/mediaX>]\n", argv[0]); + exit(-1); + } + + /* Process arguments */ + while ((opt = getopt(argc, argv, "d:")) != -1) { + switch (opt) { + case 'd': + strncpy(media_device, optarg, sizeof(media_device) - 1); + media_device[sizeof(media_device)-1] = '\0'; + break; + default: + printf("Usage: %s [-d </dev/mediaX>]\n", argv[0]); + exit(-1); + } + } + + if (getuid() != 0) { + printf("Please run the test as root - Exiting.\n"); + exit(-1); + } + + /* Open Media device and keep it open */ + fd = open(media_device, O_RDWR); + if (fd == -1) { + printf("Media Device open errno %s\n", strerror(errno)); + exit(-1); + } + + ret = ioctl(fd, MEDIA_IOC_DEVICE_INFO, &mdi); + if (ret < 0) + printf("Media Device Info errno %s\n", strerror(errno)); + else + printf("Media device model %s driver %s\n", + mdi.model, mdi.driver); +} diff --git a/tools/testing/selftests/media_tests/media_device_test.c b/tools/testing/selftests/media_tests/media_device_test.c index cbf53a032ab5..5d49943e77d0 100644 --- a/tools/testing/selftests/media_tests/media_device_test.c +++ b/tools/testing/selftests/media_tests/media_device_test.c @@ -1,5 +1,5 @@ /* - * media_devkref_test.c - Media Controller Device Kref API Test + * media_device_test.c - Media Controller Device ioctl loop Test * * Copyright (c) 2016 Shuah Khan <shuahkh@osg.samsung.com> * Copyright (c) 2016 Samsung Electronics Co., Ltd. @@ -35,13 +35,14 @@ #include <fcntl.h> #include <sys/ioctl.h> #include <sys/stat.h> +#include <time.h> #include <linux/media.h> int main(int argc, char **argv) { int opt; char media_device[256]; - int count = 0; + int count; struct media_device_info mdi; int ret; int fd; @@ -69,6 +70,10 @@ int main(int argc, char **argv) exit(-1); } + /* Generate random number of interations */ + srand((unsigned int) time(NULL)); + count = rand(); + /* Open Media device and keep it open */ fd = open(media_device, O_RDWR); if (fd == -1) { @@ -82,14 +87,16 @@ int main(int argc, char **argv) "other Oops in the dmesg. Enable KaSan kernel\n" "config option for use-after-free error detection.\n\n"); - while (count < 100) { + printf("Running test for %d iternations\n", count); + + while (count > 0) { ret = ioctl(fd, MEDIA_IOC_DEVICE_INFO, &mdi); if (ret < 0) printf("Media Device Info errno %s\n", strerror(errno)); else - printf("Media device model %s driver %s\n", - mdi.model, mdi.driver); + printf("Media device model %s driver %s - count %d\n", + mdi.model, mdi.driver, count); sleep(10); - count++; + count--; } } diff --git a/tools/testing/selftests/media_tests/open_loop_test.sh b/tools/testing/selftests/media_tests/open_loop_test.sh new file mode 100755 index 000000000000..dcd3c17efc17 --- /dev/null +++ b/tools/testing/selftests/media_tests/open_loop_test.sh @@ -0,0 +1,10 @@ +#!/bin/bash + i=0 +file=/dev/media$1 + while :; do + echo $file + i=$((i+1)) + R=$(./media_device_open -d $file); + # clear + echo -e "Loop $i\n$R" + done diff --git a/tools/testing/selftests/media_tests/regression_test.txt b/tools/testing/selftests/media_tests/regression_test.txt new file mode 100644 index 000000000000..2627367681f7 --- /dev/null +++ b/tools/testing/selftests/media_tests/regression_test.txt @@ -0,0 +1,43 @@ +Testing for regressions in Media Controller API register, ioctl, syscall, +and unregister paths. There have a few problems that result in user-after +free on media_device, media_devnode, and cdev pointers when the driver is +unbound while ioctl is in progress. + +Test Procedure: + +Run bin/unbind loop while ioctls are in progress. +Run rmmod and modprobe. +Disconnect the device. + +Setup: + +Build media_device_test +cd tools/testing/selftests/media_tests +make + +Regressions test for cdev user-after free error on /dev/mediaX when driver +is unbound: + +Start media_device_test to regression test media devnode dynamic alloc +and cdev user-after-free fixes. This opens media dev files and sits in +a loop running media ioctl MEDIA_IOC_DEVICE_INFO command once every 10 +seconds. The idea is when device file goes away, media devnode and cdev +should stick around until this test exits. + +The test for a random number of iterations or until user kills it with a +sleep 10 in between the ioctl calls. + +sudo ./media_device_test -d /dev/mediaX + +Regression test for media_devnode unregister race with ioctl_syscall: + +Start 6 open_loop_test.sh tests with different /dev/mediaX files. When +device file goes away after unbind, device file name changes. Start the +test with possible device names. If we start with /dev/media0 for example, +after unbind, /dev/media1 or /dev/media2 could get created. The idea is +keep ioctls going while bind/unbind runs. + +Copy bind_unbind_sample.txt and make changes to specify the driver name +and number to run bind and unbind. Start the bind_unbind.sh + +Run dmesg looking for any user-after free errors or mutex lock errors. diff --git a/tools/testing/selftests/media_tests/video_device_test.c b/tools/testing/selftests/media_tests/video_device_test.c new file mode 100644 index 000000000000..66d419c28653 --- /dev/null +++ b/tools/testing/selftests/media_tests/video_device_test.c @@ -0,0 +1,100 @@ +/* + * video_device_test - Video Device Test + * + * Copyright (c) 2016 Shuah Khan <shuahkh@osg.samsung.com> + * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * + * This file is released under the GPLv2. + */ + +/* + * This file adds a test for Video Device. This test should not be included + * in the Kselftest run. This test should be run when hardware and driver + * that makes use of V4L2 API is present. + * + * This test opens user specified Video Device and calls video ioctls in a + * loop once every 10 seconds. + * + * Usage: + * sudo ./video_device_test -d /dev/videoX + * + * While test is running, remove the device or unbind the driver and + * ensure there are no use after free errors and other Oops in the + * dmesg. + * When possible, enable KaSan kernel config option for use-after-free + * error detection. +*/ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <time.h> +#include <linux/videodev2.h> + +int main(int argc, char **argv) +{ + int opt; + char video_dev[256]; + int count; + struct v4l2_tuner vtuner; + struct v4l2_capability vcap; + int ret; + int fd; + + if (argc < 2) { + printf("Usage: %s [-d </dev/videoX>]\n", argv[0]); + exit(-1); + } + + /* Process arguments */ + while ((opt = getopt(argc, argv, "d:")) != -1) { + switch (opt) { + case 'd': + strncpy(video_dev, optarg, sizeof(video_dev) - 1); + video_dev[sizeof(video_dev)-1] = '\0'; + break; + default: + printf("Usage: %s [-d </dev/videoX>]\n", argv[0]); + exit(-1); + } + } + + /* Generate random number of interations */ + srand((unsigned int) time(NULL)); + count = rand(); + + /* Open Video device and keep it open */ + fd = open(video_dev, O_RDWR); + if (fd == -1) { + printf("Video Device open errno %s\n", strerror(errno)); + exit(-1); + } + + printf("\nNote:\n" + "While test is running, remove the device or unbind\n" + "driver and ensure there are no use after free errors\n" + "and other Oops in the dmesg. When possible, enable KaSan\n" + "kernel config option for use-after-free error detection.\n\n"); + + while (count > 0) { + ret = ioctl(fd, VIDIOC_QUERYCAP, &vcap); + if (ret < 0) + printf("VIDIOC_QUERYCAP errno %s\n", strerror(errno)); + else + printf("Video device driver %s\n", vcap.driver); + + ret = ioctl(fd, VIDIOC_G_TUNER, &vtuner); + if (ret < 0) + printf("VIDIOC_G_TUNER, errno %s\n", strerror(errno)); + else + printf("type %d rangelow %d rangehigh %d\n", + vtuner.type, vtuner.rangelow, vtuner.rangehigh); + sleep(10); + count--; + } +} diff --git a/tools/testing/selftests/powerpc/Makefile b/tools/testing/selftests/powerpc/Makefile index 4ca83fe80654..3c40c9d0e6c7 100644 --- a/tools/testing/selftests/powerpc/Makefile +++ b/tools/testing/selftests/powerpc/Makefile @@ -12,7 +12,8 @@ CFLAGS := -Wall -O2 -Wall -Werror -DGIT_VERSION='"$(GIT_VERSION)"' -I$(CURDIR) $ export CFLAGS -SUB_DIRS = benchmarks \ +SUB_DIRS = alignment \ + benchmarks \ copyloops \ context_switch \ dscr \ diff --git a/tools/testing/selftests/powerpc/alignment/.gitignore b/tools/testing/selftests/powerpc/alignment/.gitignore new file mode 100644 index 000000000000..1d980e3d7039 --- /dev/null +++ b/tools/testing/selftests/powerpc/alignment/.gitignore @@ -0,0 +1,5 @@ +copy_unaligned +copy_first_unaligned +paste_unaligned +paste_last_unaligned +copy_paste_unaligned_common diff --git a/tools/testing/selftests/powerpc/alignment/Makefile b/tools/testing/selftests/powerpc/alignment/Makefile new file mode 100644 index 000000000000..ad6a4e49da91 --- /dev/null +++ b/tools/testing/selftests/powerpc/alignment/Makefile @@ -0,0 +1,10 @@ +TEST_PROGS := copy_unaligned copy_first_unaligned paste_unaligned paste_last_unaligned + +all: $(TEST_PROGS) + +$(TEST_PROGS): ../harness.c ../utils.c copy_paste_unaligned_common.c + +include ../../lib.mk + +clean: + rm -f $(TEST_PROGS) diff --git a/tools/testing/selftests/powerpc/alignment/copy_first_unaligned.c b/tools/testing/selftests/powerpc/alignment/copy_first_unaligned.c new file mode 100644 index 000000000000..47b73b3a08bd --- /dev/null +++ b/tools/testing/selftests/powerpc/alignment/copy_first_unaligned.c @@ -0,0 +1,41 @@ +/* + * Copyright 2016, Chris Smart, IBM Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Calls to copy_first which are not 128-byte aligned should be + * caught and sent a SIGBUS. + * + */ + +#include <string.h> +#include <unistd.h> +#include "utils.h" +#include "instructions.h" +#include "copy_paste_unaligned_common.h" + +unsigned int expected_instruction = PPC_INST_COPY_FIRST; +unsigned int instruction_mask = 0xfc2007fe; + +int test_copy_first_unaligned(void) +{ + /* Only run this test on a P9 or later */ + SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_3_00)); + + /* Register our signal handler with SIGBUS */ + setup_signal_handler(); + + /* +1 makes buf unaligned */ + copy_first(cacheline_buf+1); + + /* We should not get here */ + return 1; +} + +int main(int argc, char *argv[]) +{ + return test_harness(test_copy_first_unaligned, "test_copy_first_unaligned"); +} diff --git a/tools/testing/selftests/powerpc/alignment/copy_paste_unaligned_common.c b/tools/testing/selftests/powerpc/alignment/copy_paste_unaligned_common.c new file mode 100644 index 000000000000..d35fa5f5d2d3 --- /dev/null +++ b/tools/testing/selftests/powerpc/alignment/copy_paste_unaligned_common.c @@ -0,0 +1,53 @@ +/* + * Copyright 2016, Chris Smart, IBM Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Common code for copy, copy_first, paste and paste_last unaligned + * tests. + * + */ + +#include <signal.h> +#include <string.h> +#include <unistd.h> +#include "utils.h" +#include "instructions.h" +#include "copy_paste_unaligned_common.h" + +unsigned int expected_instruction; +unsigned int instruction_mask; + +char cacheline_buf[128] __cacheline_aligned; + +void signal_action_handler(int signal_num, siginfo_t *info, void *ptr) +{ + ucontext_t *ctx = ptr; +#if defined(__powerpc64__) + unsigned int *pc = (unsigned int *)ctx->uc_mcontext.gp_regs[PT_NIP]; +#else + unsigned int *pc = (unsigned int *)ctx->uc_mcontext.uc_regs->gregs[PT_NIP]; +#endif + + /* + * Check that the signal was on the correct instruction, using a + * mask because the compiler assigns the register at RB. + */ + if ((*pc & instruction_mask) == expected_instruction) + _exit(0); /* We hit the right instruction */ + + _exit(1); +} + +void setup_signal_handler(void) +{ + struct sigaction signal_action; + + memset(&signal_action, 0, sizeof(signal_action)); + signal_action.sa_sigaction = signal_action_handler; + signal_action.sa_flags = SA_SIGINFO; + sigaction(SIGBUS, &signal_action, NULL); +} diff --git a/tools/testing/selftests/powerpc/alignment/copy_paste_unaligned_common.h b/tools/testing/selftests/powerpc/alignment/copy_paste_unaligned_common.h new file mode 100644 index 000000000000..053899fe506e --- /dev/null +++ b/tools/testing/selftests/powerpc/alignment/copy_paste_unaligned_common.h @@ -0,0 +1,26 @@ +/* + * Copyright 2016, Chris Smart, IBM Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Declarations for common code for copy, copy_first, paste and + * paste_last unaligned tests. + * + */ + +#ifndef _SELFTESTS_POWERPC_COPY_PASTE_H +#define _SELFTESTS_POWERPC_COPY_PASTE_H + +#include <signal.h> + +int main(int argc, char *argv[]); +void signal_action_handler(int signal_num, siginfo_t *info, void *ptr); +void setup_signal_handler(void); +extern char cacheline_buf[128] __cacheline_aligned; +extern unsigned int expected_instruction; +extern unsigned int instruction_mask; + +#endif /* _SELFTESTS_POWERPC_COPY_PASTE_H */ diff --git a/tools/testing/selftests/powerpc/alignment/copy_unaligned.c b/tools/testing/selftests/powerpc/alignment/copy_unaligned.c new file mode 100644 index 000000000000..3a4e26461554 --- /dev/null +++ b/tools/testing/selftests/powerpc/alignment/copy_unaligned.c @@ -0,0 +1,41 @@ +/* + * Copyright 2016, Chris Smart, IBM Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Calls to copy which are not 128-byte aligned should be caught + * and sent a SIGBUS. + * + */ + +#include <string.h> +#include <unistd.h> +#include "utils.h" +#include "instructions.h" +#include "copy_paste_unaligned_common.h" + +unsigned int expected_instruction = PPC_INST_COPY; +unsigned int instruction_mask = 0xfc0007fe; + +int test_copy_unaligned(void) +{ + /* Only run this test on a P9 or later */ + SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_3_00)); + + /* Register our signal handler with SIGBUS */ + setup_signal_handler(); + + /* +1 makes buf unaligned */ + copy(cacheline_buf+1); + + /* We should not get here */ + return 1; +} + +int main(int argc, char *argv[]) +{ + return test_harness(test_copy_unaligned, "test_copy_unaligned"); +} diff --git a/tools/testing/selftests/powerpc/alignment/paste_last_unaligned.c b/tools/testing/selftests/powerpc/alignment/paste_last_unaligned.c new file mode 100644 index 000000000000..6e0ad045fcc3 --- /dev/null +++ b/tools/testing/selftests/powerpc/alignment/paste_last_unaligned.c @@ -0,0 +1,43 @@ +/* + * Copyright 2016, Chris Smart, IBM Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Calls to paste_last which are not 128-byte aligned should be + * caught and sent a SIGBUS. + * + */ + +#include <string.h> +#include <unistd.h> +#include "utils.h" +#include "instructions.h" +#include "copy_paste_unaligned_common.h" + +unsigned int expected_instruction = PPC_INST_PASTE_LAST; +unsigned int instruction_mask = 0xfc2007ff; + +int test_paste_last_unaligned(void) +{ + /* Only run this test on a P9 or later */ + SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_3_00)); + + /* Register our signal handler with SIGBUS */ + setup_signal_handler(); + + copy(cacheline_buf); + + /* +1 makes buf unaligned */ + paste_last(cacheline_buf+1); + + /* We should not get here */ + return 1; +} + +int main(int argc, char *argv[]) +{ + return test_harness(test_paste_last_unaligned, "test_paste_last_unaligned"); +} diff --git a/tools/testing/selftests/powerpc/alignment/paste_unaligned.c b/tools/testing/selftests/powerpc/alignment/paste_unaligned.c new file mode 100644 index 000000000000..6f982b45e4bd --- /dev/null +++ b/tools/testing/selftests/powerpc/alignment/paste_unaligned.c @@ -0,0 +1,43 @@ +/* + * Copyright 2016, Chris Smart, IBM Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Calls to paste which are not 128-byte aligned should be caught + * and sent a SIGBUS. + * + */ + +#include <string.h> +#include <unistd.h> +#include "utils.h" +#include "instructions.h" +#include "copy_paste_unaligned_common.h" + +unsigned int expected_instruction = PPC_INST_PASTE; +unsigned int instruction_mask = 0xfc0007fe; + +int test_paste_unaligned(void) +{ + /* Only run this test on a P9 or later */ + SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_3_00)); + + /* Register our signal handler with SIGBUS */ + setup_signal_handler(); + + copy(cacheline_buf); + + /* +1 makes buf unaligned */ + paste(cacheline_buf+1); + + /* We should not get here */ + return 1; +} + +int main(int argc, char *argv[]) +{ + return test_harness(test_paste_unaligned, "test_paste_unaligned"); +} diff --git a/tools/testing/selftests/powerpc/benchmarks/.gitignore b/tools/testing/selftests/powerpc/benchmarks/.gitignore index 6fa673316ac2..bce49ebd869e 100644 --- a/tools/testing/selftests/powerpc/benchmarks/.gitignore +++ b/tools/testing/selftests/powerpc/benchmarks/.gitignore @@ -1,2 +1,4 @@ gettimeofday context_switch +mmap_bench +futex_bench
\ No newline at end of file diff --git a/tools/testing/selftests/powerpc/benchmarks/Makefile b/tools/testing/selftests/powerpc/benchmarks/Makefile index 912445ff7ce7..a9adfb7de78f 100644 --- a/tools/testing/selftests/powerpc/benchmarks/Makefile +++ b/tools/testing/selftests/powerpc/benchmarks/Makefile @@ -1,4 +1,4 @@ -TEST_PROGS := gettimeofday context_switch +TEST_PROGS := gettimeofday context_switch mmap_bench futex_bench CFLAGS += -O2 @@ -7,6 +7,7 @@ all: $(TEST_PROGS) $(TEST_PROGS): ../harness.c context_switch: ../utils.c +context_switch: CFLAGS += -maltivec -mvsx -mabi=altivec context_switch: LDLIBS += -lpthread include ../../lib.mk diff --git a/tools/testing/selftests/powerpc/benchmarks/context_switch.c b/tools/testing/selftests/powerpc/benchmarks/context_switch.c index 7b785941adec..a36883ad48a4 100644 --- a/tools/testing/selftests/powerpc/benchmarks/context_switch.c +++ b/tools/testing/selftests/powerpc/benchmarks/context_switch.c @@ -25,7 +25,9 @@ #include <sys/types.h> #include <sys/shm.h> #include <linux/futex.h> - +#ifdef __powerpc__ +#include <altivec.h> +#endif #include "../utils.h" static unsigned int timeout = 30; @@ -37,12 +39,15 @@ static int touch_fp = 1; double fp; static int touch_vector = 1; -typedef int v4si __attribute__ ((vector_size (16))); -v4si a, b, c; +vector int a, b, c; #ifdef __powerpc__ static int touch_altivec = 1; +/* + * Note: LTO (Link Time Optimisation) doesn't play well with this function + * attribute. Be very careful enabling LTO for this test. + */ static void __attribute__((__target__("no-vsx"))) altivec_touch_fn(void) { c = a + b; @@ -369,11 +374,11 @@ static void usage(void) fprintf(stderr, "\t\t--process\tUse processes (default threads)\n"); fprintf(stderr, "\t\t--timeout=X\tDuration in seconds to run (default 30)\n"); fprintf(stderr, "\t\t--vdso\t\ttouch VDSO\n"); - fprintf(stderr, "\t\t--fp\t\ttouch FP\n"); + fprintf(stderr, "\t\t--no-fp\t\tDon't touch FP\n"); #ifdef __powerpc__ - fprintf(stderr, "\t\t--altivec\ttouch altivec\n"); + fprintf(stderr, "\t\t--no-altivec\tDon't touch altivec\n"); #endif - fprintf(stderr, "\t\t--vector\ttouch vector\n"); + fprintf(stderr, "\t\t--no-vector\tDon't touch vector\n"); } int main(int argc, char *argv[]) diff --git a/tools/testing/selftests/powerpc/benchmarks/futex_bench.c b/tools/testing/selftests/powerpc/benchmarks/futex_bench.c new file mode 100644 index 000000000000..2fc711d9150d --- /dev/null +++ b/tools/testing/selftests/powerpc/benchmarks/futex_bench.c @@ -0,0 +1,42 @@ +/* + * Copyright 2016, Anton Blanchard, Michael Ellerman, IBM Corp. + * Licensed under GPLv2. + */ + +#define _GNU_SOURCE + +#include <stdio.h> +#include <sys/syscall.h> +#include <time.h> +#include <unistd.h> +#include <linux/futex.h> + +#include "utils.h" + +#define ITERATIONS 100000000 + +#define futex(A, B, C, D, E, F) syscall(__NR_futex, A, B, C, D, E, F) + +int test_futex(void) +{ + struct timespec ts_start, ts_end; + unsigned long i = ITERATIONS; + + clock_gettime(CLOCK_MONOTONIC, &ts_start); + + while (i--) { + unsigned int addr = 0; + futex(&addr, FUTEX_WAKE, 1, NULL, NULL, 0); + } + + clock_gettime(CLOCK_MONOTONIC, &ts_end); + + printf("time = %.6f\n", ts_end.tv_sec - ts_start.tv_sec + (ts_end.tv_nsec - ts_start.tv_nsec) / 1e9); + + return 0; +} + +int main(void) +{ + return test_harness(test_futex, "futex_bench"); +} diff --git a/tools/testing/selftests/powerpc/benchmarks/mmap_bench.c b/tools/testing/selftests/powerpc/benchmarks/mmap_bench.c new file mode 100644 index 000000000000..8d084a2d6e74 --- /dev/null +++ b/tools/testing/selftests/powerpc/benchmarks/mmap_bench.c @@ -0,0 +1,41 @@ +/* + * Copyright 2016, Anton Blanchard, Michael Ellerman, IBM Corp. + * Licensed under GPLv2. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <sys/mman.h> +#include <time.h> + +#include "utils.h" + +#define ITERATIONS 5000000 + +#define MEMSIZE (128 * 1024 * 1024) + +int test_mmap(void) +{ + struct timespec ts_start, ts_end; + unsigned long i = ITERATIONS; + + clock_gettime(CLOCK_MONOTONIC, &ts_start); + + while (i--) { + char *c = mmap(NULL, MEMSIZE, PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + FAIL_IF(c == MAP_FAILED); + munmap(c, MEMSIZE); + } + + clock_gettime(CLOCK_MONOTONIC, &ts_end); + + printf("time = %.6f\n", ts_end.tv_sec - ts_start.tv_sec + (ts_end.tv_nsec - ts_start.tv_nsec) / 1e9); + + return 0; +} + +int main(void) +{ + return test_harness(test_mmap, "mmap_bench"); +} diff --git a/tools/testing/selftests/powerpc/instructions.h b/tools/testing/selftests/powerpc/instructions.h new file mode 100644 index 000000000000..0fb0bd3b28c9 --- /dev/null +++ b/tools/testing/selftests/powerpc/instructions.h @@ -0,0 +1,68 @@ +#ifndef _SELFTESTS_POWERPC_INSTRUCTIONS_H +#define _SELFTESTS_POWERPC_INSTRUCTIONS_H + +#include <stdio.h> +#include <stdlib.h> + +/* This defines the "copy" instruction from Power ISA 3.0 Book II, section 4.4. */ +#define __COPY(RA, RB, L) \ + (0x7c00060c | (RA) << (31-15) | (RB) << (31-20) | (L) << (31-10)) +#define COPY(RA, RB, L) \ + .long __COPY((RA), (RB), (L)) + +static inline void copy(void *i) +{ + asm volatile(str(COPY(0, %0, 0))";" + : + : "b" (i) + : "memory" + ); +} + +static inline void copy_first(void *i) +{ + asm volatile(str(COPY(0, %0, 1))";" + : + : "b" (i) + : "memory" + ); +} + +/* This defines the "paste" instruction from Power ISA 3.0 Book II, section 4.4. */ +#define __PASTE(RA, RB, L, RC) \ + (0x7c00070c | (RA) << (31-15) | (RB) << (31-20) | (L) << (31-10) | (RC) << (31-31)) +#define PASTE(RA, RB, L, RC) \ + .long __PASTE((RA), (RB), (L), (RC)) + +static inline int paste(void *i) +{ + int cr; + + asm volatile(str(PASTE(0, %1, 0, 0))";" + "mfcr %0;" + : "=r" (cr) + : "b" (i) + : "memory" + ); + return cr; +} + +static inline int paste_last(void *i) +{ + int cr; + + asm volatile(str(PASTE(0, %1, 1, 1))";" + "mfcr %0;" + : "=r" (cr) + : "b" (i) + : "memory" + ); + return cr; +} + +#define PPC_INST_COPY __COPY(0, 0, 0) +#define PPC_INST_COPY_FIRST __COPY(0, 0, 1) +#define PPC_INST_PASTE __PASTE(0, 0, 0, 0) +#define PPC_INST_PASTE_LAST __PASTE(0, 0, 1, 1) + +#endif /* _SELFTESTS_POWERPC_INSTRUCTIONS_H */ diff --git a/tools/testing/selftests/powerpc/mm/.gitignore b/tools/testing/selftests/powerpc/mm/.gitignore index b43ade0ec861..e715a3f2fbf4 100644 --- a/tools/testing/selftests/powerpc/mm/.gitignore +++ b/tools/testing/selftests/powerpc/mm/.gitignore @@ -1,3 +1,4 @@ hugetlb_vs_thp_test subpage_prot tempfile +prot_sao
\ No newline at end of file diff --git a/tools/testing/selftests/powerpc/mm/Makefile b/tools/testing/selftests/powerpc/mm/Makefile index ee179e22308c..3bdb96eae558 100644 --- a/tools/testing/selftests/powerpc/mm/Makefile +++ b/tools/testing/selftests/powerpc/mm/Makefile @@ -1,13 +1,15 @@ noarg: $(MAKE) -C ../ -TEST_PROGS := hugetlb_vs_thp_test subpage_prot +TEST_PROGS := hugetlb_vs_thp_test subpage_prot prot_sao TEST_FILES := tempfile all: $(TEST_PROGS) $(TEST_FILES) $(TEST_PROGS): ../harness.c +prot_sao: ../utils.c + include ../../lib.mk tempfile: diff --git a/tools/testing/selftests/powerpc/mm/prot_sao.c b/tools/testing/selftests/powerpc/mm/prot_sao.c new file mode 100644 index 000000000000..611530d43fa9 --- /dev/null +++ b/tools/testing/selftests/powerpc/mm/prot_sao.c @@ -0,0 +1,42 @@ +/* + * Copyright 2016, Michael Ellerman, IBM Corp. + * Licensed under GPLv2. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> + +#include <asm/cputable.h> + +#include "utils.h" + +#define SIZE (64 * 1024) + +int test_prot_sao(void) +{ + char *p; + + /* 2.06 or later should support SAO */ + SKIP_IF(!have_hwcap(PPC_FEATURE_ARCH_2_06)); + + /* + * Ensure we can ask for PROT_SAO. + * We can't really verify that it does the right thing, but at least we + * confirm the kernel will accept it. + */ + p = mmap(NULL, SIZE, PROT_READ | PROT_WRITE | PROT_SAO, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + FAIL_IF(p == MAP_FAILED); + + /* Write to the mapping, to at least cause a fault */ + memset(p, 0xaa, SIZE); + + return 0; +} + +int main(void) +{ + return test_harness(test_prot_sao, "prot-sao"); +} diff --git a/tools/testing/selftests/powerpc/pmu/ebb/.gitignore b/tools/testing/selftests/powerpc/pmu/ebb/.gitignore index 42bddbed8b64..44b7df14a936 100644 --- a/tools/testing/selftests/powerpc/pmu/ebb/.gitignore +++ b/tools/testing/selftests/powerpc/pmu/ebb/.gitignore @@ -20,3 +20,5 @@ back_to_back_ebbs_test lost_exception_test no_handler_test cycles_with_mmcr2_test +ebb_lmr +ebb_lmr_regs
\ No newline at end of file diff --git a/tools/testing/selftests/powerpc/pmu/ebb/Makefile b/tools/testing/selftests/powerpc/pmu/ebb/Makefile index 8d2279c4bb4b..6b0453e60d53 100644 --- a/tools/testing/selftests/powerpc/pmu/ebb/Makefile +++ b/tools/testing/selftests/powerpc/pmu/ebb/Makefile @@ -14,7 +14,7 @@ TEST_PROGS := reg_access_test event_attributes_test cycles_test \ fork_cleanup_test ebb_on_child_test \ ebb_on_willing_child_test back_to_back_ebbs_test \ lost_exception_test no_handler_test \ - cycles_with_mmcr2_test + cycles_with_mmcr2_test ebb_lmr ebb_lmr_regs all: $(TEST_PROGS) diff --git a/tools/testing/selftests/powerpc/pmu/ebb/ebb_lmr.c b/tools/testing/selftests/powerpc/pmu/ebb/ebb_lmr.c new file mode 100644 index 000000000000..c47ebd55ba4d --- /dev/null +++ b/tools/testing/selftests/powerpc/pmu/ebb/ebb_lmr.c @@ -0,0 +1,143 @@ +/* + * Copyright 2016, Jack Miller, IBM Corp. + * Licensed under GPLv2. + */ + +#include <stdlib.h> +#include <stdio.h> + +#include "ebb.h" +#include "ebb_lmr.h" + +#define SIZE (32 * 1024 * 1024) /* 32M */ +#define LM_SIZE 0 /* Smallest encoding, 32M */ + +#define SECTIONS 64 /* 1 per bit in LMSER */ +#define SECTION_SIZE (SIZE / SECTIONS) +#define SECTION_LONGS (SECTION_SIZE / sizeof(long)) + +static unsigned long *test_mem; + +static int lmr_count = 0; + +void ebb_lmr_handler(void) +{ + lmr_count++; +} + +void ldmx_full_section(unsigned long *mem, int section) +{ + unsigned long *ptr; + int i; + + for (i = 0; i < SECTION_LONGS; i++) { + ptr = &mem[(SECTION_LONGS * section) + i]; + ldmx((unsigned long) &ptr); + ebb_lmr_reset(); + } +} + +unsigned long section_masks[] = { + 0x8000000000000000, + 0xFF00000000000000, + 0x0000000F70000000, + 0x8000000000000001, + 0xF0F0F0F0F0F0F0F0, + 0x0F0F0F0F0F0F0F0F, + 0x0 +}; + +int ebb_lmr_section_test(unsigned long *mem) +{ + unsigned long *mask = section_masks; + int i; + + for (; *mask; mask++) { + mtspr(SPRN_LMSER, *mask); + printf("Testing mask 0x%016lx\n", mfspr(SPRN_LMSER)); + + for (i = 0; i < 64; i++) { + lmr_count = 0; + ldmx_full_section(mem, i); + if (*mask & (1UL << (63 - i))) + FAIL_IF(lmr_count != SECTION_LONGS); + else + FAIL_IF(lmr_count); + } + } + + return 0; +} + +int ebb_lmr(void) +{ + int i; + + SKIP_IF(!lmr_is_supported()); + + setup_ebb_handler(ebb_lmr_handler); + + ebb_global_enable(); + + FAIL_IF(posix_memalign((void **)&test_mem, SIZE, SIZE) != 0); + + mtspr(SPRN_LMSER, 0); + + FAIL_IF(mfspr(SPRN_LMSER) != 0); + + mtspr(SPRN_LMRR, ((unsigned long)test_mem | LM_SIZE)); + + FAIL_IF(mfspr(SPRN_LMRR) != ((unsigned long)test_mem | LM_SIZE)); + + /* Read every single byte to ensure we get no false positives */ + for (i = 0; i < SECTIONS; i++) + ldmx_full_section(test_mem, i); + + FAIL_IF(lmr_count != 0); + + /* Turn on the first section */ + + mtspr(SPRN_LMSER, (1UL << 63)); + FAIL_IF(mfspr(SPRN_LMSER) != (1UL << 63)); + + /* Enable LM (BESCR) */ + + mtspr(SPRN_BESCR, mfspr(SPRN_BESCR) | BESCR_LME); + FAIL_IF(!(mfspr(SPRN_BESCR) & BESCR_LME)); + + ldmx((unsigned long)&test_mem); + + FAIL_IF(lmr_count != 1); // exactly one exception + FAIL_IF(mfspr(SPRN_BESCR) & BESCR_LME); // LM now disabled + FAIL_IF(!(mfspr(SPRN_BESCR) & BESCR_LMEO)); // occurred bit set + + printf("Simple LMR EBB OK\n"); + + /* This shouldn't cause an EBB since it's been disabled */ + ldmx((unsigned long)&test_mem); + FAIL_IF(lmr_count != 1); + + printf("LMR disable on EBB OK\n"); + + ebb_lmr_reset(); + + /* This should cause an EBB or reset is broken */ + ldmx((unsigned long)&test_mem); + FAIL_IF(lmr_count != 2); + + printf("LMR reset EBB OK\n"); + + ebb_lmr_reset(); + + return ebb_lmr_section_test(test_mem); +} + +int main(void) +{ + int ret = test_harness(ebb_lmr, "ebb_lmr"); + + if (test_mem) + free(test_mem); + + return ret; +} diff --git a/tools/testing/selftests/powerpc/pmu/ebb/ebb_lmr.h b/tools/testing/selftests/powerpc/pmu/ebb/ebb_lmr.h new file mode 100644 index 000000000000..ef50abd557cd --- /dev/null +++ b/tools/testing/selftests/powerpc/pmu/ebb/ebb_lmr.h @@ -0,0 +1,39 @@ +#ifndef _SELFTESTS_POWERPC_PMU_EBB_LMR_H +#define _SELFTESTS_POWERPC_PMU_EBB_LMR_H + +#include "reg.h" + +#ifndef PPC_FEATURE2_ARCH_3_00 +#define PPC_FEATURE2_ARCH_3_00 0x00800000 +#endif + +#define lmr_is_supported() have_hwcap2(PPC_FEATURE2_ARCH_3_00) + +static inline void ebb_lmr_reset(void) +{ + unsigned long bescr = mfspr(SPRN_BESCR); + bescr &= ~(BESCR_LMEO); + bescr |= BESCR_LME; + mtspr(SPRN_BESCR, bescr); +} + +#define LDMX(t, a, b)\ + (0x7c00026a | \ + (((t) & 0x1f) << 21) | \ + (((a) & 0x1f) << 16) | \ + (((b) & 0x1f) << 11)) + +static inline unsigned long ldmx(unsigned long address) +{ + unsigned long ret; + + asm volatile ("mr 9, %1\r\n" + ".long " __stringify(LDMX(9, 0, 9)) "\r\n" + "mr %0, 9\r\n":"=r"(ret) + :"r"(address) + :"r9"); + + return ret; +} + +#endif diff --git a/tools/testing/selftests/powerpc/pmu/ebb/ebb_lmr_regs.c b/tools/testing/selftests/powerpc/pmu/ebb/ebb_lmr_regs.c new file mode 100644 index 000000000000..aff4241fd88a --- /dev/null +++ b/tools/testing/selftests/powerpc/pmu/ebb/ebb_lmr_regs.c @@ -0,0 +1,37 @@ +/* + * Copyright 2016, Jack Miller, IBM Corp. + * Licensed under GPLv2. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> + +#include "ebb.h" +#include "ebb_lmr.h" + +#define CHECKS 10000 + +int ebb_lmr_regs(void) +{ + int i; + + SKIP_IF(!lmr_is_supported()); + + ebb_global_enable(); + + for (i = 0; i < CHECKS; i++) { + mtspr(SPRN_LMRR, i << 25); // skip size and rsvd bits + mtspr(SPRN_LMSER, i); + + FAIL_IF(mfspr(SPRN_LMRR) != (i << 25)); + FAIL_IF(mfspr(SPRN_LMSER) != i); + } + + return 0; +} + +int main(void) +{ + return test_harness(ebb_lmr_regs, "ebb_lmr_regs"); +} diff --git a/tools/testing/selftests/powerpc/pmu/ebb/instruction_count_test.c b/tools/testing/selftests/powerpc/pmu/ebb/instruction_count_test.c index 5da355135df2..ae9a79086111 100644 --- a/tools/testing/selftests/powerpc/pmu/ebb/instruction_count_test.c +++ b/tools/testing/selftests/powerpc/pmu/ebb/instruction_count_test.c @@ -51,7 +51,7 @@ static int do_count_loop(struct event *event, uint64_t instructions, printf("Looped for %lu instructions, overhead %lu\n", instructions, overhead); printf("Expected %lu\n", expected); printf("Actual %llu\n", event->result.value); - printf("Error %ld, %f%%\n", difference, percentage); + printf("Delta %ld, %f%%\n", difference, percentage); printf("Took %d EBBs\n", ebb_state.stats.ebb_count); } diff --git a/tools/testing/selftests/powerpc/pmu/lib.c b/tools/testing/selftests/powerpc/pmu/lib.c index a361ad3334ce..8b992fa5b478 100644 --- a/tools/testing/selftests/powerpc/pmu/lib.c +++ b/tools/testing/selftests/powerpc/pmu/lib.c @@ -190,7 +190,7 @@ int parse_proc_maps(void) bool require_paranoia_below(int level) { - unsigned long current; + long current; char *end, buf[16]; FILE *f; int rc; @@ -208,7 +208,7 @@ bool require_paranoia_below(int level) goto out_close; } - current = strtoul(buf, &end, 10); + current = strtol(buf, &end, 10); if (end == buf) { printf("Couldn't parse " PARANOID_PATH "?\n"); @@ -216,7 +216,7 @@ bool require_paranoia_below(int level) } if (current >= level) - goto out; + goto out_close; rc = 0; out_close: diff --git a/tools/testing/selftests/powerpc/reg.h b/tools/testing/selftests/powerpc/reg.h index 65bfdeeebdee..fddf368ed82f 100644 --- a/tools/testing/selftests/powerpc/reg.h +++ b/tools/testing/selftests/powerpc/reg.h @@ -34,6 +34,11 @@ #define BESCR_PMEO 0x1 /* PMU Event-based exception Occurred */ #define BESCR_PME (0x1ul << 32) /* PMU Event-based exception Enable */ +#define BESCR_LME (0x1ul << 34) /* Load Monitor Enable */ +#define BESCR_LMEO (0x1ul << 2) /* Load Monitor Exception Occurred */ + +#define SPRN_LMRR 813 /* Load Monitor Region Register */ +#define SPRN_LMSER 814 /* Load Monitor Section Enable Register */ #define SPRN_PMC1 771 #define SPRN_PMC2 772 diff --git a/tools/testing/selftests/powerpc/tm/.gitignore b/tools/testing/selftests/powerpc/tm/.gitignore index bb942db845bf..82c0a9ce6e74 100644 --- a/tools/testing/selftests/powerpc/tm/.gitignore +++ b/tools/testing/selftests/powerpc/tm/.gitignore @@ -6,3 +6,4 @@ tm-vmxcopy tm-fork tm-tar tm-tmspr +tm-exec diff --git a/tools/testing/selftests/powerpc/tm/Makefile b/tools/testing/selftests/powerpc/tm/Makefile index d0505dbd22d5..9d301d785d9e 100644 --- a/tools/testing/selftests/powerpc/tm/Makefile +++ b/tools/testing/selftests/powerpc/tm/Makefile @@ -1,11 +1,14 @@ -TEST_PROGS := tm-resched-dscr tm-syscall tm-signal-msr-resv tm-signal-stack tm-vmxcopy tm-fork tm-tar tm-tmspr +TEST_PROGS := tm-resched-dscr tm-syscall tm-signal-msr-resv tm-signal-stack \ + tm-vmxcopy tm-fork tm-tar tm-tmspr tm-exec tm-execed all: $(TEST_PROGS) $(TEST_PROGS): ../harness.c ../utils.c +CFLAGS += -mhtm + tm-syscall: tm-syscall-asm.S -tm-syscall: CFLAGS += -mhtm -I../../../../../usr/include +tm-syscall: CFLAGS += -I../../../../../usr/include tm-tmspr: CFLAGS += -pthread include ../../lib.mk diff --git a/tools/testing/selftests/powerpc/tm/tm-exec.c b/tools/testing/selftests/powerpc/tm/tm-exec.c new file mode 100644 index 000000000000..3d27fa0ece04 --- /dev/null +++ b/tools/testing/selftests/powerpc/tm/tm-exec.c @@ -0,0 +1,70 @@ +/* + * Copyright 2016, Cyril Bur, IBM Corp. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Syscalls can be performed provided the transactions are suspended. + * The exec() class of syscall is unique as a new process is loaded. + * + * It makes little sense for after an exec() call for the previously + * suspended transaction to still exist. + */ + +#define _GNU_SOURCE +#include <errno.h> +#include <inttypes.h> +#include <libgen.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "utils.h" +#include "tm.h" + +static char *path; + +static int test_exec(void) +{ + SKIP_IF(!have_htm()); + + asm __volatile__( + "tbegin.;" + "blt 1f; " + "tsuspend.;" + "1: ;" + : : : "memory"); + + execl(path, "tm-exec", "--child", NULL); + + /* Shouldn't get here */ + perror("execl() failed"); + return 1; +} + +static int after_exec(void) +{ + asm __volatile__( + "tbegin.;" + "blt 1f;" + "tsuspend.;" + "1: ;" + : : : "memory"); + + FAIL_IF(failure_is_nesting()); + return 0; +} + +int main(int argc, char *argv[]) +{ + path = argv[0]; + + if (argc > 1 && strcmp(argv[1], "--child") == 0) + return after_exec(); + + return test_harness(test_exec, "tm_exec"); +} diff --git a/tools/testing/selftests/powerpc/tm/tm-syscall.c b/tools/testing/selftests/powerpc/tm/tm-syscall.c index 60560cb20e38..454b965a2db3 100644 --- a/tools/testing/selftests/powerpc/tm/tm-syscall.c +++ b/tools/testing/selftests/powerpc/tm/tm-syscall.c @@ -27,21 +27,6 @@ unsigned retries = 0; #define TEST_DURATION 10 /* seconds */ #define TM_RETRIES 100 -long failure_code(void) -{ - return __builtin_get_texasru() >> 24; -} - -bool failure_is_persistent(void) -{ - return (failure_code() & TM_CAUSE_PERSISTENT) == TM_CAUSE_PERSISTENT; -} - -bool failure_is_syscall(void) -{ - return (failure_code() & TM_CAUSE_SYSCALL) == TM_CAUSE_SYSCALL; -} - pid_t getppid_tm(bool suspend) { int i; diff --git a/tools/testing/selftests/powerpc/tm/tm.h b/tools/testing/selftests/powerpc/tm/tm.h index 24144b25772c..60318bad7d7a 100644 --- a/tools/testing/selftests/powerpc/tm/tm.h +++ b/tools/testing/selftests/powerpc/tm/tm.h @@ -6,8 +6,9 @@ #ifndef _SELFTESTS_POWERPC_TM_TM_H #define _SELFTESTS_POWERPC_TM_TM_H -#include <stdbool.h> +#include <asm/tm.h> #include <asm/cputable.h> +#include <stdbool.h> #include "../utils.h" @@ -31,4 +32,24 @@ static inline bool have_htm_nosc(void) #endif } +static inline long failure_code(void) +{ + return __builtin_get_texasru() >> 24; +} + +static inline bool failure_is_persistent(void) +{ + return (failure_code() & TM_CAUSE_PERSISTENT) == TM_CAUSE_PERSISTENT; +} + +static inline bool failure_is_syscall(void) +{ + return (failure_code() & TM_CAUSE_SYSCALL) == TM_CAUSE_SYSCALL; +} + +static inline bool failure_is_nesting(void) +{ + return (__builtin_get_texasru() & 0x400000); +} + #endif /* _SELFTESTS_POWERPC_TM_TM_H */ diff --git a/tools/testing/selftests/powerpc/utils.h b/tools/testing/selftests/powerpc/utils.h index a985cfaa535e..fbd33e52ef8f 100644 --- a/tools/testing/selftests/powerpc/utils.h +++ b/tools/testing/selftests/powerpc/utils.h @@ -27,6 +27,11 @@ int test_harness(int (test_function)(void), char *name); extern void *get_auxv_entry(int type); int pick_online_cpu(void); +static inline bool have_hwcap(unsigned long ftr) +{ + return ((unsigned long)get_auxv_entry(AT_HWCAP) & ftr) == ftr; +} + static inline bool have_hwcap2(unsigned long ftr2) { return ((unsigned long)get_auxv_entry(AT_HWCAP2) & ftr2) == ftr2; diff --git a/tools/testing/selftests/timers/Makefile b/tools/testing/selftests/timers/Makefile index 4a1be1b75a7f..1d5556869137 100644 --- a/tools/testing/selftests/timers/Makefile +++ b/tools/testing/selftests/timers/Makefile @@ -10,7 +10,7 @@ TEST_PROGS = posix_timers nanosleep nsleep-lat set-timer-lat mqueue-lat \ TEST_PROGS_EXTENDED = alarmtimer-suspend valid-adjtimex adjtick change_skew \ skew_consistency clocksource-switch leap-a-day \ - leapcrash set-tai set-2038 + leapcrash set-tai set-2038 set-tz bins = $(TEST_PROGS) $(TEST_PROGS_EXTENDED) @@ -30,6 +30,7 @@ run_destructive_tests: run_tests ./clocksource-switch ./leap-a-day -s -i 10 ./leapcrash + ./set-tz ./set-tai ./set-2038 diff --git a/tools/testing/selftests/timers/set-tz.c b/tools/testing/selftests/timers/set-tz.c new file mode 100644 index 000000000000..f4184928b16b --- /dev/null +++ b/tools/testing/selftests/timers/set-tz.c @@ -0,0 +1,119 @@ +/* Set tz value + * by: John Stultz <john.stultz@linaro.org> + * (C) Copyright Linaro 2016 + * Licensed under the GPLv2 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <sys/time.h> +#include <sys/timex.h> +#include <string.h> +#include <signal.h> +#include <unistd.h> +#ifdef KTEST +#include "../kselftest.h" +#else +static inline int ksft_exit_pass(void) +{ + exit(0); +} +static inline int ksft_exit_fail(void) +{ + exit(1); +} +#endif + +int set_tz(int min, int dst) +{ + struct timezone tz; + + tz.tz_minuteswest = min; + tz.tz_dsttime = dst; + + return settimeofday(0, &tz); +} + +int get_tz_min(void) +{ + struct timezone tz; + struct timeval tv; + + memset(&tz, 0, sizeof(tz)); + gettimeofday(&tv, &tz); + return tz.tz_minuteswest; +} + +int get_tz_dst(void) +{ + struct timezone tz; + struct timeval tv; + + memset(&tz, 0, sizeof(tz)); + gettimeofday(&tv, &tz); + return tz.tz_dsttime; +} + +int main(int argc, char **argv) +{ + int i, ret; + int min, dst; + + min = get_tz_min(); + dst = get_tz_dst(); + printf("tz_minuteswest started at %i, dst at %i\n", min, dst); + + printf("Checking tz_minuteswest can be properly set: "); + for (i = -15*60; i < 15*60; i += 30) { + ret = set_tz(i, dst); + ret = get_tz_min(); + if (ret != i) { + printf("[FAILED] expected: %i got %i\n", i, ret); + goto err; + } + } + printf("[OK]\n"); + + printf("Checking invalid tz_minuteswest values are caught: "); + + if (!set_tz(-15*60-1, dst)) { + printf("[FAILED] %i didn't return failure!\n", -15*60-1); + goto err; + } + + if (!set_tz(15*60+1, dst)) { + printf("[FAILED] %i didn't return failure!\n", 15*60+1); + goto err; + } + + if (!set_tz(-24*60, dst)) { + printf("[FAILED] %i didn't return failure!\n", -24*60); + goto err; + } + + if (!set_tz(24*60, dst)) { + printf("[FAILED] %i didn't return failure!\n", 24*60); + goto err; + } + + printf("[OK]\n"); + + set_tz(min, dst); + return ksft_exit_pass(); + +err: + set_tz(min, dst); + return ksft_exit_fail(); +} diff --git a/tools/testing/selftests/vm/compaction_test.c b/tools/testing/selftests/vm/compaction_test.c index 00c4f65d12da..6d1437f895b8 100644 --- a/tools/testing/selftests/vm/compaction_test.c +++ b/tools/testing/selftests/vm/compaction_test.c @@ -101,7 +101,7 @@ int check_compaction(unsigned long mem_free, unsigned int hugepage_size) /* Start with the initial condition of 0 huge pages*/ if (write(fd, "0", sizeof(char)) != sizeof(char)) { - perror("Failed to write to /proc/sys/vm/nr_hugepages\n"); + perror("Failed to write 0 to /proc/sys/vm/nr_hugepages\n"); goto close_fd; } @@ -110,14 +110,14 @@ int check_compaction(unsigned long mem_free, unsigned int hugepage_size) /* Request a large number of huge pages. The Kernel will allocate as much as it can */ if (write(fd, "100000", (6*sizeof(char))) != (6*sizeof(char))) { - perror("Failed to write to /proc/sys/vm/nr_hugepages\n"); + perror("Failed to write 100000 to /proc/sys/vm/nr_hugepages\n"); goto close_fd; } lseek(fd, 0, SEEK_SET); if (read(fd, nr_hugepages, sizeof(nr_hugepages)) <= 0) { - perror("Failed to read from /proc/sys/vm/nr_hugepages\n"); + perror("Failed to re-read from /proc/sys/vm/nr_hugepages\n"); goto close_fd; } @@ -138,7 +138,7 @@ int check_compaction(unsigned long mem_free, unsigned int hugepage_size) if (write(fd, initial_nr_hugepages, strlen(initial_nr_hugepages)) != strlen(initial_nr_hugepages)) { - perror("Failed to write to /proc/sys/vm/nr_hugepages\n"); + perror("Failed to write value to /proc/sys/vm/nr_hugepages\n"); goto close_fd; } diff --git a/tools/testing/selftests/vm/on-fault-limit.c b/tools/testing/selftests/vm/on-fault-limit.c index 245acccce42d..0ae458f32fdb 100644 --- a/tools/testing/selftests/vm/on-fault-limit.c +++ b/tools/testing/selftests/vm/on-fault-limit.c @@ -20,7 +20,7 @@ static int test_limit(void) return ret; } - if (mlockall(MCL_CURRENT | MCL_ONFAULT | MCL_FUTURE)) { + if (mlockall(MCL_ONFAULT | MCL_FUTURE)) { perror("mlockall"); return ret; } |