Index: projects/clang350-import/contrib/binutils/gas/config/tc-ppc.c =================================================================== --- projects/clang350-import/contrib/binutils/gas/config/tc-ppc.c (revision 276347) +++ projects/clang350-import/contrib/binutils/gas/config/tc-ppc.c (revision 276348) @@ -1,6188 +1,6188 @@ /* tc-ppc.c -- Assemble for the PowerPC or POWER (RS/6000) Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. Written by Ian Lance Taylor, Cygnus Support. This file is part of GAS, the GNU Assembler. GAS is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GAS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GAS; see the file COPYING. If not, write to the Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ #include "as.h" #include "safe-ctype.h" #include "subsegs.h" #include "dw2gencfi.h" #include "opcode/ppc.h" #ifdef OBJ_ELF #include "elf/ppc.h" #include "dwarf2dbg.h" #endif #ifdef TE_PE #include "coff/pe.h" #endif /* This is the assembler for the PowerPC or POWER (RS/6000) chips. */ /* Tell the main code what the endianness is. */ extern int target_big_endian; /* Whether or not, we've set target_big_endian. */ static int set_target_endian = 0; /* Whether to use user friendly register names. */ #ifndef TARGET_REG_NAMES_P #ifdef TE_PE #define TARGET_REG_NAMES_P TRUE #else #define TARGET_REG_NAMES_P FALSE #endif #endif /* Macros for calculating LO, HI, HA, HIGHER, HIGHERA, HIGHEST, HIGHESTA. */ /* #lo(value) denotes the least significant 16 bits of the indicated. */ #define PPC_LO(v) ((v) & 0xffff) /* #hi(value) denotes bits 16 through 31 of the indicated value. */ #define PPC_HI(v) (((v) >> 16) & 0xffff) /* #ha(value) denotes the high adjusted value: bits 16 through 31 of the indicated value, compensating for #lo() being treated as a signed number. */ #define PPC_HA(v) PPC_HI ((v) + 0x8000) /* #higher(value) denotes bits 32 through 47 of the indicated value. */ #define PPC_HIGHER(v) (((v) >> 16 >> 16) & 0xffff) /* #highera(value) denotes bits 32 through 47 of the indicated value, compensating for #lo() being treated as a signed number. */ #define PPC_HIGHERA(v) PPC_HIGHER ((v) + 0x8000) /* #highest(value) denotes bits 48 through 63 of the indicated value. */ #define PPC_HIGHEST(v) (((v) >> 24 >> 24) & 0xffff) /* #highesta(value) denotes bits 48 through 63 of the indicated value, compensating for #lo being treated as a signed number. */ #define PPC_HIGHESTA(v) PPC_HIGHEST ((v) + 0x8000) #define SEX16(val) ((((val) & 0xffff) ^ 0x8000) - 0x8000) static bfd_boolean reg_names_p = TARGET_REG_NAMES_P; static void ppc_macro (char *, const struct powerpc_macro *); static void ppc_byte (int); #if defined (OBJ_XCOFF) || defined (OBJ_ELF) static void ppc_tc (int); static void ppc_machine (int); #endif #ifdef OBJ_XCOFF static void ppc_comm (int); static void ppc_bb (int); static void ppc_bc (int); static void ppc_bf (int); static void ppc_biei (int); static void ppc_bs (int); static void ppc_eb (int); static void ppc_ec (int); static void ppc_ef (int); static void ppc_es (int); static void ppc_csect (int); static void ppc_change_csect (symbolS *, offsetT); static void ppc_function (int); static void ppc_extern (int); static void ppc_lglobl (int); static void ppc_section (int); static void ppc_named_section (int); static void ppc_stabx (int); static void ppc_rename (int); static void ppc_toc (int); static void ppc_xcoff_cons (int); static void ppc_vbyte (int); #endif #ifdef OBJ_ELF static void ppc_elf_cons (int); static void ppc_elf_rdata (int); static void ppc_elf_lcomm (int); #endif #ifdef TE_PE static void ppc_previous (int); static void ppc_pdata (int); static void ppc_ydata (int); static void ppc_reldata (int); static void ppc_rdata (int); static void ppc_ualong (int); static void ppc_znop (int); static void ppc_pe_comm (int); static void ppc_pe_section (int); static void ppc_pe_function (int); static void ppc_pe_tocd (int); #endif /* Generic assembler global variables which must be defined by all targets. */ #ifdef OBJ_ELF /* This string holds the chars that always start a comment. If the pre-processor is disabled, these aren't very useful. The macro tc_comment_chars points to this. We use this, rather than the usual comment_chars, so that we can switch for Solaris conventions. */ static const char ppc_solaris_comment_chars[] = "#!"; static const char ppc_eabi_comment_chars[] = "#"; #ifdef TARGET_SOLARIS_COMMENT const char *ppc_comment_chars = ppc_solaris_comment_chars; #else const char *ppc_comment_chars = ppc_eabi_comment_chars; #endif #else const char comment_chars[] = "#"; #endif /* Characters which start a comment at the beginning of a line. */ const char line_comment_chars[] = "#"; /* Characters which may be used to separate multiple commands on a single line. */ const char line_separator_chars[] = ";"; /* Characters which are used to indicate an exponent in a floating point number. */ const char EXP_CHARS[] = "eE"; /* Characters which mean that a number is a floating point constant, as in 0d1.0. */ const char FLT_CHARS[] = "dD"; /* Anything that can start an operand needs to be mentioned here, to stop the input scrubber eating whitespace. */ const char ppc_symbol_chars[] = "%["; /* The dwarf2 data alignment, adjusted for 32 or 64 bit. */ int ppc_cie_data_alignment; /* The target specific pseudo-ops which we support. */ const pseudo_typeS md_pseudo_table[] = { /* Pseudo-ops which must be overridden. */ { "byte", ppc_byte, 0 }, #ifdef OBJ_XCOFF /* Pseudo-ops specific to the RS/6000 XCOFF format. Some of these legitimately belong in the obj-*.c file. However, XCOFF is based on COFF, and is only implemented for the RS/6000. We just use obj-coff.c, and add what we need here. */ { "comm", ppc_comm, 0 }, { "lcomm", ppc_comm, 1 }, { "bb", ppc_bb, 0 }, { "bc", ppc_bc, 0 }, { "bf", ppc_bf, 0 }, { "bi", ppc_biei, 0 }, { "bs", ppc_bs, 0 }, { "csect", ppc_csect, 0 }, { "data", ppc_section, 'd' }, { "eb", ppc_eb, 0 }, { "ec", ppc_ec, 0 }, { "ef", ppc_ef, 0 }, { "ei", ppc_biei, 1 }, { "es", ppc_es, 0 }, { "extern", ppc_extern, 0 }, { "function", ppc_function, 0 }, { "lglobl", ppc_lglobl, 0 }, { "rename", ppc_rename, 0 }, { "section", ppc_named_section, 0 }, { "stabx", ppc_stabx, 0 }, { "text", ppc_section, 't' }, { "toc", ppc_toc, 0 }, { "long", ppc_xcoff_cons, 2 }, { "llong", ppc_xcoff_cons, 3 }, { "word", ppc_xcoff_cons, 1 }, { "short", ppc_xcoff_cons, 1 }, { "vbyte", ppc_vbyte, 0 }, #endif #ifdef OBJ_ELF { "llong", ppc_elf_cons, 8 }, { "quad", ppc_elf_cons, 8 }, { "long", ppc_elf_cons, 4 }, { "word", ppc_elf_cons, 2 }, { "short", ppc_elf_cons, 2 }, { "rdata", ppc_elf_rdata, 0 }, { "rodata", ppc_elf_rdata, 0 }, { "lcomm", ppc_elf_lcomm, 0 }, #endif #ifdef TE_PE /* Pseudo-ops specific to the Windows NT PowerPC PE (coff) format. */ { "previous", ppc_previous, 0 }, { "pdata", ppc_pdata, 0 }, { "ydata", ppc_ydata, 0 }, { "reldata", ppc_reldata, 0 }, { "rdata", ppc_rdata, 0 }, { "ualong", ppc_ualong, 0 }, { "znop", ppc_znop, 0 }, { "comm", ppc_pe_comm, 0 }, { "lcomm", ppc_pe_comm, 1 }, { "section", ppc_pe_section, 0 }, { "function", ppc_pe_function,0 }, { "tocd", ppc_pe_tocd, 0 }, #endif #if defined (OBJ_XCOFF) || defined (OBJ_ELF) { "tc", ppc_tc, 0 }, { "machine", ppc_machine, 0 }, #endif { NULL, NULL, 0 } }; /* Predefined register names if -mregnames (or default for Windows NT). In general, there are lots of them, in an attempt to be compatible with a number of other Windows NT assemblers. */ /* Structure to hold information about predefined registers. */ struct pd_reg { char *name; int value; }; /* List of registers that are pre-defined: Each general register has predefined names of the form: 1. r which has the value . 2. r. which has the value . Each floating point register has predefined names of the form: 1. f which has the value . 2. f. which has the value . Each vector unit register has predefined names of the form: 1. v which has the value . 2. v. which has the value . Each condition register has predefined names of the form: 1. cr which has the value . 2. cr. which has the value . There are individual registers as well: sp or r.sp has the value 1 rtoc or r.toc has the value 2 fpscr has the value 0 xer has the value 1 lr has the value 8 ctr has the value 9 pmr has the value 0 dar has the value 19 dsisr has the value 18 dec has the value 22 sdr1 has the value 25 srr0 has the value 26 srr1 has the value 27 The table is sorted. Suitable for searching by a binary search. */ static const struct pd_reg pre_defined_registers[] = { { "cr.0", 0 }, /* Condition Registers */ { "cr.1", 1 }, { "cr.2", 2 }, { "cr.3", 3 }, { "cr.4", 4 }, { "cr.5", 5 }, { "cr.6", 6 }, { "cr.7", 7 }, { "cr0", 0 }, { "cr1", 1 }, { "cr2", 2 }, { "cr3", 3 }, { "cr4", 4 }, { "cr5", 5 }, { "cr6", 6 }, { "cr7", 7 }, { "ctr", 9 }, { "dar", 19 }, /* Data Access Register */ { "dec", 22 }, /* Decrementer */ { "dsisr", 18 }, /* Data Storage Interrupt Status Register */ { "f.0", 0 }, /* Floating point registers */ { "f.1", 1 }, { "f.10", 10 }, { "f.11", 11 }, { "f.12", 12 }, { "f.13", 13 }, { "f.14", 14 }, { "f.15", 15 }, { "f.16", 16 }, { "f.17", 17 }, { "f.18", 18 }, { "f.19", 19 }, { "f.2", 2 }, { "f.20", 20 }, { "f.21", 21 }, { "f.22", 22 }, { "f.23", 23 }, { "f.24", 24 }, { "f.25", 25 }, { "f.26", 26 }, { "f.27", 27 }, { "f.28", 28 }, { "f.29", 29 }, { "f.3", 3 }, { "f.30", 30 }, { "f.31", 31 }, { "f.4", 4 }, { "f.5", 5 }, { "f.6", 6 }, { "f.7", 7 }, { "f.8", 8 }, { "f.9", 9 }, { "f0", 0 }, { "f1", 1 }, { "f10", 10 }, { "f11", 11 }, { "f12", 12 }, { "f13", 13 }, { "f14", 14 }, { "f15", 15 }, { "f16", 16 }, { "f17", 17 }, { "f18", 18 }, { "f19", 19 }, { "f2", 2 }, { "f20", 20 }, { "f21", 21 }, { "f22", 22 }, { "f23", 23 }, { "f24", 24 }, { "f25", 25 }, { "f26", 26 }, { "f27", 27 }, { "f28", 28 }, { "f29", 29 }, { "f3", 3 }, { "f30", 30 }, { "f31", 31 }, { "f4", 4 }, { "f5", 5 }, { "f6", 6 }, { "f7", 7 }, { "f8", 8 }, { "f9", 9 }, { "fpscr", 0 }, { "lr", 8 }, /* Link Register */ { "pmr", 0 }, { "r.0", 0 }, /* General Purpose Registers */ { "r.1", 1 }, { "r.10", 10 }, { "r.11", 11 }, { "r.12", 12 }, { "r.13", 13 }, { "r.14", 14 }, { "r.15", 15 }, { "r.16", 16 }, { "r.17", 17 }, { "r.18", 18 }, { "r.19", 19 }, { "r.2", 2 }, { "r.20", 20 }, { "r.21", 21 }, { "r.22", 22 }, { "r.23", 23 }, { "r.24", 24 }, { "r.25", 25 }, { "r.26", 26 }, { "r.27", 27 }, { "r.28", 28 }, { "r.29", 29 }, { "r.3", 3 }, { "r.30", 30 }, { "r.31", 31 }, { "r.4", 4 }, { "r.5", 5 }, { "r.6", 6 }, { "r.7", 7 }, { "r.8", 8 }, { "r.9", 9 }, { "r.sp", 1 }, /* Stack Pointer */ { "r.toc", 2 }, /* Pointer to the table of contents */ { "r0", 0 }, /* More general purpose registers */ { "r1", 1 }, { "r10", 10 }, { "r11", 11 }, { "r12", 12 }, { "r13", 13 }, { "r14", 14 }, { "r15", 15 }, { "r16", 16 }, { "r17", 17 }, { "r18", 18 }, { "r19", 19 }, { "r2", 2 }, { "r20", 20 }, { "r21", 21 }, { "r22", 22 }, { "r23", 23 }, { "r24", 24 }, { "r25", 25 }, { "r26", 26 }, { "r27", 27 }, { "r28", 28 }, { "r29", 29 }, { "r3", 3 }, { "r30", 30 }, { "r31", 31 }, { "r4", 4 }, { "r5", 5 }, { "r6", 6 }, { "r7", 7 }, { "r8", 8 }, { "r9", 9 }, { "rtoc", 2 }, /* Table of contents */ { "sdr1", 25 }, /* Storage Description Register 1 */ { "sp", 1 }, { "srr0", 26 }, /* Machine Status Save/Restore Register 0 */ { "srr1", 27 }, /* Machine Status Save/Restore Register 1 */ { "v.0", 0 }, /* Vector registers */ { "v.1", 1 }, { "v.10", 10 }, { "v.11", 11 }, { "v.12", 12 }, { "v.13", 13 }, { "v.14", 14 }, { "v.15", 15 }, { "v.16", 16 }, { "v.17", 17 }, { "v.18", 18 }, { "v.19", 19 }, { "v.2", 2 }, { "v.20", 20 }, { "v.21", 21 }, { "v.22", 22 }, { "v.23", 23 }, { "v.24", 24 }, { "v.25", 25 }, { "v.26", 26 }, { "v.27", 27 }, { "v.28", 28 }, { "v.29", 29 }, { "v.3", 3 }, { "v.30", 30 }, { "v.31", 31 }, { "v.4", 4 }, { "v.5", 5 }, { "v.6", 6 }, { "v.7", 7 }, { "v.8", 8 }, { "v.9", 9 }, { "v0", 0 }, { "v1", 1 }, { "v10", 10 }, { "v11", 11 }, { "v12", 12 }, { "v13", 13 }, { "v14", 14 }, { "v15", 15 }, { "v16", 16 }, { "v17", 17 }, { "v18", 18 }, { "v19", 19 }, { "v2", 2 }, { "v20", 20 }, { "v21", 21 }, { "v22", 22 }, { "v23", 23 }, { "v24", 24 }, { "v25", 25 }, { "v26", 26 }, { "v27", 27 }, { "v28", 28 }, { "v29", 29 }, { "v3", 3 }, { "v30", 30 }, { "v31", 31 }, { "v4", 4 }, { "v5", 5 }, { "v6", 6 }, { "v7", 7 }, { "v8", 8 }, { "v9", 9 }, { "xer", 1 }, }; #define REG_NAME_CNT (sizeof (pre_defined_registers) / sizeof (struct pd_reg)) /* Given NAME, find the register number associated with that name, return the integer value associated with the given name or -1 on failure. */ static int reg_name_search (const struct pd_reg *regs, int regcount, const char *name) { int middle, low, high; int cmp; low = 0; high = regcount - 1; do { middle = (low + high) / 2; cmp = strcasecmp (name, regs[middle].name); if (cmp < 0) high = middle - 1; else if (cmp > 0) low = middle + 1; else return regs[middle].value; } while (low <= high); return -1; } /* * Summary of register_name. * * in: Input_line_pointer points to 1st char of operand. * * out: A expressionS. * The operand may have been a register: in this case, X_op == O_register, * X_add_number is set to the register number, and truth is returned. * Input_line_pointer->(next non-blank) char after operand, or is in its * original state. */ static bfd_boolean register_name (expressionS *expressionP) { int reg_number; char *name; char *start; char c; /* Find the spelling of the operand. */ start = name = input_line_pointer; if (name[0] == '%' && ISALPHA (name[1])) name = ++input_line_pointer; else if (!reg_names_p || !ISALPHA (name[0])) return FALSE; c = get_symbol_end (); reg_number = reg_name_search (pre_defined_registers, REG_NAME_CNT, name); /* Put back the delimiting char. */ *input_line_pointer = c; /* Look to see if it's in the register table. */ if (reg_number >= 0) { expressionP->X_op = O_register; expressionP->X_add_number = reg_number; /* Make the rest nice. */ expressionP->X_add_symbol = NULL; expressionP->X_op_symbol = NULL; return TRUE; } /* Reset the line as if we had not done anything. */ input_line_pointer = start; return FALSE; } /* This function is called for each symbol seen in an expression. It handles the special parsing which PowerPC assemblers are supposed to use for condition codes. */ /* Whether to do the special parsing. */ static bfd_boolean cr_operand; /* Names to recognize in a condition code. This table is sorted. */ static const struct pd_reg cr_names[] = { { "cr0", 0 }, { "cr1", 1 }, { "cr2", 2 }, { "cr3", 3 }, { "cr4", 4 }, { "cr5", 5 }, { "cr6", 6 }, { "cr7", 7 }, { "eq", 2 }, { "gt", 1 }, { "lt", 0 }, { "so", 3 }, { "un", 3 } }; /* Parsing function. This returns non-zero if it recognized an expression. */ int ppc_parse_name (const char *name, expressionS *expr) { int val; if (! cr_operand) return 0; val = reg_name_search (cr_names, sizeof cr_names / sizeof cr_names[0], name); if (val < 0) return 0; expr->X_op = O_constant; expr->X_add_number = val; return 1; } /* Local variables. */ /* The type of processor we are assembling for. This is one or more of the PPC_OPCODE flags defined in opcode/ppc.h. */ static unsigned long ppc_cpu = PPC_OPCODE_ANY; /* Whether to target xcoff64/elf64. */ static unsigned int ppc_obj64 = BFD_DEFAULT_TARGET_SIZE == 64; /* Opcode hash table. */ static struct hash_control *ppc_hash; /* Macro hash table. */ static struct hash_control *ppc_macro_hash; #ifdef OBJ_ELF /* What type of shared library support to use. */ static enum { SHLIB_NONE, SHLIB_PIC, SHLIB_MRELOCATABLE } shlib = SHLIB_NONE; /* Flags to set in the elf header. */ static flagword ppc_flags = 0; /* Whether this is Solaris or not. */ #ifdef TARGET_SOLARIS_COMMENT #define SOLARIS_P TRUE #else #define SOLARIS_P FALSE #endif static bfd_boolean msolaris = SOLARIS_P; #endif #ifdef OBJ_XCOFF /* The RS/6000 assembler uses the .csect pseudo-op to generate code using a bunch of different sections. These assembler sections, however, are all encompassed within the .text or .data sections of the final output file. We handle this by using different subsegments within these main segments. */ /* Next subsegment to allocate within the .text segment. */ static subsegT ppc_text_subsegment = 2; /* Linked list of csects in the text section. */ static symbolS *ppc_text_csects; /* Next subsegment to allocate within the .data segment. */ static subsegT ppc_data_subsegment = 2; /* Linked list of csects in the data section. */ static symbolS *ppc_data_csects; /* The current csect. */ static symbolS *ppc_current_csect; /* The RS/6000 assembler uses a TOC which holds addresses of functions and variables. Symbols are put in the TOC with the .tc pseudo-op. A special relocation is used when accessing TOC entries. We handle the TOC as a subsegment within the .data segment. We set it up if we see a .toc pseudo-op, and save the csect symbol here. */ static symbolS *ppc_toc_csect; /* The first frag in the TOC subsegment. */ static fragS *ppc_toc_frag; /* The first frag in the first subsegment after the TOC in the .data segment. NULL if there are no subsegments after the TOC. */ static fragS *ppc_after_toc_frag; /* The current static block. */ static symbolS *ppc_current_block; /* The COFF debugging section; set by md_begin. This is not the .debug section, but is instead the secret BFD section which will cause BFD to set the section number of a symbol to N_DEBUG. */ static asection *ppc_coff_debug_section; #endif /* OBJ_XCOFF */ #ifdef TE_PE /* Various sections that we need for PE coff support. */ static segT ydata_section; static segT pdata_section; static segT reldata_section; static segT rdata_section; static segT tocdata_section; /* The current section and the previous section. See ppc_previous. */ static segT ppc_previous_section; static segT ppc_current_section; #endif /* TE_PE */ #ifdef OBJ_ELF symbolS *GOT_symbol; /* Pre-defined "_GLOBAL_OFFSET_TABLE" */ #define PPC_APUINFO_ISEL 0x40 #define PPC_APUINFO_PMR 0x41 #define PPC_APUINFO_RFMCI 0x42 #define PPC_APUINFO_CACHELCK 0x43 #define PPC_APUINFO_SPE 0x100 #define PPC_APUINFO_EFS 0x101 #define PPC_APUINFO_BRLOCK 0x102 /* * We keep a list of APUinfo */ unsigned long *ppc_apuinfo_list; unsigned int ppc_apuinfo_num; unsigned int ppc_apuinfo_num_alloc; #endif /* OBJ_ELF */ #ifdef OBJ_ELF const char *const md_shortopts = "b:l:usm:K:VQ:"; #else const char *const md_shortopts = "um:"; #endif const struct option md_longopts[] = { {NULL, no_argument, NULL, 0} }; const size_t md_longopts_size = sizeof (md_longopts); /* Handle -m options that set cpu type, and .machine arg. */ static int parse_cpu (const char *arg) { /* -mpwrx and -mpwr2 mean to assemble for the IBM POWER/2 (RIOS2). */ if (strcmp (arg, "pwrx") == 0 || strcmp (arg, "pwr2") == 0) ppc_cpu = PPC_OPCODE_POWER | PPC_OPCODE_POWER2 | PPC_OPCODE_32; /* -mpwr means to assemble for the IBM POWER (RIOS1). */ else if (strcmp (arg, "pwr") == 0) ppc_cpu = PPC_OPCODE_POWER | PPC_OPCODE_32; /* -m601 means to assemble for the PowerPC 601, which includes instructions that are holdovers from the Power. */ else if (strcmp (arg, "601") == 0) ppc_cpu = (PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC | PPC_OPCODE_601 | PPC_OPCODE_32); /* -mppc, -mppc32, -m603, and -m604 mean to assemble for the PowerPC 603/604. */ else if (strcmp (arg, "ppc") == 0 || strcmp (arg, "ppc32") == 0 || strcmp (arg, "603") == 0 || strcmp (arg, "604") == 0) ppc_cpu = PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC | PPC_OPCODE_32; /* -m403 and -m405 mean to assemble for the PowerPC 403/405. */ else if (strcmp (arg, "403") == 0 || strcmp (arg, "405") == 0) ppc_cpu = (PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC | PPC_OPCODE_403 | PPC_OPCODE_32); else if (strcmp (arg, "440") == 0) ppc_cpu = (PPC_OPCODE_PPC | PPC_OPCODE_BOOKE | PPC_OPCODE_32 | PPC_OPCODE_440 | PPC_OPCODE_ISEL | PPC_OPCODE_RFMCI); else if (strcmp (arg, "7400") == 0 || strcmp (arg, "7410") == 0 || strcmp (arg, "7450") == 0 || strcmp (arg, "7455") == 0) ppc_cpu = (PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC | PPC_OPCODE_ALTIVEC | PPC_OPCODE_32); else if (strcmp (arg, "e300") == 0) ppc_cpu = (PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC | PPC_OPCODE_32 | PPC_OPCODE_E300); else if (strcmp (arg, "altivec") == 0) { if (ppc_cpu == 0) ppc_cpu = PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC | PPC_OPCODE_ALTIVEC; else ppc_cpu |= PPC_OPCODE_ALTIVEC; } else if (strcmp (arg, "e500") == 0 || strcmp (arg, "e500x2") == 0) { ppc_cpu = (PPC_OPCODE_PPC | PPC_OPCODE_BOOKE | PPC_OPCODE_SPE | PPC_OPCODE_ISEL | PPC_OPCODE_EFS | PPC_OPCODE_BRLOCK | PPC_OPCODE_PMR | PPC_OPCODE_CACHELCK | PPC_OPCODE_RFMCI); } else if (strcmp (arg, "spe") == 0) { if (ppc_cpu == 0) ppc_cpu = PPC_OPCODE_PPC | PPC_OPCODE_SPE | PPC_OPCODE_EFS; else ppc_cpu |= PPC_OPCODE_SPE; } /* -mppc64 and -m620 mean to assemble for the 64-bit PowerPC 620. */ else if (strcmp (arg, "ppc64") == 0 || strcmp (arg, "620") == 0) { ppc_cpu = PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC | PPC_OPCODE_64; } else if (strcmp (arg, "ppc64bridge") == 0) { ppc_cpu = (PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC | PPC_OPCODE_64_BRIDGE | PPC_OPCODE_64); } /* -mbooke/-mbooke32 mean enable 32-bit BookE support. */ else if (strcmp (arg, "booke") == 0 || strcmp (arg, "booke32") == 0) { ppc_cpu = PPC_OPCODE_PPC | PPC_OPCODE_BOOKE | PPC_OPCODE_32; } /* -mbooke64 means enable 64-bit BookE support. */ else if (strcmp (arg, "booke64") == 0) { ppc_cpu = (PPC_OPCODE_PPC | PPC_OPCODE_BOOKE | PPC_OPCODE_BOOKE64 | PPC_OPCODE_64); } else if (strcmp (arg, "power4") == 0) { ppc_cpu = (PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC | PPC_OPCODE_64 | PPC_OPCODE_POWER4); } else if (strcmp (arg, "power5") == 0) { ppc_cpu = (PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC | PPC_OPCODE_64 | PPC_OPCODE_POWER4 | PPC_OPCODE_POWER5); } else if (strcmp (arg, "power6") == 0) { ppc_cpu = (PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC | PPC_OPCODE_64 | PPC_OPCODE_POWER4 | PPC_OPCODE_POWER5 | PPC_OPCODE_POWER6); } else if (strcmp (arg, "cell") == 0) { ppc_cpu = (PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC | PPC_OPCODE_64 | PPC_OPCODE_POWER4 | PPC_OPCODE_CELL); } /* -mcom means assemble for the common intersection between Power and PowerPC. At present, we just allow the union, rather than the intersection. */ else if (strcmp (arg, "com") == 0) ppc_cpu = PPC_OPCODE_COMMON | PPC_OPCODE_32; /* -many means to assemble for any architecture (PWR/PWRX/PPC). */ else if (strcmp (arg, "any") == 0) ppc_cpu |= PPC_OPCODE_ANY; else return 0; return 1; } int md_parse_option (int c, char *arg) { switch (c) { case 'u': /* -u means that any undefined symbols should be treated as external, which is the default for gas anyhow. */ break; #ifdef OBJ_ELF case 'l': /* Solaris as takes -le (presumably for little endian). For completeness sake, recognize -be also. */ if (strcmp (arg, "e") == 0) { target_big_endian = 0; set_target_endian = 1; } else return 0; break; case 'b': if (strcmp (arg, "e") == 0) { target_big_endian = 1; set_target_endian = 1; } else return 0; break; case 'K': /* Recognize -K PIC. */ if (strcmp (arg, "PIC") == 0 || strcmp (arg, "pic") == 0) { shlib = SHLIB_PIC; ppc_flags |= EF_PPC_RELOCATABLE_LIB; } else return 0; break; #endif /* a64 and a32 determine whether to use XCOFF64 or XCOFF32. */ case 'a': if (strcmp (arg, "64") == 0) { #ifdef BFD64 ppc_obj64 = 1; #else as_fatal (_("%s unsupported"), "-a64"); #endif } else if (strcmp (arg, "32") == 0) ppc_obj64 = 0; else return 0; break; case 'm': if (parse_cpu (arg)) ; else if (strcmp (arg, "regnames") == 0) reg_names_p = TRUE; else if (strcmp (arg, "no-regnames") == 0) reg_names_p = FALSE; #ifdef OBJ_ELF /* -mrelocatable/-mrelocatable-lib -- warn about initializations that require relocation. */ else if (strcmp (arg, "relocatable") == 0) { shlib = SHLIB_MRELOCATABLE; ppc_flags |= EF_PPC_RELOCATABLE; } else if (strcmp (arg, "relocatable-lib") == 0) { shlib = SHLIB_MRELOCATABLE; ppc_flags |= EF_PPC_RELOCATABLE_LIB; } /* -memb, set embedded bit. */ else if (strcmp (arg, "emb") == 0) ppc_flags |= EF_PPC_EMB; /* -mlittle/-mbig set the endianess. */ else if (strcmp (arg, "little") == 0 || strcmp (arg, "little-endian") == 0) { target_big_endian = 0; set_target_endian = 1; } else if (strcmp (arg, "big") == 0 || strcmp (arg, "big-endian") == 0) { target_big_endian = 1; set_target_endian = 1; } else if (strcmp (arg, "solaris") == 0) { msolaris = TRUE; ppc_comment_chars = ppc_solaris_comment_chars; } else if (strcmp (arg, "no-solaris") == 0) { msolaris = FALSE; ppc_comment_chars = ppc_eabi_comment_chars; } #endif else { as_bad (_("invalid switch -m%s"), arg); return 0; } break; #ifdef OBJ_ELF /* -V: SVR4 argument to print version ID. */ case 'V': print_version_id (); break; /* -Qy, -Qn: SVR4 arguments controlling whether a .comment section should be emitted or not. FIXME: Not implemented. */ case 'Q': break; /* Solaris takes -s to specify that .stabs go in a .stabs section, rather than .stabs.excl, which is ignored by the linker. FIXME: Not implemented. */ case 's': if (arg) return 0; break; #endif default: return 0; } return 1; } void md_show_usage (FILE *stream) { fprintf (stream, _("\ PowerPC options:\n\ -a32 generate ELF32/XCOFF32\n\ -a64 generate ELF64/XCOFF64\n\ -u ignored\n\ -mpwrx, -mpwr2 generate code for POWER/2 (RIOS2)\n\ -mpwr generate code for POWER (RIOS1)\n\ -m601 generate code for PowerPC 601\n\ -mppc, -mppc32, -m603, -m604\n\ generate code for PowerPC 603/604\n\ -m403, -m405 generate code for PowerPC 403/405\n\ -m440 generate code for PowerPC 440\n\ -m7400, -m7410, -m7450, -m7455\n\ generate code For PowerPC 7400/7410/7450/7455\n")); fprintf (stream, _("\ -mppc64, -m620 generate code for PowerPC 620/625/630\n\ -mppc64bridge generate code for PowerPC 64, including bridge insns\n\ -mbooke64 generate code for 64-bit PowerPC BookE\n\ -mbooke, mbooke32 generate code for 32-bit PowerPC BookE\n\ -mpower4 generate code for Power4 architecture\n\ -mpower5 generate code for Power5 architecture\n\ -mpower6 generate code for Power6 architecture\n\ -mcell generate code for Cell Broadband Engine architecture\n\ -mcom generate code Power/PowerPC common instructions\n\ -many generate code for any architecture (PWR/PWRX/PPC)\n")); fprintf (stream, _("\ -maltivec generate code for AltiVec\n\ -me300 generate code for PowerPC e300 family\n\ -me500, -me500x2 generate code for Motorola e500 core complex\n\ -mspe generate code for Motorola SPE instructions\n\ -mregnames Allow symbolic names for registers\n\ -mno-regnames Do not allow symbolic names for registers\n")); #ifdef OBJ_ELF fprintf (stream, _("\ -mrelocatable support for GCC's -mrelocatble option\n\ -mrelocatable-lib support for GCC's -mrelocatble-lib option\n\ -memb set PPC_EMB bit in ELF flags\n\ -mlittle, -mlittle-endian, -l, -le\n\ generate code for a little endian machine\n\ -mbig, -mbig-endian, -b, -be\n\ generate code for a big endian machine\n\ -msolaris generate code for Solaris\n\ -mno-solaris do not generate code for Solaris\n\ -V print assembler version number\n\ -Qy, -Qn ignored\n")); #endif } /* Set ppc_cpu if it is not already set. */ static void ppc_set_cpu (void) { const char *default_os = TARGET_OS; const char *default_cpu = TARGET_CPU; if ((ppc_cpu & ~PPC_OPCODE_ANY) == 0) { if (ppc_obj64) ppc_cpu |= PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC | PPC_OPCODE_64; else if (strncmp (default_os, "aix", 3) == 0 && default_os[3] >= '4' && default_os[3] <= '9') ppc_cpu |= PPC_OPCODE_COMMON | PPC_OPCODE_32; else if (strncmp (default_os, "aix3", 4) == 0) ppc_cpu |= PPC_OPCODE_POWER | PPC_OPCODE_32; else if (strcmp (default_cpu, "rs6000") == 0) ppc_cpu |= PPC_OPCODE_POWER | PPC_OPCODE_32; else if (strncmp (default_cpu, "powerpc", 7) == 0) ppc_cpu |= PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC | PPC_OPCODE_32; else as_fatal (_("Unknown default cpu = %s, os = %s"), default_cpu, default_os); } } /* Figure out the BFD architecture to use. This function and ppc_mach are called well before md_begin, when the output file is opened. */ enum bfd_architecture ppc_arch (void) { const char *default_cpu = TARGET_CPU; ppc_set_cpu (); if ((ppc_cpu & PPC_OPCODE_PPC) != 0) return bfd_arch_powerpc; else if ((ppc_cpu & PPC_OPCODE_POWER) != 0) return bfd_arch_rs6000; else if ((ppc_cpu & (PPC_OPCODE_COMMON | PPC_OPCODE_ANY)) != 0) { if (strcmp (default_cpu, "rs6000") == 0) return bfd_arch_rs6000; else if (strncmp (default_cpu, "powerpc", 7) == 0) return bfd_arch_powerpc; } as_fatal (_("Neither Power nor PowerPC opcodes were selected.")); return bfd_arch_unknown; } unsigned long ppc_mach (void) { if (ppc_obj64) return bfd_mach_ppc64; else if (ppc_arch () == bfd_arch_rs6000) return bfd_mach_rs6k; else return bfd_mach_ppc; } extern char* ppc_target_format (void) { #ifdef OBJ_COFF #ifdef TE_PE return target_big_endian ? "pe-powerpc" : "pe-powerpcle"; #elif TE_POWERMAC return "xcoff-powermac"; #else # ifdef TE_AIX5 return (ppc_obj64 ? "aix5coff64-rs6000" : "aixcoff-rs6000"); # else return (ppc_obj64 ? "aixcoff64-rs6000" : "aixcoff-rs6000"); # endif #endif #endif #ifdef OBJ_ELF # ifdef TE_VXWORKS return "elf32-powerpc-vxworks"; # else return (target_big_endian ? (ppc_obj64 ? "elf64-powerpc-freebsd" : "elf32-powerpc-freebsd") : (ppc_obj64 ? "elf64-powerpcle" : "elf32-powerpcle")); # endif #endif } /* Insert opcodes and macros into hash tables. Called at startup and for .cpu pseudo. */ static void ppc_setup_opcodes (void) { const struct powerpc_opcode *op; const struct powerpc_opcode *op_end; const struct powerpc_macro *macro; const struct powerpc_macro *macro_end; bfd_boolean bad_insn = FALSE; if (ppc_hash != NULL) hash_die (ppc_hash); if (ppc_macro_hash != NULL) hash_die (ppc_macro_hash); /* Insert the opcodes into a hash table. */ ppc_hash = hash_new (); if (ENABLE_CHECKING) { unsigned int i; /* Check operand masks. Code here and in the disassembler assumes all the 1's in the mask are contiguous. */ for (i = 0; i < num_powerpc_operands; ++i) { unsigned long mask = powerpc_operands[i].bitm; unsigned long right_bit; unsigned int j; right_bit = mask & -mask; mask += right_bit; right_bit = mask & -mask; if (mask != right_bit) { as_bad (_("powerpc_operands[%d].bitm invalid"), i); bad_insn = TRUE; } for (j = i + 1; j < num_powerpc_operands; ++j) if (memcmp (&powerpc_operands[i], &powerpc_operands[j], sizeof (powerpc_operands[0])) == 0) { as_bad (_("powerpc_operands[%d] duplicates powerpc_operands[%d]"), j, i); bad_insn = TRUE; } } } op_end = powerpc_opcodes + powerpc_num_opcodes; for (op = powerpc_opcodes; op < op_end; op++) { if (ENABLE_CHECKING) { const unsigned char *o; unsigned long omask = op->mask; /* The mask had better not trim off opcode bits. */ if ((op->opcode & omask) != op->opcode) { as_bad (_("mask trims opcode bits for %s"), op->name); bad_insn = TRUE; } /* The operands must not overlap the opcode or each other. */ for (o = op->operands; *o; ++o) if (*o >= num_powerpc_operands) { as_bad (_("operand index error for %s"), op->name); bad_insn = TRUE; } else { const struct powerpc_operand *operand = &powerpc_operands[*o]; if (operand->shift >= 0) { unsigned long mask = operand->bitm << operand->shift; if (omask & mask) { as_bad (_("operand %d overlap in %s"), (int) (o - op->operands), op->name); bad_insn = TRUE; } omask |= mask; } } } if ((op->flags & ppc_cpu & ~(PPC_OPCODE_32 | PPC_OPCODE_64)) != 0 && ((op->flags & (PPC_OPCODE_32 | PPC_OPCODE_64)) == 0 || ((op->flags & (PPC_OPCODE_32 | PPC_OPCODE_64)) == (ppc_cpu & (PPC_OPCODE_32 | PPC_OPCODE_64))) || (ppc_cpu & PPC_OPCODE_64_BRIDGE) != 0) /* Certain instructions (eg: extsw) do not exist in the 32-bit BookE instruction set, but they do exist in the 64-bit BookE instruction set, and other PPC instruction sets. Check to see if the opcode has the BOOKE64 flag set. If it does make sure that the target CPU is not the BookE32. */ && ((op->flags & PPC_OPCODE_BOOKE64) == 0 || (ppc_cpu & PPC_OPCODE_BOOKE64) == PPC_OPCODE_BOOKE64 || (ppc_cpu & PPC_OPCODE_BOOKE) == 0) && ((op->flags & (PPC_OPCODE_POWER4 | PPC_OPCODE_NOPOWER4)) == 0 || ((op->flags & PPC_OPCODE_POWER4) == (ppc_cpu & PPC_OPCODE_POWER4))) && ((op->flags & PPC_OPCODE_POWER5) == 0 || ((op->flags & PPC_OPCODE_POWER5) == (ppc_cpu & PPC_OPCODE_POWER5))) && ((op->flags & PPC_OPCODE_POWER6) == 0 || ((op->flags & PPC_OPCODE_POWER6) == (ppc_cpu & PPC_OPCODE_POWER6)))) { const char *retval; retval = hash_insert (ppc_hash, op->name, (void *) op); if (retval != NULL) { /* Ignore Power duplicates for -m601. */ if ((ppc_cpu & PPC_OPCODE_601) != 0 && (op->flags & PPC_OPCODE_POWER) != 0) continue; as_bad (_("duplicate instruction %s"), op->name); bad_insn = TRUE; } } } if ((ppc_cpu & PPC_OPCODE_ANY) != 0) for (op = powerpc_opcodes; op < op_end; op++) hash_insert (ppc_hash, op->name, (void *) op); /* Insert the macros into a hash table. */ ppc_macro_hash = hash_new (); macro_end = powerpc_macros + powerpc_num_macros; for (macro = powerpc_macros; macro < macro_end; macro++) { if ((macro->flags & ppc_cpu) != 0) { const char *retval; retval = hash_insert (ppc_macro_hash, macro->name, (void *) macro); if (retval != (const char *) NULL) { as_bad (_("duplicate macro %s"), macro->name); bad_insn = TRUE; } } } if (bad_insn) abort (); } /* This function is called when the assembler starts up. It is called after the options have been parsed and the output file has been opened. */ void md_begin (void) { ppc_set_cpu (); ppc_cie_data_alignment = ppc_obj64 ? -8 : -4; #ifdef OBJ_ELF /* Set the ELF flags if desired. */ if (ppc_flags && !msolaris) bfd_set_private_flags (stdoutput, ppc_flags); #endif ppc_setup_opcodes (); /* Tell the main code what the endianness is if it is not overridden by the user. */ if (!set_target_endian) { set_target_endian = 1; target_big_endian = PPC_BIG_ENDIAN; } #ifdef OBJ_XCOFF ppc_coff_debug_section = coff_section_from_bfd_index (stdoutput, N_DEBUG); /* Create dummy symbols to serve as initial csects. This forces the text csects to precede the data csects. These symbols will not be output. */ ppc_text_csects = symbol_make ("dummy\001"); symbol_get_tc (ppc_text_csects)->within = ppc_text_csects; ppc_data_csects = symbol_make ("dummy\001"); symbol_get_tc (ppc_data_csects)->within = ppc_data_csects; #endif #ifdef TE_PE ppc_current_section = text_section; ppc_previous_section = 0; #endif } void ppc_cleanup (void) { #ifdef OBJ_ELF if (ppc_apuinfo_list == NULL) return; /* Ok, so write the section info out. We have this layout: byte data what ---- ---- ---- 0 8 length of "APUinfo\0" 4 (n*4) number of APU's (4 bytes each) 8 2 note type 2 12 "APUinfo\0" name 20 APU#1 first APU's info 24 APU#2 second APU's info ... ... */ { char *p; asection *seg = now_seg; subsegT subseg = now_subseg; asection *apuinfo_secp = (asection *) NULL; unsigned int i; /* Create the .PPC.EMB.apuinfo section. */ apuinfo_secp = subseg_new (".PPC.EMB.apuinfo", 0); bfd_set_section_flags (stdoutput, apuinfo_secp, SEC_HAS_CONTENTS | SEC_READONLY); p = frag_more (4); md_number_to_chars (p, (valueT) 8, 4); p = frag_more (4); md_number_to_chars (p, (valueT) ppc_apuinfo_num * 4, 4); p = frag_more (4); md_number_to_chars (p, (valueT) 2, 4); p = frag_more (8); strcpy (p, "APUinfo"); for (i = 0; i < ppc_apuinfo_num; i++) { p = frag_more (4); md_number_to_chars (p, (valueT) ppc_apuinfo_list[i], 4); } frag_align (2, 0, 0); /* We probably can't restore the current segment, for there likely isn't one yet... */ if (seg && subseg) subseg_set (seg, subseg); } #endif } /* Insert an operand value into an instruction. */ static unsigned long ppc_insert_operand (unsigned long insn, const struct powerpc_operand *operand, offsetT val, char *file, unsigned int line) { long min, max, right; max = operand->bitm; right = max & -max; min = 0; if ((operand->flags & PPC_OPERAND_SIGNED) != 0) { if ((operand->flags & PPC_OPERAND_SIGNOPT) == 0) max = (max >> 1) & -right; min = ~max & -right; } if ((operand->flags & PPC_OPERAND_PLUS1) != 0) max++; if ((operand->flags & PPC_OPERAND_NEGATIVE) != 0) { long tmp = min; min = -max; max = -tmp; } if (min <= max) { /* Some people write constants with the sign extension done by hand but only up to 32 bits. This shouldn't really be valid, but, to permit this code to assemble on a 64-bit host, we sign extend the 32-bit value to 64 bits if so doing makes the value valid. */ if (val > max && (offsetT) (val - 0x80000000 - 0x80000000) >= min && (offsetT) (val - 0x80000000 - 0x80000000) <= max && ((val - 0x80000000 - 0x80000000) & (right - 1)) == 0) val = val - 0x80000000 - 0x80000000; /* Similarly, people write expressions like ~(1<<15), and expect this to be OK for a 32-bit unsigned value. */ else if (val < min && (offsetT) (val + 0x80000000 + 0x80000000) >= min && (offsetT) (val + 0x80000000 + 0x80000000) <= max && ((val + 0x80000000 + 0x80000000) & (right - 1)) == 0) val = val + 0x80000000 + 0x80000000; else if (val < min || val > max || (val & (right - 1)) != 0) as_bad_value_out_of_range (_("operand"), val, min, max, file, line); } if (operand->insert) { const char *errmsg; errmsg = NULL; insn = (*operand->insert) (insn, (long) val, ppc_cpu, &errmsg); if (errmsg != (const char *) NULL) - as_bad_where (file, line, errmsg); + as_bad_where (file, line, "%s", errmsg); } else insn |= ((long) val & operand->bitm) << operand->shift; return insn; } #ifdef OBJ_ELF /* Parse @got, etc. and return the desired relocation. */ static bfd_reloc_code_real_type ppc_elf_suffix (char **str_p, expressionS *exp_p) { struct map_bfd { char *string; unsigned int length : 8; unsigned int valid32 : 1; unsigned int valid64 : 1; unsigned int reloc; }; char ident[20]; char *str = *str_p; char *str2; int ch; int len; const struct map_bfd *ptr; #define MAP(str, reloc) { str, sizeof (str) - 1, 1, 1, reloc } #define MAP32(str, reloc) { str, sizeof (str) - 1, 1, 0, reloc } #define MAP64(str, reloc) { str, sizeof (str) - 1, 0, 1, reloc } static const struct map_bfd mapping[] = { MAP ("l", BFD_RELOC_LO16), MAP ("h", BFD_RELOC_HI16), MAP ("ha", BFD_RELOC_HI16_S), MAP ("brtaken", BFD_RELOC_PPC_B16_BRTAKEN), MAP ("brntaken", BFD_RELOC_PPC_B16_BRNTAKEN), MAP ("got", BFD_RELOC_16_GOTOFF), MAP ("got@l", BFD_RELOC_LO16_GOTOFF), MAP ("got@h", BFD_RELOC_HI16_GOTOFF), MAP ("got@ha", BFD_RELOC_HI16_S_GOTOFF), MAP ("plt@l", BFD_RELOC_LO16_PLTOFF), MAP ("plt@h", BFD_RELOC_HI16_PLTOFF), MAP ("plt@ha", BFD_RELOC_HI16_S_PLTOFF), MAP ("copy", BFD_RELOC_PPC_COPY), MAP ("globdat", BFD_RELOC_PPC_GLOB_DAT), MAP ("sectoff", BFD_RELOC_16_BASEREL), MAP ("sectoff@l", BFD_RELOC_LO16_BASEREL), MAP ("sectoff@h", BFD_RELOC_HI16_BASEREL), MAP ("sectoff@ha", BFD_RELOC_HI16_S_BASEREL), MAP ("tls", BFD_RELOC_PPC_TLS), MAP ("dtpmod", BFD_RELOC_PPC_DTPMOD), MAP ("dtprel", BFD_RELOC_PPC_DTPREL), MAP ("dtprel@l", BFD_RELOC_PPC_DTPREL16_LO), MAP ("dtprel@h", BFD_RELOC_PPC_DTPREL16_HI), MAP ("dtprel@ha", BFD_RELOC_PPC_DTPREL16_HA), MAP ("tprel", BFD_RELOC_PPC_TPREL), MAP ("tprel@l", BFD_RELOC_PPC_TPREL16_LO), MAP ("tprel@h", BFD_RELOC_PPC_TPREL16_HI), MAP ("tprel@ha", BFD_RELOC_PPC_TPREL16_HA), MAP ("got@tlsgd", BFD_RELOC_PPC_GOT_TLSGD16), MAP ("got@tlsgd@l", BFD_RELOC_PPC_GOT_TLSGD16_LO), MAP ("got@tlsgd@h", BFD_RELOC_PPC_GOT_TLSGD16_HI), MAP ("got@tlsgd@ha", BFD_RELOC_PPC_GOT_TLSGD16_HA), MAP ("got@tlsld", BFD_RELOC_PPC_GOT_TLSLD16), MAP ("got@tlsld@l", BFD_RELOC_PPC_GOT_TLSLD16_LO), MAP ("got@tlsld@h", BFD_RELOC_PPC_GOT_TLSLD16_HI), MAP ("got@tlsld@ha", BFD_RELOC_PPC_GOT_TLSLD16_HA), MAP ("got@dtprel", BFD_RELOC_PPC_GOT_DTPREL16), MAP ("got@dtprel@l", BFD_RELOC_PPC_GOT_DTPREL16_LO), MAP ("got@dtprel@h", BFD_RELOC_PPC_GOT_DTPREL16_HI), MAP ("got@dtprel@ha", BFD_RELOC_PPC_GOT_DTPREL16_HA), MAP ("got@tprel", BFD_RELOC_PPC_GOT_TPREL16), MAP ("got@tprel@l", BFD_RELOC_PPC_GOT_TPREL16_LO), MAP ("got@tprel@h", BFD_RELOC_PPC_GOT_TPREL16_HI), MAP ("got@tprel@ha", BFD_RELOC_PPC_GOT_TPREL16_HA), MAP32 ("fixup", BFD_RELOC_CTOR), MAP32 ("plt", BFD_RELOC_24_PLT_PCREL), MAP32 ("pltrel24", BFD_RELOC_24_PLT_PCREL), MAP32 ("local24pc", BFD_RELOC_PPC_LOCAL24PC), MAP32 ("local", BFD_RELOC_PPC_LOCAL24PC), MAP32 ("pltrel", BFD_RELOC_32_PLT_PCREL), MAP32 ("sdarel", BFD_RELOC_GPREL16), MAP32 ("naddr", BFD_RELOC_PPC_EMB_NADDR32), MAP32 ("naddr16", BFD_RELOC_PPC_EMB_NADDR16), MAP32 ("naddr@l", BFD_RELOC_PPC_EMB_NADDR16_LO), MAP32 ("naddr@h", BFD_RELOC_PPC_EMB_NADDR16_HI), MAP32 ("naddr@ha", BFD_RELOC_PPC_EMB_NADDR16_HA), MAP32 ("sdai16", BFD_RELOC_PPC_EMB_SDAI16), MAP32 ("sda2rel", BFD_RELOC_PPC_EMB_SDA2REL), MAP32 ("sda2i16", BFD_RELOC_PPC_EMB_SDA2I16), MAP32 ("sda21", BFD_RELOC_PPC_EMB_SDA21), MAP32 ("mrkref", BFD_RELOC_PPC_EMB_MRKREF), MAP32 ("relsect", BFD_RELOC_PPC_EMB_RELSEC16), MAP32 ("relsect@l", BFD_RELOC_PPC_EMB_RELST_LO), MAP32 ("relsect@h", BFD_RELOC_PPC_EMB_RELST_HI), MAP32 ("relsect@ha", BFD_RELOC_PPC_EMB_RELST_HA), MAP32 ("bitfld", BFD_RELOC_PPC_EMB_BIT_FLD), MAP32 ("relsda", BFD_RELOC_PPC_EMB_RELSDA), MAP32 ("xgot", BFD_RELOC_PPC_TOC16), MAP64 ("higher", BFD_RELOC_PPC64_HIGHER), MAP64 ("highera", BFD_RELOC_PPC64_HIGHER_S), MAP64 ("highest", BFD_RELOC_PPC64_HIGHEST), MAP64 ("highesta", BFD_RELOC_PPC64_HIGHEST_S), MAP64 ("tocbase", BFD_RELOC_PPC64_TOC), MAP64 ("toc", BFD_RELOC_PPC_TOC16), MAP64 ("toc@l", BFD_RELOC_PPC64_TOC16_LO), MAP64 ("toc@h", BFD_RELOC_PPC64_TOC16_HI), MAP64 ("toc@ha", BFD_RELOC_PPC64_TOC16_HA), MAP64 ("dtprel@higher", BFD_RELOC_PPC64_DTPREL16_HIGHER), MAP64 ("dtprel@highera", BFD_RELOC_PPC64_DTPREL16_HIGHERA), MAP64 ("dtprel@highest", BFD_RELOC_PPC64_DTPREL16_HIGHEST), MAP64 ("dtprel@highesta", BFD_RELOC_PPC64_DTPREL16_HIGHESTA), MAP64 ("tprel@higher", BFD_RELOC_PPC64_TPREL16_HIGHER), MAP64 ("tprel@highera", BFD_RELOC_PPC64_TPREL16_HIGHERA), MAP64 ("tprel@highest", BFD_RELOC_PPC64_TPREL16_HIGHEST), MAP64 ("tprel@highesta", BFD_RELOC_PPC64_TPREL16_HIGHESTA), { (char *) 0, 0, 0, 0, BFD_RELOC_UNUSED } }; if (*str++ != '@') return BFD_RELOC_UNUSED; for (ch = *str, str2 = ident; (str2 < ident + sizeof (ident) - 1 && (ISALNUM (ch) || ch == '@')); ch = *++str) { *str2++ = TOLOWER (ch); } *str2 = '\0'; len = str2 - ident; ch = ident[0]; for (ptr = &mapping[0]; ptr->length > 0; ptr++) if (ch == ptr->string[0] && len == ptr->length && memcmp (ident, ptr->string, ptr->length) == 0 && (ppc_obj64 ? ptr->valid64 : ptr->valid32)) { int reloc = ptr->reloc; if (!ppc_obj64) if (exp_p->X_add_number != 0 && (reloc == (int) BFD_RELOC_16_GOTOFF || reloc == (int) BFD_RELOC_LO16_GOTOFF || reloc == (int) BFD_RELOC_HI16_GOTOFF || reloc == (int) BFD_RELOC_HI16_S_GOTOFF)) as_warn (_("identifier+constant@got means identifier@got+constant")); /* Now check for identifier@suffix+constant. */ if (*str == '-' || *str == '+') { char *orig_line = input_line_pointer; expressionS new_exp; input_line_pointer = str; expression (&new_exp); if (new_exp.X_op == O_constant) { exp_p->X_add_number += new_exp.X_add_number; str = input_line_pointer; } if (&input_line_pointer != str_p) input_line_pointer = orig_line; } *str_p = str; if (reloc == (int) BFD_RELOC_PPC64_TOC && exp_p->X_op == O_symbol && strcmp (S_GET_NAME (exp_p->X_add_symbol), ".TOC.") == 0) { /* Change the symbol so that the dummy .TOC. symbol can be omitted from the object file. */ exp_p->X_add_symbol = &abs_symbol; } return (bfd_reloc_code_real_type) reloc; } return BFD_RELOC_UNUSED; } /* Like normal .long/.short/.word, except support @got, etc. Clobbers input_line_pointer, checks end-of-line. */ static void ppc_elf_cons (int nbytes /* 1=.byte, 2=.word, 4=.long, 8=.llong */) { expressionS exp; bfd_reloc_code_real_type reloc; if (is_it_end_of_statement ()) { demand_empty_rest_of_line (); return; } do { expression (&exp); if (exp.X_op == O_symbol && *input_line_pointer == '@' && (reloc = ppc_elf_suffix (&input_line_pointer, &exp)) != BFD_RELOC_UNUSED) { reloc_howto_type *reloc_howto; int size; reloc_howto = bfd_reloc_type_lookup (stdoutput, reloc); size = bfd_get_reloc_size (reloc_howto); if (size > nbytes) { as_bad (_("%s relocations do not fit in %d bytes\n"), reloc_howto->name, nbytes); } else { char *p; int offset; p = frag_more (nbytes); offset = 0; if (target_big_endian) offset = nbytes - size; fix_new_exp (frag_now, p - frag_now->fr_literal + offset, size, &exp, 0, reloc); } } else emit_expr (&exp, (unsigned int) nbytes); } while (*input_line_pointer++ == ','); /* Put terminator back into stream. */ input_line_pointer--; demand_empty_rest_of_line (); } /* Solaris pseduo op to change to the .rodata section. */ static void ppc_elf_rdata (int xxx) { char *save_line = input_line_pointer; static char section[] = ".rodata\n"; /* Just pretend this is .section .rodata */ input_line_pointer = section; obj_elf_section (xxx); input_line_pointer = save_line; } /* Pseudo op to make file scope bss items. */ static void ppc_elf_lcomm (int xxx ATTRIBUTE_UNUSED) { char *name; char c; char *p; offsetT size; symbolS *symbolP; offsetT align; segT old_sec; int old_subsec; char *pfrag; int align2; name = input_line_pointer; c = get_symbol_end (); /* just after name is now '\0'. */ p = input_line_pointer; *p = c; SKIP_WHITESPACE (); if (*input_line_pointer != ',') { as_bad (_("Expected comma after symbol-name: rest of line ignored.")); ignore_rest_of_line (); return; } input_line_pointer++; /* skip ',' */ if ((size = get_absolute_expression ()) < 0) { as_warn (_(".COMMon length (%ld.) <0! Ignored."), (long) size); ignore_rest_of_line (); return; } /* The third argument to .lcomm is the alignment. */ if (*input_line_pointer != ',') align = 8; else { ++input_line_pointer; align = get_absolute_expression (); if (align <= 0) { as_warn (_("ignoring bad alignment")); align = 8; } } *p = 0; symbolP = symbol_find_or_make (name); *p = c; if (S_IS_DEFINED (symbolP) && ! S_IS_COMMON (symbolP)) { as_bad (_("Ignoring attempt to re-define symbol `%s'."), S_GET_NAME (symbolP)); ignore_rest_of_line (); return; } if (S_GET_VALUE (symbolP) && S_GET_VALUE (symbolP) != (valueT) size) { as_bad (_("Length of .lcomm \"%s\" is already %ld. Not changed to %ld."), S_GET_NAME (symbolP), (long) S_GET_VALUE (symbolP), (long) size); ignore_rest_of_line (); return; } /* Allocate_bss. */ old_sec = now_seg; old_subsec = now_subseg; if (align) { /* Convert to a power of 2 alignment. */ for (align2 = 0; (align & 1) == 0; align >>= 1, ++align2); if (align != 1) { as_bad (_("Common alignment not a power of 2")); ignore_rest_of_line (); return; } } else align2 = 0; record_alignment (bss_section, align2); subseg_set (bss_section, 0); if (align2) frag_align (align2, 0, 0); if (S_GET_SEGMENT (symbolP) == bss_section) symbol_get_frag (symbolP)->fr_symbol = 0; symbol_set_frag (symbolP, frag_now); pfrag = frag_var (rs_org, 1, 1, (relax_substateT) 0, symbolP, size, (char *) 0); *pfrag = 0; S_SET_SIZE (symbolP, size); S_SET_SEGMENT (symbolP, bss_section); subseg_set (old_sec, old_subsec); demand_empty_rest_of_line (); } /* Validate any relocations emitted for -mrelocatable, possibly adding fixups for word relocations in writable segments, so we can adjust them at runtime. */ static void ppc_elf_validate_fix (fixS *fixp, segT seg) { if (fixp->fx_done || fixp->fx_pcrel) return; switch (shlib) { case SHLIB_NONE: case SHLIB_PIC: return; case SHLIB_MRELOCATABLE: if (fixp->fx_r_type <= BFD_RELOC_UNUSED && fixp->fx_r_type != BFD_RELOC_16_GOTOFF && fixp->fx_r_type != BFD_RELOC_HI16_GOTOFF && fixp->fx_r_type != BFD_RELOC_LO16_GOTOFF && fixp->fx_r_type != BFD_RELOC_HI16_S_GOTOFF && fixp->fx_r_type != BFD_RELOC_16_BASEREL && fixp->fx_r_type != BFD_RELOC_LO16_BASEREL && fixp->fx_r_type != BFD_RELOC_HI16_BASEREL && fixp->fx_r_type != BFD_RELOC_HI16_S_BASEREL && (seg->flags & SEC_LOAD) != 0 && strcmp (segment_name (seg), ".got2") != 0 && strcmp (segment_name (seg), ".dtors") != 0 && strcmp (segment_name (seg), ".ctors") != 0 && strcmp (segment_name (seg), ".fixup") != 0 && strcmp (segment_name (seg), ".gcc_except_table") != 0 && strcmp (segment_name (seg), ".eh_frame") != 0 && strcmp (segment_name (seg), ".ex_shared") != 0) { if ((seg->flags & (SEC_READONLY | SEC_CODE)) != 0 || fixp->fx_r_type != BFD_RELOC_CTOR) { as_bad_where (fixp->fx_file, fixp->fx_line, _("Relocation cannot be done when using -mrelocatable")); } } return; } } /* Prevent elf_frob_file_before_adjust removing a weak undefined function descriptor sym if the corresponding code sym is used. */ void ppc_frob_file_before_adjust (void) { symbolS *symp; asection *toc; if (!ppc_obj64) return; for (symp = symbol_rootP; symp; symp = symbol_next (symp)) { const char *name; char *dotname; symbolS *dotsym; size_t len; name = S_GET_NAME (symp); if (name[0] == '.') continue; if (! S_IS_WEAK (symp) || S_IS_DEFINED (symp)) continue; len = strlen (name) + 1; dotname = xmalloc (len + 1); dotname[0] = '.'; memcpy (dotname + 1, name, len); dotsym = symbol_find_noref (dotname, 1); free (dotname); if (dotsym != NULL && (symbol_used_p (dotsym) || symbol_used_in_reloc_p (dotsym))) symbol_mark_used (symp); } toc = bfd_get_section_by_name (stdoutput, ".toc"); if (toc != NULL && bfd_section_size (stdoutput, toc) > 0x10000) as_warn (_("TOC section size exceeds 64k")); /* Don't emit .TOC. symbol. */ symp = symbol_find (".TOC."); if (symp != NULL) symbol_remove (symp, &symbol_rootP, &symbol_lastP); } #endif /* OBJ_ELF */ #ifdef TE_PE /* * Summary of parse_toc_entry. * * in: Input_line_pointer points to the '[' in one of: * * [toc] [tocv] [toc32] [toc64] * * Anything else is an error of one kind or another. * * out: * return value: success or failure * toc_kind: kind of toc reference * input_line_pointer: * success: first char after the ']' * failure: unchanged * * settings: * * [toc] - rv == success, toc_kind = default_toc * [tocv] - rv == success, toc_kind = data_in_toc * [toc32] - rv == success, toc_kind = must_be_32 * [toc64] - rv == success, toc_kind = must_be_64 * */ enum toc_size_qualifier { default_toc, /* The toc cell constructed should be the system default size */ data_in_toc, /* This is a direct reference to a toc cell */ must_be_32, /* The toc cell constructed must be 32 bits wide */ must_be_64 /* The toc cell constructed must be 64 bits wide */ }; static int parse_toc_entry (enum toc_size_qualifier *toc_kind) { char *start; char *toc_spec; char c; enum toc_size_qualifier t; /* Save the input_line_pointer. */ start = input_line_pointer; /* Skip over the '[' , and whitespace. */ ++input_line_pointer; SKIP_WHITESPACE (); /* Find the spelling of the operand. */ toc_spec = input_line_pointer; c = get_symbol_end (); if (strcmp (toc_spec, "toc") == 0) { t = default_toc; } else if (strcmp (toc_spec, "tocv") == 0) { t = data_in_toc; } else if (strcmp (toc_spec, "toc32") == 0) { t = must_be_32; } else if (strcmp (toc_spec, "toc64") == 0) { t = must_be_64; } else { as_bad (_("syntax error: invalid toc specifier `%s'"), toc_spec); *input_line_pointer = c; input_line_pointer = start; return 0; } /* Now find the ']'. */ *input_line_pointer = c; SKIP_WHITESPACE (); /* leading whitespace could be there. */ c = *input_line_pointer++; /* input_line_pointer->past char in c. */ if (c != ']') { as_bad (_("syntax error: expected `]', found `%c'"), c); input_line_pointer = start; return 0; } *toc_kind = t; return 1; } #endif #ifdef OBJ_ELF #define APUID(a,v) ((((a) & 0xffff) << 16) | ((v) & 0xffff)) static void ppc_apuinfo_section_add (unsigned int apu, unsigned int version) { unsigned int i; /* Check we don't already exist. */ for (i = 0; i < ppc_apuinfo_num; i++) if (ppc_apuinfo_list[i] == APUID (apu, version)) return; if (ppc_apuinfo_num == ppc_apuinfo_num_alloc) { if (ppc_apuinfo_num_alloc == 0) { ppc_apuinfo_num_alloc = 4; ppc_apuinfo_list = (unsigned long *) xmalloc (sizeof (unsigned long) * ppc_apuinfo_num_alloc); } else { ppc_apuinfo_num_alloc += 4; ppc_apuinfo_list = (unsigned long *) xrealloc (ppc_apuinfo_list, sizeof (unsigned long) * ppc_apuinfo_num_alloc); } } ppc_apuinfo_list[ppc_apuinfo_num++] = APUID (apu, version); } #undef APUID #endif /* We need to keep a list of fixups. We can't simply generate them as we go, because that would require us to first create the frag, and that would screw up references to ``.''. */ struct ppc_fixup { expressionS exp; int opindex; bfd_reloc_code_real_type reloc; }; #define MAX_INSN_FIXUPS (5) /* This routine is called for each instruction to be assembled. */ void md_assemble (char *str) { char *s; const struct powerpc_opcode *opcode; unsigned long insn; const unsigned char *opindex_ptr; int skip_optional; int need_paren; int next_opindex; struct ppc_fixup fixups[MAX_INSN_FIXUPS]; int fc; char *f; int addr_mod; int i; #ifdef OBJ_ELF bfd_reloc_code_real_type reloc; #endif /* Get the opcode. */ for (s = str; *s != '\0' && ! ISSPACE (*s); s++) ; if (*s != '\0') *s++ = '\0'; /* Look up the opcode in the hash table. */ opcode = (const struct powerpc_opcode *) hash_find (ppc_hash, str); if (opcode == (const struct powerpc_opcode *) NULL) { const struct powerpc_macro *macro; macro = (const struct powerpc_macro *) hash_find (ppc_macro_hash, str); if (macro == (const struct powerpc_macro *) NULL) as_bad (_("Unrecognized opcode: `%s'"), str); else ppc_macro (s, macro); return; } insn = opcode->opcode; str = s; while (ISSPACE (*str)) ++str; /* PowerPC operands are just expressions. The only real issue is that a few operand types are optional. All cases which might use an optional operand separate the operands only with commas (in some cases parentheses are used, as in ``lwz 1,0(1)'' but such cases never have optional operands). Most instructions with optional operands have only one. Those that have more than one optional operand can take either all their operands or none. So, before we start seriously parsing the operands, we check to see if we have optional operands, and if we do, we count the number of commas to see which operands have been omitted. */ skip_optional = 0; for (opindex_ptr = opcode->operands; *opindex_ptr != 0; opindex_ptr++) { const struct powerpc_operand *operand; operand = &powerpc_operands[*opindex_ptr]; if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0) { unsigned int opcount; unsigned int num_operands_expected; unsigned int i; /* There is an optional operand. Count the number of commas in the input line. */ if (*str == '\0') opcount = 0; else { opcount = 1; s = str; while ((s = strchr (s, ',')) != (char *) NULL) { ++opcount; ++s; } } /* Compute the number of expected operands. Do not count fake operands. */ for (num_operands_expected = 0, i = 0; opcode->operands[i]; i ++) if ((powerpc_operands [opcode->operands[i]].flags & PPC_OPERAND_FAKE) == 0) ++ num_operands_expected; /* If there are fewer operands in the line then are called for by the instruction, we want to skip the optional operands. */ if (opcount < num_operands_expected) skip_optional = 1; break; } } /* Gather the operands. */ need_paren = 0; next_opindex = 0; fc = 0; for (opindex_ptr = opcode->operands; *opindex_ptr != 0; opindex_ptr++) { const struct powerpc_operand *operand; const char *errmsg; char *hold; expressionS ex; char endc; if (next_opindex == 0) operand = &powerpc_operands[*opindex_ptr]; else { operand = &powerpc_operands[next_opindex]; next_opindex = 0; } errmsg = NULL; /* If this is a fake operand, then we do not expect anything from the input. */ if ((operand->flags & PPC_OPERAND_FAKE) != 0) { insn = (*operand->insert) (insn, 0L, ppc_cpu, &errmsg); if (errmsg != (const char *) NULL) - as_bad (errmsg); + as_bad ("%s", errmsg); continue; } /* If this is an optional operand, and we are skipping it, just insert a zero. */ if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0 && skip_optional) { if (operand->insert) { insn = (*operand->insert) (insn, 0L, ppc_cpu, &errmsg); if (errmsg != (const char *) NULL) - as_bad (errmsg); + as_bad ("%s", errmsg); } if ((operand->flags & PPC_OPERAND_NEXT) != 0) next_opindex = *opindex_ptr + 1; continue; } /* Gather the operand. */ hold = input_line_pointer; input_line_pointer = str; #ifdef TE_PE if (*input_line_pointer == '[') { /* We are expecting something like the second argument here: * * lwz r4,[toc].GS.0.static_int(rtoc) * ^^^^^^^^^^^^^^^^^^^^^^^^^^^ * The argument following the `]' must be a symbol name, and the * register must be the toc register: 'rtoc' or '2' * * The effect is to 0 as the displacement field * in the instruction, and issue an IMAGE_REL_PPC_TOCREL16 (or * the appropriate variation) reloc against it based on the symbol. * The linker will build the toc, and insert the resolved toc offset. * * Note: * o The size of the toc entry is currently assumed to be * 32 bits. This should not be assumed to be a hard coded * number. * o In an effort to cope with a change from 32 to 64 bits, * there are also toc entries that are specified to be * either 32 or 64 bits: * lwz r4,[toc32].GS.0.static_int(rtoc) * lwz r4,[toc64].GS.0.static_int(rtoc) * These demand toc entries of the specified size, and the * instruction probably requires it. */ int valid_toc; enum toc_size_qualifier toc_kind; bfd_reloc_code_real_type toc_reloc; /* Go parse off the [tocXX] part. */ valid_toc = parse_toc_entry (&toc_kind); if (!valid_toc) { /* Note: message has already been issued. FIXME: what sort of recovery should we do? demand_rest_of_line (); return; ? */ } /* Now get the symbol following the ']'. */ expression (&ex); switch (toc_kind) { case default_toc: /* In this case, we may not have seen the symbol yet, since it is allowed to appear on a .extern or .globl or just be a label in the .data section. */ toc_reloc = BFD_RELOC_PPC_TOC16; break; case data_in_toc: /* 1. The symbol must be defined and either in the toc section, or a global. 2. The reloc generated must have the TOCDEFN flag set in upper bit mess of the reloc type. FIXME: It's a little confusing what the tocv qualifier can be used for. At the very least, I've seen three uses, only one of which I'm sure I can explain. */ if (ex.X_op == O_symbol) { assert (ex.X_add_symbol != NULL); if (symbol_get_bfdsym (ex.X_add_symbol)->section != tocdata_section) { as_bad (_("[tocv] symbol is not a toc symbol")); } } toc_reloc = BFD_RELOC_PPC_TOC16; break; case must_be_32: /* FIXME: these next two specifically specify 32/64 bit toc entries. We don't support them today. Is this the right way to say that? */ toc_reloc = BFD_RELOC_UNUSED; as_bad (_("Unimplemented toc32 expression modifier")); break; case must_be_64: /* FIXME: see above. */ toc_reloc = BFD_RELOC_UNUSED; as_bad (_("Unimplemented toc64 expression modifier")); break; default: fprintf (stderr, _("Unexpected return value [%d] from parse_toc_entry!\n"), toc_kind); abort (); break; } /* We need to generate a fixup for this expression. */ if (fc >= MAX_INSN_FIXUPS) as_fatal (_("too many fixups")); fixups[fc].reloc = toc_reloc; fixups[fc].exp = ex; fixups[fc].opindex = *opindex_ptr; ++fc; /* Ok. We've set up the fixup for the instruction. Now make it look like the constant 0 was found here. */ ex.X_unsigned = 1; ex.X_op = O_constant; ex.X_add_number = 0; ex.X_add_symbol = NULL; ex.X_op_symbol = NULL; } else #endif /* TE_PE */ { if (! register_name (&ex)) { if ((operand->flags & PPC_OPERAND_CR) != 0) cr_operand = TRUE; expression (&ex); cr_operand = FALSE; } } str = input_line_pointer; input_line_pointer = hold; if (ex.X_op == O_illegal) as_bad (_("illegal operand")); else if (ex.X_op == O_absent) as_bad (_("missing operand")); else if (ex.X_op == O_register) { insn = ppc_insert_operand (insn, operand, ex.X_add_number, (char *) NULL, 0); } else if (ex.X_op == O_constant) { #ifdef OBJ_ELF /* Allow @HA, @L, @H on constants. */ char *orig_str = str; if ((reloc = ppc_elf_suffix (&str, &ex)) != BFD_RELOC_UNUSED) switch (reloc) { default: str = orig_str; break; case BFD_RELOC_LO16: /* X_unsigned is the default, so if the user has done something which cleared it, we always produce a signed value. */ if (ex.X_unsigned && ! (operand->flags & PPC_OPERAND_SIGNED)) ex.X_add_number &= 0xffff; else ex.X_add_number = SEX16 (ex.X_add_number); break; case BFD_RELOC_HI16: if (ex.X_unsigned && ! (operand->flags & PPC_OPERAND_SIGNED)) ex.X_add_number = PPC_HI (ex.X_add_number); else ex.X_add_number = SEX16 (PPC_HI (ex.X_add_number)); break; case BFD_RELOC_HI16_S: if (ex.X_unsigned && ! (operand->flags & PPC_OPERAND_SIGNED)) ex.X_add_number = PPC_HA (ex.X_add_number); else ex.X_add_number = SEX16 (PPC_HA (ex.X_add_number)); break; case BFD_RELOC_PPC64_HIGHER: if (ex.X_unsigned && ! (operand->flags & PPC_OPERAND_SIGNED)) ex.X_add_number = PPC_HIGHER (ex.X_add_number); else ex.X_add_number = SEX16 (PPC_HIGHER (ex.X_add_number)); break; case BFD_RELOC_PPC64_HIGHER_S: if (ex.X_unsigned && ! (operand->flags & PPC_OPERAND_SIGNED)) ex.X_add_number = PPC_HIGHERA (ex.X_add_number); else ex.X_add_number = SEX16 (PPC_HIGHERA (ex.X_add_number)); break; case BFD_RELOC_PPC64_HIGHEST: if (ex.X_unsigned && ! (operand->flags & PPC_OPERAND_SIGNED)) ex.X_add_number = PPC_HIGHEST (ex.X_add_number); else ex.X_add_number = SEX16 (PPC_HIGHEST (ex.X_add_number)); break; case BFD_RELOC_PPC64_HIGHEST_S: if (ex.X_unsigned && ! (operand->flags & PPC_OPERAND_SIGNED)) ex.X_add_number = PPC_HIGHESTA (ex.X_add_number); else ex.X_add_number = SEX16 (PPC_HIGHESTA (ex.X_add_number)); break; } #endif /* OBJ_ELF */ insn = ppc_insert_operand (insn, operand, ex.X_add_number, (char *) NULL, 0); } #ifdef OBJ_ELF else { if (ex.X_op == O_symbol && str[0] == '(') { const char *sym_name = S_GET_NAME (ex.X_add_symbol); if (sym_name[0] == '.') ++sym_name; if (strcasecmp (sym_name, "__tls_get_addr") == 0) { expressionS tls_exp; hold = input_line_pointer; input_line_pointer = str + 1; expression (&tls_exp); if (tls_exp.X_op == O_symbol) { reloc = BFD_RELOC_UNUSED; if (strncasecmp (input_line_pointer, "@tlsgd)", 7) == 0) { reloc = BFD_RELOC_PPC_TLSGD; input_line_pointer += 7; } else if (strncasecmp (input_line_pointer, "@tlsld)", 7) == 0) { reloc = BFD_RELOC_PPC_TLSLD; input_line_pointer += 7; } if (reloc != BFD_RELOC_UNUSED) { SKIP_WHITESPACE (); str = input_line_pointer; if (fc >= MAX_INSN_FIXUPS) as_fatal (_("too many fixups")); fixups[fc].exp = tls_exp; fixups[fc].opindex = *opindex_ptr; fixups[fc].reloc = reloc; ++fc; } } input_line_pointer = hold; } } if ((reloc = ppc_elf_suffix (&str, &ex)) != BFD_RELOC_UNUSED) { /* Some TLS tweaks. */ switch (reloc) { default: break; case BFD_RELOC_PPC_TLS: insn = ppc_insert_operand (insn, operand, ppc_obj64 ? 13 : 2, (char *) NULL, 0); break; /* We'll only use the 32 (or 64) bit form of these relocations in constants. Instructions get the 16 bit form. */ case BFD_RELOC_PPC_DTPREL: reloc = BFD_RELOC_PPC_DTPREL16; break; case BFD_RELOC_PPC_TPREL: reloc = BFD_RELOC_PPC_TPREL16; break; } /* For the absolute forms of branches, convert the PC relative form back into the absolute. */ if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0) { switch (reloc) { case BFD_RELOC_PPC_B26: reloc = BFD_RELOC_PPC_BA26; break; case BFD_RELOC_PPC_B16: reloc = BFD_RELOC_PPC_BA16; break; case BFD_RELOC_PPC_B16_BRTAKEN: reloc = BFD_RELOC_PPC_BA16_BRTAKEN; break; case BFD_RELOC_PPC_B16_BRNTAKEN: reloc = BFD_RELOC_PPC_BA16_BRNTAKEN; break; default: break; } } if (ppc_obj64 && (operand->flags & (PPC_OPERAND_DS | PPC_OPERAND_DQ)) != 0) { switch (reloc) { case BFD_RELOC_16: reloc = BFD_RELOC_PPC64_ADDR16_DS; break; case BFD_RELOC_LO16: reloc = BFD_RELOC_PPC64_ADDR16_LO_DS; break; case BFD_RELOC_16_GOTOFF: reloc = BFD_RELOC_PPC64_GOT16_DS; break; case BFD_RELOC_LO16_GOTOFF: reloc = BFD_RELOC_PPC64_GOT16_LO_DS; break; case BFD_RELOC_LO16_PLTOFF: reloc = BFD_RELOC_PPC64_PLT16_LO_DS; break; case BFD_RELOC_16_BASEREL: reloc = BFD_RELOC_PPC64_SECTOFF_DS; break; case BFD_RELOC_LO16_BASEREL: reloc = BFD_RELOC_PPC64_SECTOFF_LO_DS; break; case BFD_RELOC_PPC_TOC16: reloc = BFD_RELOC_PPC64_TOC16_DS; break; case BFD_RELOC_PPC64_TOC16_LO: reloc = BFD_RELOC_PPC64_TOC16_LO_DS; break; case BFD_RELOC_PPC64_PLTGOT16: reloc = BFD_RELOC_PPC64_PLTGOT16_DS; break; case BFD_RELOC_PPC64_PLTGOT16_LO: reloc = BFD_RELOC_PPC64_PLTGOT16_LO_DS; break; case BFD_RELOC_PPC_DTPREL16: reloc = BFD_RELOC_PPC64_DTPREL16_DS; break; case BFD_RELOC_PPC_DTPREL16_LO: reloc = BFD_RELOC_PPC64_DTPREL16_LO_DS; break; case BFD_RELOC_PPC_TPREL16: reloc = BFD_RELOC_PPC64_TPREL16_DS; break; case BFD_RELOC_PPC_TPREL16_LO: reloc = BFD_RELOC_PPC64_TPREL16_LO_DS; break; case BFD_RELOC_PPC_GOT_DTPREL16: case BFD_RELOC_PPC_GOT_DTPREL16_LO: case BFD_RELOC_PPC_GOT_TPREL16: case BFD_RELOC_PPC_GOT_TPREL16_LO: break; default: as_bad (_("unsupported relocation for DS offset field")); break; } } } /* We need to generate a fixup for this expression. */ if (fc >= MAX_INSN_FIXUPS) as_fatal (_("too many fixups")); fixups[fc].exp = ex; fixups[fc].opindex = *opindex_ptr; fixups[fc].reloc = reloc; ++fc; } #else /* OBJ_ELF */ else { /* We need to generate a fixup for this expression. */ if (fc >= MAX_INSN_FIXUPS) as_fatal (_("too many fixups")); fixups[fc].exp = ex; fixups[fc].opindex = *opindex_ptr; fixups[fc].reloc = BFD_RELOC_UNUSED; ++fc; } #endif /* OBJ_ELF */ if (need_paren) { endc = ')'; need_paren = 0; } else if ((operand->flags & PPC_OPERAND_PARENS) != 0) { endc = '('; need_paren = 1; } else endc = ','; /* The call to expression should have advanced str past any whitespace. */ if (*str != endc && (endc != ',' || *str != '\0')) { as_bad (_("syntax error; found `%c' but expected `%c'"), *str, endc); break; } if (*str != '\0') ++str; } while (ISSPACE (*str)) ++str; if (*str != '\0') as_bad (_("junk at end of line: `%s'"), str); #ifdef OBJ_ELF /* Do we need/want a APUinfo section? */ if (ppc_cpu & (PPC_OPCODE_SPE | PPC_OPCODE_ISEL | PPC_OPCODE_EFS | PPC_OPCODE_BRLOCK | PPC_OPCODE_PMR | PPC_OPCODE_CACHELCK | PPC_OPCODE_RFMCI)) { /* These are all version "1". */ if (opcode->flags & PPC_OPCODE_SPE) ppc_apuinfo_section_add (PPC_APUINFO_SPE, 1); if (opcode->flags & PPC_OPCODE_ISEL) ppc_apuinfo_section_add (PPC_APUINFO_ISEL, 1); if (opcode->flags & PPC_OPCODE_EFS) ppc_apuinfo_section_add (PPC_APUINFO_EFS, 1); if (opcode->flags & PPC_OPCODE_BRLOCK) ppc_apuinfo_section_add (PPC_APUINFO_BRLOCK, 1); if (opcode->flags & PPC_OPCODE_PMR) ppc_apuinfo_section_add (PPC_APUINFO_PMR, 1); if (opcode->flags & PPC_OPCODE_CACHELCK) ppc_apuinfo_section_add (PPC_APUINFO_CACHELCK, 1); if (opcode->flags & PPC_OPCODE_RFMCI) ppc_apuinfo_section_add (PPC_APUINFO_RFMCI, 1); } #endif /* Write out the instruction. */ f = frag_more (4); addr_mod = frag_now_fix () & 3; if (frag_now->has_code && frag_now->insn_addr != addr_mod) as_bad (_("instruction address is not a multiple of 4")); frag_now->insn_addr = addr_mod; frag_now->has_code = 1; md_number_to_chars (f, insn, 4); #ifdef OBJ_ELF dwarf2_emit_insn (4); #endif /* Create any fixups. At this point we do not use a bfd_reloc_code_real_type, but instead just use the BFD_RELOC_UNUSED plus the operand index. This lets us easily handle fixups for any operand type, although that is admittedly not a very exciting feature. We pick a BFD reloc type in md_apply_fix. */ for (i = 0; i < fc; i++) { const struct powerpc_operand *operand; operand = &powerpc_operands[fixups[i].opindex]; if (fixups[i].reloc != BFD_RELOC_UNUSED) { reloc_howto_type *reloc_howto; int size; int offset; fixS *fixP; reloc_howto = bfd_reloc_type_lookup (stdoutput, fixups[i].reloc); if (!reloc_howto) abort (); size = bfd_get_reloc_size (reloc_howto); offset = target_big_endian ? (4 - size) : 0; if (size < 1 || size > 4) abort (); fixP = fix_new_exp (frag_now, f - frag_now->fr_literal + offset, size, &fixups[i].exp, reloc_howto->pc_relative, fixups[i].reloc); /* Turn off complaints that the addend is too large for things like foo+100000@ha. */ switch (fixups[i].reloc) { case BFD_RELOC_16_GOTOFF: case BFD_RELOC_PPC_TOC16: case BFD_RELOC_LO16: case BFD_RELOC_HI16: case BFD_RELOC_HI16_S: #ifdef OBJ_ELF case BFD_RELOC_PPC64_HIGHER: case BFD_RELOC_PPC64_HIGHER_S: case BFD_RELOC_PPC64_HIGHEST: case BFD_RELOC_PPC64_HIGHEST_S: #endif fixP->fx_no_overflow = 1; break; default: break; } } else fix_new_exp (frag_now, f - frag_now->fr_literal, 4, &fixups[i].exp, (operand->flags & PPC_OPERAND_RELATIVE) != 0, ((bfd_reloc_code_real_type) (fixups[i].opindex + (int) BFD_RELOC_UNUSED))); } } /* Handle a macro. Gather all the operands, transform them as described by the macro, and call md_assemble recursively. All the operands are separated by commas; we don't accept parentheses around operands here. */ static void ppc_macro (char *str, const struct powerpc_macro *macro) { char *operands[10]; unsigned int count; char *s; unsigned int len; const char *format; unsigned int arg; char *send; char *complete; /* Gather the users operands into the operands array. */ count = 0; s = str; while (1) { if (count >= sizeof operands / sizeof operands[0]) break; operands[count++] = s; s = strchr (s, ','); if (s == (char *) NULL) break; *s++ = '\0'; } if (count != macro->operands) { as_bad (_("wrong number of operands")); return; } /* Work out how large the string must be (the size is unbounded because it includes user input). */ len = 0; format = macro->format; while (*format != '\0') { if (*format != '%') { ++len; ++format; } else { arg = strtol (format + 1, &send, 10); know (send != format && arg < count); len += strlen (operands[arg]); format = send; } } /* Put the string together. */ complete = s = (char *) alloca (len + 1); format = macro->format; while (*format != '\0') { if (*format != '%') *s++ = *format++; else { arg = strtol (format + 1, &send, 10); strcpy (s, operands[arg]); s += strlen (s); format = send; } } *s = '\0'; /* Assemble the constructed instruction. */ md_assemble (complete); } #ifdef OBJ_ELF /* For ELF, add support for SHF_EXCLUDE and SHT_ORDERED. */ int ppc_section_letter (int letter, char **ptr_msg) { if (letter == 'e') return SHF_EXCLUDE; *ptr_msg = _("Bad .section directive: want a,e,w,x,M,S,G,T in string"); return -1; } int ppc_section_word (char *str, size_t len) { if (len == 7 && strncmp (str, "exclude", 7) == 0) return SHF_EXCLUDE; return -1; } int ppc_section_type (char *str, size_t len) { if (len == 7 && strncmp (str, "ordered", 7) == 0) return SHT_ORDERED; return -1; } int ppc_section_flags (int flags, int attr, int type) { if (type == SHT_ORDERED) flags |= SEC_ALLOC | SEC_LOAD | SEC_SORT_ENTRIES; if (attr & SHF_EXCLUDE) flags |= SEC_EXCLUDE; return flags; } #endif /* OBJ_ELF */ /* Pseudo-op handling. */ /* The .byte pseudo-op. This is similar to the normal .byte pseudo-op, but it can also take a single ASCII string. */ static void ppc_byte (int ignore ATTRIBUTE_UNUSED) { if (*input_line_pointer != '\"') { cons (1); return; } /* Gather characters. A real double quote is doubled. Unusual characters are not permitted. */ ++input_line_pointer; while (1) { char c; c = *input_line_pointer++; if (c == '\"') { if (*input_line_pointer != '\"') break; ++input_line_pointer; } FRAG_APPEND_1_CHAR (c); } demand_empty_rest_of_line (); } #ifdef OBJ_XCOFF /* XCOFF specific pseudo-op handling. */ /* This is set if we are creating a .stabx symbol, since we don't want to handle symbol suffixes for such symbols. */ static bfd_boolean ppc_stab_symbol; /* The .comm and .lcomm pseudo-ops for XCOFF. XCOFF puts common symbols in the .bss segment as though they were local common symbols, and uses a different smclas. The native Aix 4.3.3 assembler aligns .comm and .lcomm to 4 bytes. */ static void ppc_comm (int lcomm) { asection *current_seg = now_seg; subsegT current_subseg = now_subseg; char *name; char endc; char *end_name; offsetT size; offsetT align; symbolS *lcomm_sym = NULL; symbolS *sym; char *pfrag; name = input_line_pointer; endc = get_symbol_end (); end_name = input_line_pointer; *end_name = endc; if (*input_line_pointer != ',') { as_bad (_("missing size")); ignore_rest_of_line (); return; } ++input_line_pointer; size = get_absolute_expression (); if (size < 0) { as_bad (_("negative size")); ignore_rest_of_line (); return; } if (! lcomm) { /* The third argument to .comm is the alignment. */ if (*input_line_pointer != ',') align = 2; else { ++input_line_pointer; align = get_absolute_expression (); if (align <= 0) { as_warn (_("ignoring bad alignment")); align = 2; } } } else { char *lcomm_name; char lcomm_endc; if (size <= 4) align = 2; else align = 3; /* The third argument to .lcomm appears to be the real local common symbol to create. References to the symbol named in the first argument are turned into references to the third argument. */ if (*input_line_pointer != ',') { as_bad (_("missing real symbol name")); ignore_rest_of_line (); return; } ++input_line_pointer; lcomm_name = input_line_pointer; lcomm_endc = get_symbol_end (); lcomm_sym = symbol_find_or_make (lcomm_name); *input_line_pointer = lcomm_endc; } *end_name = '\0'; sym = symbol_find_or_make (name); *end_name = endc; if (S_IS_DEFINED (sym) || S_GET_VALUE (sym) != 0) { as_bad (_("attempt to redefine symbol")); ignore_rest_of_line (); return; } record_alignment (bss_section, align); if (! lcomm || ! S_IS_DEFINED (lcomm_sym)) { symbolS *def_sym; offsetT def_size; if (! lcomm) { def_sym = sym; def_size = size; S_SET_EXTERNAL (sym); } else { symbol_get_tc (lcomm_sym)->output = 1; def_sym = lcomm_sym; def_size = 0; } subseg_set (bss_section, 1); frag_align (align, 0, 0); symbol_set_frag (def_sym, frag_now); pfrag = frag_var (rs_org, 1, 1, (relax_substateT) 0, def_sym, def_size, (char *) NULL); *pfrag = 0; S_SET_SEGMENT (def_sym, bss_section); symbol_get_tc (def_sym)->align = align; } else if (lcomm) { /* Align the size of lcomm_sym. */ symbol_get_frag (lcomm_sym)->fr_offset = ((symbol_get_frag (lcomm_sym)->fr_offset + (1 << align) - 1) &~ ((1 << align) - 1)); if (align > symbol_get_tc (lcomm_sym)->align) symbol_get_tc (lcomm_sym)->align = align; } if (lcomm) { /* Make sym an offset from lcomm_sym. */ S_SET_SEGMENT (sym, bss_section); symbol_set_frag (sym, symbol_get_frag (lcomm_sym)); S_SET_VALUE (sym, symbol_get_frag (lcomm_sym)->fr_offset); symbol_get_frag (lcomm_sym)->fr_offset += size; } subseg_set (current_seg, current_subseg); demand_empty_rest_of_line (); } /* The .csect pseudo-op. This switches us into a different subsegment. The first argument is a symbol whose value is the start of the .csect. In COFF, csect symbols get special aux entries defined by the x_csect field of union internal_auxent. The optional second argument is the alignment (the default is 2). */ static void ppc_csect (int ignore ATTRIBUTE_UNUSED) { char *name; char endc; symbolS *sym; offsetT align; name = input_line_pointer; endc = get_symbol_end (); sym = symbol_find_or_make (name); *input_line_pointer = endc; if (S_GET_NAME (sym)[0] == '\0') { /* An unnamed csect is assumed to be [PR]. */ symbol_get_tc (sym)->class = XMC_PR; } align = 2; if (*input_line_pointer == ',') { ++input_line_pointer; align = get_absolute_expression (); } ppc_change_csect (sym, align); demand_empty_rest_of_line (); } /* Change to a different csect. */ static void ppc_change_csect (symbolS *sym, offsetT align) { if (S_IS_DEFINED (sym)) subseg_set (S_GET_SEGMENT (sym), symbol_get_tc (sym)->subseg); else { symbolS **list_ptr; int after_toc; int hold_chunksize; symbolS *list; int is_code; segT sec; /* This is a new csect. We need to look at the symbol class to figure out whether it should go in the text section or the data section. */ after_toc = 0; is_code = 0; switch (symbol_get_tc (sym)->class) { case XMC_PR: case XMC_RO: case XMC_DB: case XMC_GL: case XMC_XO: case XMC_SV: case XMC_TI: case XMC_TB: S_SET_SEGMENT (sym, text_section); symbol_get_tc (sym)->subseg = ppc_text_subsegment; ++ppc_text_subsegment; list_ptr = &ppc_text_csects; is_code = 1; break; case XMC_RW: case XMC_TC0: case XMC_TC: case XMC_DS: case XMC_UA: case XMC_BS: case XMC_UC: if (ppc_toc_csect != NULL && (symbol_get_tc (ppc_toc_csect)->subseg + 1 == ppc_data_subsegment)) after_toc = 1; S_SET_SEGMENT (sym, data_section); symbol_get_tc (sym)->subseg = ppc_data_subsegment; ++ppc_data_subsegment; list_ptr = &ppc_data_csects; break; default: abort (); } /* We set the obstack chunk size to a small value before changing subsegments, so that we don't use a lot of memory space for what may be a small section. */ hold_chunksize = chunksize; chunksize = 64; sec = subseg_new (segment_name (S_GET_SEGMENT (sym)), symbol_get_tc (sym)->subseg); chunksize = hold_chunksize; if (after_toc) ppc_after_toc_frag = frag_now; record_alignment (sec, align); if (is_code) frag_align_code (align, 0); else frag_align (align, 0, 0); symbol_set_frag (sym, frag_now); S_SET_VALUE (sym, (valueT) frag_now_fix ()); symbol_get_tc (sym)->align = align; symbol_get_tc (sym)->output = 1; symbol_get_tc (sym)->within = sym; for (list = *list_ptr; symbol_get_tc (list)->next != (symbolS *) NULL; list = symbol_get_tc (list)->next) ; symbol_get_tc (list)->next = sym; symbol_remove (sym, &symbol_rootP, &symbol_lastP); symbol_append (sym, symbol_get_tc (list)->within, &symbol_rootP, &symbol_lastP); } ppc_current_csect = sym; } /* This function handles the .text and .data pseudo-ops. These pseudo-ops aren't really used by XCOFF; we implement them for the convenience of people who aren't used to XCOFF. */ static void ppc_section (int type) { const char *name; symbolS *sym; if (type == 't') name = ".text[PR]"; else if (type == 'd') name = ".data[RW]"; else abort (); sym = symbol_find_or_make (name); ppc_change_csect (sym, 2); demand_empty_rest_of_line (); } /* This function handles the .section pseudo-op. This is mostly to give an error, since XCOFF only supports .text, .data and .bss, but we do permit the user to name the text or data section. */ static void ppc_named_section (int ignore ATTRIBUTE_UNUSED) { char *user_name; const char *real_name; char c; symbolS *sym; user_name = input_line_pointer; c = get_symbol_end (); if (strcmp (user_name, ".text") == 0) real_name = ".text[PR]"; else if (strcmp (user_name, ".data") == 0) real_name = ".data[RW]"; else { as_bad (_("The XCOFF file format does not support arbitrary sections")); *input_line_pointer = c; ignore_rest_of_line (); return; } *input_line_pointer = c; sym = symbol_find_or_make (real_name); ppc_change_csect (sym, 2); demand_empty_rest_of_line (); } /* The .extern pseudo-op. We create an undefined symbol. */ static void ppc_extern (int ignore ATTRIBUTE_UNUSED) { char *name; char endc; name = input_line_pointer; endc = get_symbol_end (); (void) symbol_find_or_make (name); *input_line_pointer = endc; demand_empty_rest_of_line (); } /* The .lglobl pseudo-op. Keep the symbol in the symbol table. */ static void ppc_lglobl (int ignore ATTRIBUTE_UNUSED) { char *name; char endc; symbolS *sym; name = input_line_pointer; endc = get_symbol_end (); sym = symbol_find_or_make (name); *input_line_pointer = endc; symbol_get_tc (sym)->output = 1; demand_empty_rest_of_line (); } /* The .rename pseudo-op. The RS/6000 assembler can rename symbols, although I don't know why it bothers. */ static void ppc_rename (int ignore ATTRIBUTE_UNUSED) { char *name; char endc; symbolS *sym; int len; name = input_line_pointer; endc = get_symbol_end (); sym = symbol_find_or_make (name); *input_line_pointer = endc; if (*input_line_pointer != ',') { as_bad (_("missing rename string")); ignore_rest_of_line (); return; } ++input_line_pointer; symbol_get_tc (sym)->real_name = demand_copy_C_string (&len); demand_empty_rest_of_line (); } /* The .stabx pseudo-op. This is similar to a normal .stabs pseudo-op, but slightly different. A sample is .stabx "main:F-1",.main,142,0 The first argument is the symbol name to create. The second is the value, and the third is the storage class. The fourth seems to be always zero, and I am assuming it is the type. */ static void ppc_stabx (int ignore ATTRIBUTE_UNUSED) { char *name; int len; symbolS *sym; expressionS exp; name = demand_copy_C_string (&len); if (*input_line_pointer != ',') { as_bad (_("missing value")); return; } ++input_line_pointer; ppc_stab_symbol = TRUE; sym = symbol_make (name); ppc_stab_symbol = FALSE; symbol_get_tc (sym)->real_name = name; (void) expression (&exp); switch (exp.X_op) { case O_illegal: case O_absent: case O_big: as_bad (_("illegal .stabx expression; zero assumed")); exp.X_add_number = 0; /* Fall through. */ case O_constant: S_SET_VALUE (sym, (valueT) exp.X_add_number); symbol_set_frag (sym, &zero_address_frag); break; case O_symbol: if (S_GET_SEGMENT (exp.X_add_symbol) == undefined_section) symbol_set_value_expression (sym, &exp); else { S_SET_VALUE (sym, exp.X_add_number + S_GET_VALUE (exp.X_add_symbol)); symbol_set_frag (sym, symbol_get_frag (exp.X_add_symbol)); } break; default: /* The value is some complex expression. This will probably fail at some later point, but this is probably the right thing to do here. */ symbol_set_value_expression (sym, &exp); break; } S_SET_SEGMENT (sym, ppc_coff_debug_section); symbol_get_bfdsym (sym)->flags |= BSF_DEBUGGING; if (*input_line_pointer != ',') { as_bad (_("missing class")); return; } ++input_line_pointer; S_SET_STORAGE_CLASS (sym, get_absolute_expression ()); if (*input_line_pointer != ',') { as_bad (_("missing type")); return; } ++input_line_pointer; S_SET_DATA_TYPE (sym, get_absolute_expression ()); symbol_get_tc (sym)->output = 1; if (S_GET_STORAGE_CLASS (sym) == C_STSYM) { symbol_get_tc (sym)->within = ppc_current_block; /* In this case : .bs name .stabx "z",arrays_,133,0 .es .comm arrays_,13768,3 resolve_symbol_value will copy the exp's "within" into sym's when the offset is 0. Since this seems to be corner case problem, only do the correction for storage class C_STSYM. A better solution would be to have the tc field updated in ppc_symbol_new_hook. */ if (exp.X_op == O_symbol) { symbol_get_tc (exp.X_add_symbol)->within = ppc_current_block; } } if (exp.X_op != O_symbol || ! S_IS_EXTERNAL (exp.X_add_symbol) || S_GET_SEGMENT (exp.X_add_symbol) != bss_section) ppc_frob_label (sym); else { symbol_remove (sym, &symbol_rootP, &symbol_lastP); symbol_append (sym, exp.X_add_symbol, &symbol_rootP, &symbol_lastP); if (symbol_get_tc (ppc_current_csect)->within == exp.X_add_symbol) symbol_get_tc (ppc_current_csect)->within = sym; } demand_empty_rest_of_line (); } /* The .function pseudo-op. This takes several arguments. The first argument seems to be the external name of the symbol. The second argument seems to be the label for the start of the function. gcc uses the same name for both. I have no idea what the third and fourth arguments are meant to be. The optional fifth argument is an expression for the size of the function. In COFF this symbol gets an aux entry like that used for a csect. */ static void ppc_function (int ignore ATTRIBUTE_UNUSED) { char *name; char endc; char *s; symbolS *ext_sym; symbolS *lab_sym; name = input_line_pointer; endc = get_symbol_end (); /* Ignore any [PR] suffix. */ name = ppc_canonicalize_symbol_name (name); s = strchr (name, '['); if (s != (char *) NULL && strcmp (s + 1, "PR]") == 0) *s = '\0'; ext_sym = symbol_find_or_make (name); *input_line_pointer = endc; if (*input_line_pointer != ',') { as_bad (_("missing symbol name")); ignore_rest_of_line (); return; } ++input_line_pointer; name = input_line_pointer; endc = get_symbol_end (); lab_sym = symbol_find_or_make (name); *input_line_pointer = endc; if (ext_sym != lab_sym) { expressionS exp; exp.X_op = O_symbol; exp.X_add_symbol = lab_sym; exp.X_op_symbol = NULL; exp.X_add_number = 0; exp.X_unsigned = 0; symbol_set_value_expression (ext_sym, &exp); } if (symbol_get_tc (ext_sym)->class == -1) symbol_get_tc (ext_sym)->class = XMC_PR; symbol_get_tc (ext_sym)->output = 1; if (*input_line_pointer == ',') { expressionS ignore; /* Ignore the third argument. */ ++input_line_pointer; expression (&ignore); if (*input_line_pointer == ',') { /* Ignore the fourth argument. */ ++input_line_pointer; expression (&ignore); if (*input_line_pointer == ',') { /* The fifth argument is the function size. */ ++input_line_pointer; symbol_get_tc (ext_sym)->size = symbol_new ("L0\001", absolute_section, (valueT) 0, &zero_address_frag); pseudo_set (symbol_get_tc (ext_sym)->size); } } } S_SET_DATA_TYPE (ext_sym, DT_FCN << N_BTSHFT); SF_SET_FUNCTION (ext_sym); SF_SET_PROCESS (ext_sym); coff_add_linesym (ext_sym); demand_empty_rest_of_line (); } /* The .bf pseudo-op. This is just like a COFF C_FCN symbol named ".bf". If the pseudo op .bi was seen before .bf, patch the .bi sym with the correct line number */ static symbolS *saved_bi_sym = 0; static void ppc_bf (int ignore ATTRIBUTE_UNUSED) { symbolS *sym; sym = symbol_make (".bf"); S_SET_SEGMENT (sym, text_section); symbol_set_frag (sym, frag_now); S_SET_VALUE (sym, frag_now_fix ()); S_SET_STORAGE_CLASS (sym, C_FCN); coff_line_base = get_absolute_expression (); S_SET_NUMBER_AUXILIARY (sym, 1); SA_SET_SYM_LNNO (sym, coff_line_base); /* Line number for bi. */ if (saved_bi_sym) { S_SET_VALUE (saved_bi_sym, coff_n_line_nos); saved_bi_sym = 0; } symbol_get_tc (sym)->output = 1; ppc_frob_label (sym); demand_empty_rest_of_line (); } /* The .ef pseudo-op. This is just like a COFF C_FCN symbol named ".ef", except that the line number is absolute, not relative to the most recent ".bf" symbol. */ static void ppc_ef (int ignore ATTRIBUTE_UNUSED) { symbolS *sym; sym = symbol_make (".ef"); S_SET_SEGMENT (sym, text_section); symbol_set_frag (sym, frag_now); S_SET_VALUE (sym, frag_now_fix ()); S_SET_STORAGE_CLASS (sym, C_FCN); S_SET_NUMBER_AUXILIARY (sym, 1); SA_SET_SYM_LNNO (sym, get_absolute_expression ()); symbol_get_tc (sym)->output = 1; ppc_frob_label (sym); demand_empty_rest_of_line (); } /* The .bi and .ei pseudo-ops. These take a string argument and generates a C_BINCL or C_EINCL symbol, which goes at the start of the symbol list. The value of .bi will be know when the next .bf is encountered. */ static void ppc_biei (int ei) { static symbolS *last_biei; char *name; int len; symbolS *sym; symbolS *look; name = demand_copy_C_string (&len); /* The value of these symbols is actually file offset. Here we set the value to the index into the line number entries. In ppc_frob_symbols we set the fix_line field, which will cause BFD to do the right thing. */ sym = symbol_make (name); /* obj-coff.c currently only handles line numbers correctly in the .text section. */ S_SET_SEGMENT (sym, text_section); S_SET_VALUE (sym, coff_n_line_nos); symbol_get_bfdsym (sym)->flags |= BSF_DEBUGGING; S_SET_STORAGE_CLASS (sym, ei ? C_EINCL : C_BINCL); symbol_get_tc (sym)->output = 1; /* Save bi. */ if (ei) saved_bi_sym = 0; else saved_bi_sym = sym; for (look = last_biei ? last_biei : symbol_rootP; (look != (symbolS *) NULL && (S_GET_STORAGE_CLASS (look) == C_FILE || S_GET_STORAGE_CLASS (look) == C_BINCL || S_GET_STORAGE_CLASS (look) == C_EINCL)); look = symbol_next (look)) ; if (look != (symbolS *) NULL) { symbol_remove (sym, &symbol_rootP, &symbol_lastP); symbol_insert (sym, look, &symbol_rootP, &symbol_lastP); last_biei = sym; } demand_empty_rest_of_line (); } /* The .bs pseudo-op. This generates a C_BSTAT symbol named ".bs". There is one argument, which is a csect symbol. The value of the .bs symbol is the index of this csect symbol. */ static void ppc_bs (int ignore ATTRIBUTE_UNUSED) { char *name; char endc; symbolS *csect; symbolS *sym; if (ppc_current_block != NULL) as_bad (_("nested .bs blocks")); name = input_line_pointer; endc = get_symbol_end (); csect = symbol_find_or_make (name); *input_line_pointer = endc; sym = symbol_make (".bs"); S_SET_SEGMENT (sym, now_seg); S_SET_STORAGE_CLASS (sym, C_BSTAT); symbol_get_bfdsym (sym)->flags |= BSF_DEBUGGING; symbol_get_tc (sym)->output = 1; symbol_get_tc (sym)->within = csect; ppc_frob_label (sym); ppc_current_block = sym; demand_empty_rest_of_line (); } /* The .es pseudo-op. Generate a C_ESTART symbol named .es. */ static void ppc_es (int ignore ATTRIBUTE_UNUSED) { symbolS *sym; if (ppc_current_block == NULL) as_bad (_(".es without preceding .bs")); sym = symbol_make (".es"); S_SET_SEGMENT (sym, now_seg); S_SET_STORAGE_CLASS (sym, C_ESTAT); symbol_get_bfdsym (sym)->flags |= BSF_DEBUGGING; symbol_get_tc (sym)->output = 1; ppc_frob_label (sym); ppc_current_block = NULL; demand_empty_rest_of_line (); } /* The .bb pseudo-op. Generate a C_BLOCK symbol named .bb, with a line number. */ static void ppc_bb (int ignore ATTRIBUTE_UNUSED) { symbolS *sym; sym = symbol_make (".bb"); S_SET_SEGMENT (sym, text_section); symbol_set_frag (sym, frag_now); S_SET_VALUE (sym, frag_now_fix ()); S_SET_STORAGE_CLASS (sym, C_BLOCK); S_SET_NUMBER_AUXILIARY (sym, 1); SA_SET_SYM_LNNO (sym, get_absolute_expression ()); symbol_get_tc (sym)->output = 1; SF_SET_PROCESS (sym); ppc_frob_label (sym); demand_empty_rest_of_line (); } /* The .eb pseudo-op. Generate a C_BLOCK symbol named .eb, with a line number. */ static void ppc_eb (int ignore ATTRIBUTE_UNUSED) { symbolS *sym; sym = symbol_make (".eb"); S_SET_SEGMENT (sym, text_section); symbol_set_frag (sym, frag_now); S_SET_VALUE (sym, frag_now_fix ()); S_SET_STORAGE_CLASS (sym, C_BLOCK); S_SET_NUMBER_AUXILIARY (sym, 1); SA_SET_SYM_LNNO (sym, get_absolute_expression ()); symbol_get_tc (sym)->output = 1; SF_SET_PROCESS (sym); ppc_frob_label (sym); demand_empty_rest_of_line (); } /* The .bc pseudo-op. This just creates a C_BCOMM symbol with a specified name. */ static void ppc_bc (int ignore ATTRIBUTE_UNUSED) { char *name; int len; symbolS *sym; name = demand_copy_C_string (&len); sym = symbol_make (name); S_SET_SEGMENT (sym, ppc_coff_debug_section); symbol_get_bfdsym (sym)->flags |= BSF_DEBUGGING; S_SET_STORAGE_CLASS (sym, C_BCOMM); S_SET_VALUE (sym, 0); symbol_get_tc (sym)->output = 1; ppc_frob_label (sym); demand_empty_rest_of_line (); } /* The .ec pseudo-op. This just creates a C_ECOMM symbol. */ static void ppc_ec (int ignore ATTRIBUTE_UNUSED) { symbolS *sym; sym = symbol_make (".ec"); S_SET_SEGMENT (sym, ppc_coff_debug_section); symbol_get_bfdsym (sym)->flags |= BSF_DEBUGGING; S_SET_STORAGE_CLASS (sym, C_ECOMM); S_SET_VALUE (sym, 0); symbol_get_tc (sym)->output = 1; ppc_frob_label (sym); demand_empty_rest_of_line (); } /* The .toc pseudo-op. Switch to the .toc subsegment. */ static void ppc_toc (int ignore ATTRIBUTE_UNUSED) { if (ppc_toc_csect != (symbolS *) NULL) subseg_set (data_section, symbol_get_tc (ppc_toc_csect)->subseg); else { subsegT subseg; symbolS *sym; symbolS *list; subseg = ppc_data_subsegment; ++ppc_data_subsegment; subseg_new (segment_name (data_section), subseg); ppc_toc_frag = frag_now; sym = symbol_find_or_make ("TOC[TC0]"); symbol_set_frag (sym, frag_now); S_SET_SEGMENT (sym, data_section); S_SET_VALUE (sym, (valueT) frag_now_fix ()); symbol_get_tc (sym)->subseg = subseg; symbol_get_tc (sym)->output = 1; symbol_get_tc (sym)->within = sym; ppc_toc_csect = sym; for (list = ppc_data_csects; symbol_get_tc (list)->next != (symbolS *) NULL; list = symbol_get_tc (list)->next) ; symbol_get_tc (list)->next = sym; symbol_remove (sym, &symbol_rootP, &symbol_lastP); symbol_append (sym, symbol_get_tc (list)->within, &symbol_rootP, &symbol_lastP); } ppc_current_csect = ppc_toc_csect; demand_empty_rest_of_line (); } /* The AIX assembler automatically aligns the operands of a .long or .short pseudo-op, and we want to be compatible. */ static void ppc_xcoff_cons (int log_size) { frag_align (log_size, 0, 0); record_alignment (now_seg, log_size); cons (1 << log_size); } static void ppc_vbyte (int dummy ATTRIBUTE_UNUSED) { expressionS exp; int byte_count; (void) expression (&exp); if (exp.X_op != O_constant) { as_bad (_("non-constant byte count")); return; } byte_count = exp.X_add_number; if (*input_line_pointer != ',') { as_bad (_("missing value")); return; } ++input_line_pointer; cons (byte_count); } #endif /* OBJ_XCOFF */ #if defined (OBJ_XCOFF) || defined (OBJ_ELF) /* The .tc pseudo-op. This is used when generating either XCOFF or ELF. This takes two or more arguments. When generating XCOFF output, the first argument is the name to give to this location in the toc; this will be a symbol with class TC. The rest of the arguments are N-byte values to actually put at this location in the TOC; often there is just one more argument, a relocatable symbol reference. The size of the value to store depends on target word size. A 32-bit target uses 4-byte values, a 64-bit target uses 8-byte values. When not generating XCOFF output, the arguments are the same, but the first argument is simply ignored. */ static void ppc_tc (int ignore ATTRIBUTE_UNUSED) { #ifdef OBJ_XCOFF /* Define the TOC symbol name. */ { char *name; char endc; symbolS *sym; if (ppc_toc_csect == (symbolS *) NULL || ppc_toc_csect != ppc_current_csect) { as_bad (_(".tc not in .toc section")); ignore_rest_of_line (); return; } name = input_line_pointer; endc = get_symbol_end (); sym = symbol_find_or_make (name); *input_line_pointer = endc; if (S_IS_DEFINED (sym)) { symbolS *label; label = symbol_get_tc (ppc_current_csect)->within; if (symbol_get_tc (label)->class != XMC_TC0) { as_bad (_(".tc with no label")); ignore_rest_of_line (); return; } S_SET_SEGMENT (label, S_GET_SEGMENT (sym)); symbol_set_frag (label, symbol_get_frag (sym)); S_SET_VALUE (label, S_GET_VALUE (sym)); while (! is_end_of_line[(unsigned char) *input_line_pointer]) ++input_line_pointer; return; } S_SET_SEGMENT (sym, now_seg); symbol_set_frag (sym, frag_now); S_SET_VALUE (sym, (valueT) frag_now_fix ()); symbol_get_tc (sym)->class = XMC_TC; symbol_get_tc (sym)->output = 1; ppc_frob_label (sym); } #endif /* OBJ_XCOFF */ #ifdef OBJ_ELF int align; /* Skip the TOC symbol name. */ while (is_part_of_name (*input_line_pointer) || *input_line_pointer == '[' || *input_line_pointer == ']' || *input_line_pointer == '{' || *input_line_pointer == '}') ++input_line_pointer; /* Align to a four/eight byte boundary. */ align = ppc_obj64 ? 3 : 2; frag_align (align, 0, 0); record_alignment (now_seg, align); #endif /* OBJ_ELF */ if (*input_line_pointer != ',') demand_empty_rest_of_line (); else { ++input_line_pointer; cons (ppc_obj64 ? 8 : 4); } } /* Pseudo-op .machine. */ static void ppc_machine (int ignore ATTRIBUTE_UNUSED) { char *cpu_string; #define MAX_HISTORY 100 static unsigned long *cpu_history; static int curr_hist; SKIP_WHITESPACE (); if (*input_line_pointer == '"') { int len; cpu_string = demand_copy_C_string (&len); } else { char c; cpu_string = input_line_pointer; c = get_symbol_end (); cpu_string = xstrdup (cpu_string); *input_line_pointer = c; } if (cpu_string != NULL) { unsigned long old_cpu = ppc_cpu; char *p; for (p = cpu_string; *p != 0; p++) *p = TOLOWER (*p); if (strcmp (cpu_string, "push") == 0) { if (cpu_history == NULL) cpu_history = xmalloc (MAX_HISTORY * sizeof (*cpu_history)); if (curr_hist >= MAX_HISTORY) as_bad (_(".machine stack overflow")); else cpu_history[curr_hist++] = ppc_cpu; } else if (strcmp (cpu_string, "pop") == 0) { if (curr_hist <= 0) as_bad (_(".machine stack underflow")); else ppc_cpu = cpu_history[--curr_hist]; } else if (parse_cpu (cpu_string)) ; else as_bad (_("invalid machine `%s'"), cpu_string); if (ppc_cpu != old_cpu) ppc_setup_opcodes (); } demand_empty_rest_of_line (); } /* See whether a symbol is in the TOC section. */ static int ppc_is_toc_sym (symbolS *sym) { #ifdef OBJ_XCOFF return symbol_get_tc (sym)->class == XMC_TC; #endif #ifdef OBJ_ELF const char *sname = segment_name (S_GET_SEGMENT (sym)); if (ppc_obj64) return strcmp (sname, ".toc") == 0; else return strcmp (sname, ".got") == 0; #endif } #endif /* defined (OBJ_XCOFF) || defined (OBJ_ELF) */ #ifdef TE_PE /* Pseudo-ops specific to the Windows NT PowerPC PE (coff) format. */ /* Set the current section. */ static void ppc_set_current_section (segT new) { ppc_previous_section = ppc_current_section; ppc_current_section = new; } /* pseudo-op: .previous behaviour: toggles the current section with the previous section. errors: None warnings: "No previous section" */ static void ppc_previous (int ignore ATTRIBUTE_UNUSED) { symbolS *tmp; if (ppc_previous_section == NULL) { as_warn (_("No previous section to return to. Directive ignored.")); return; } subseg_set (ppc_previous_section, 0); ppc_set_current_section (ppc_previous_section); } /* pseudo-op: .pdata behaviour: predefined read only data section double word aligned errors: None warnings: None initial: .section .pdata "adr3" a - don't know -- maybe a misprint d - initialized data r - readable 3 - double word aligned (that would be 4 byte boundary) commentary: Tag index tables (also known as the function table) for exception handling, debugging, etc. */ static void ppc_pdata (int ignore ATTRIBUTE_UNUSED) { if (pdata_section == 0) { pdata_section = subseg_new (".pdata", 0); bfd_set_section_flags (stdoutput, pdata_section, (SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_READONLY | SEC_DATA )); bfd_set_section_alignment (stdoutput, pdata_section, 2); } else { pdata_section = subseg_new (".pdata", 0); } ppc_set_current_section (pdata_section); } /* pseudo-op: .ydata behaviour: predefined read only data section double word aligned errors: None warnings: None initial: .section .ydata "drw3" a - don't know -- maybe a misprint d - initialized data r - readable 3 - double word aligned (that would be 4 byte boundary) commentary: Tag tables (also known as the scope table) for exception handling, debugging, etc. */ static void ppc_ydata (int ignore ATTRIBUTE_UNUSED) { if (ydata_section == 0) { ydata_section = subseg_new (".ydata", 0); bfd_set_section_flags (stdoutput, ydata_section, (SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_READONLY | SEC_DATA )); bfd_set_section_alignment (stdoutput, ydata_section, 3); } else { ydata_section = subseg_new (".ydata", 0); } ppc_set_current_section (ydata_section); } /* pseudo-op: .reldata behaviour: predefined read write data section double word aligned (4-byte) FIXME: relocation is applied to it FIXME: what's the difference between this and .data? errors: None warnings: None initial: .section .reldata "drw3" d - initialized data r - readable w - writeable 3 - double word aligned (that would be 8 byte boundary) commentary: Like .data, but intended to hold data subject to relocation, such as function descriptors, etc. */ static void ppc_reldata (int ignore ATTRIBUTE_UNUSED) { if (reldata_section == 0) { reldata_section = subseg_new (".reldata", 0); bfd_set_section_flags (stdoutput, reldata_section, (SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_DATA)); bfd_set_section_alignment (stdoutput, reldata_section, 2); } else { reldata_section = subseg_new (".reldata", 0); } ppc_set_current_section (reldata_section); } /* pseudo-op: .rdata behaviour: predefined read only data section double word aligned errors: None warnings: None initial: .section .rdata "dr3" d - initialized data r - readable 3 - double word aligned (that would be 4 byte boundary) */ static void ppc_rdata (int ignore ATTRIBUTE_UNUSED) { if (rdata_section == 0) { rdata_section = subseg_new (".rdata", 0); bfd_set_section_flags (stdoutput, rdata_section, (SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_READONLY | SEC_DATA )); bfd_set_section_alignment (stdoutput, rdata_section, 2); } else { rdata_section = subseg_new (".rdata", 0); } ppc_set_current_section (rdata_section); } /* pseudo-op: .ualong behaviour: much like .int, with the exception that no alignment is performed. FIXME: test the alignment statement errors: None warnings: None */ static void ppc_ualong (int ignore ATTRIBUTE_UNUSED) { /* Try for long. */ cons (4); } /* pseudo-op: .znop behaviour: Issue a nop instruction Issue a IMAGE_REL_PPC_IFGLUE relocation against it, using the supplied symbol name. errors: None warnings: Missing symbol name */ static void ppc_znop (int ignore ATTRIBUTE_UNUSED) { unsigned long insn; const struct powerpc_opcode *opcode; expressionS ex; char *f; symbolS *sym; char *symbol_name; char c; char *name; unsigned int exp; flagword flags; asection *sec; /* Strip out the symbol name. */ symbol_name = input_line_pointer; c = get_symbol_end (); name = xmalloc (input_line_pointer - symbol_name + 1); strcpy (name, symbol_name); sym = symbol_find_or_make (name); *input_line_pointer = c; SKIP_WHITESPACE (); /* Look up the opcode in the hash table. */ opcode = (const struct powerpc_opcode *) hash_find (ppc_hash, "nop"); /* Stick in the nop. */ insn = opcode->opcode; /* Write out the instruction. */ f = frag_more (4); md_number_to_chars (f, insn, 4); fix_new (frag_now, f - frag_now->fr_literal, 4, sym, 0, 0, BFD_RELOC_16_GOT_PCREL); } /* pseudo-op: behaviour: errors: warnings: */ static void ppc_pe_comm (int lcomm) { char *name; char c; char *p; offsetT temp; symbolS *symbolP; offsetT align; name = input_line_pointer; c = get_symbol_end (); /* just after name is now '\0'. */ p = input_line_pointer; *p = c; SKIP_WHITESPACE (); if (*input_line_pointer != ',') { as_bad (_("Expected comma after symbol-name: rest of line ignored.")); ignore_rest_of_line (); return; } input_line_pointer++; /* skip ',' */ if ((temp = get_absolute_expression ()) < 0) { as_warn (_(".COMMon length (%ld.) <0! Ignored."), (long) temp); ignore_rest_of_line (); return; } if (! lcomm) { /* The third argument to .comm is the alignment. */ if (*input_line_pointer != ',') align = 3; else { ++input_line_pointer; align = get_absolute_expression (); if (align <= 0) { as_warn (_("ignoring bad alignment")); align = 3; } } } *p = 0; symbolP = symbol_find_or_make (name); *p = c; if (S_IS_DEFINED (symbolP) && ! S_IS_COMMON (symbolP)) { as_bad (_("Ignoring attempt to re-define symbol `%s'."), S_GET_NAME (symbolP)); ignore_rest_of_line (); return; } if (S_GET_VALUE (symbolP)) { if (S_GET_VALUE (symbolP) != (valueT) temp) as_bad (_("Length of .comm \"%s\" is already %ld. Not changed to %ld."), S_GET_NAME (symbolP), (long) S_GET_VALUE (symbolP), (long) temp); } else { S_SET_VALUE (symbolP, (valueT) temp); S_SET_EXTERNAL (symbolP); S_SET_SEGMENT (symbolP, bfd_com_section_ptr); } demand_empty_rest_of_line (); } /* * implement the .section pseudo op: * .section name {, "flags"} * ^ ^ * | +--- optional flags: 'b' for bss * | 'i' for info * +-- section name 'l' for lib * 'n' for noload * 'o' for over * 'w' for data * 'd' (apparently m88k for data) * 'x' for text * But if the argument is not a quoted string, treat it as a * subsegment number. * * FIXME: this is a copy of the section processing from obj-coff.c, with * additions/changes for the moto-pas assembler support. There are three * categories: * * FIXME: I just noticed this. This doesn't work at all really. It it * setting bits that bfd probably neither understands or uses. The * correct approach (?) will have to incorporate extra fields attached * to the section to hold the system specific stuff. (krk) * * Section Contents: * 'a' - unknown - referred to in documentation, but no definition supplied * 'c' - section has code * 'd' - section has initialized data * 'u' - section has uninitialized data * 'i' - section contains directives (info) * 'n' - section can be discarded * 'R' - remove section at link time * * Section Protection: * 'r' - section is readable * 'w' - section is writeable * 'x' - section is executable * 's' - section is sharable * * Section Alignment: * '0' - align to byte boundary * '1' - align to halfword undary * '2' - align to word boundary * '3' - align to doubleword boundary * '4' - align to quadword boundary * '5' - align to 32 byte boundary * '6' - align to 64 byte boundary * */ void ppc_pe_section (int ignore ATTRIBUTE_UNUSED) { /* Strip out the section name. */ char *section_name; char c; char *name; unsigned int exp; flagword flags; segT sec; int align; section_name = input_line_pointer; c = get_symbol_end (); name = xmalloc (input_line_pointer - section_name + 1); strcpy (name, section_name); *input_line_pointer = c; SKIP_WHITESPACE (); exp = 0; flags = SEC_NO_FLAGS; if (strcmp (name, ".idata$2") == 0) { align = 0; } else if (strcmp (name, ".idata$3") == 0) { align = 0; } else if (strcmp (name, ".idata$4") == 0) { align = 2; } else if (strcmp (name, ".idata$5") == 0) { align = 2; } else if (strcmp (name, ".idata$6") == 0) { align = 1; } else /* Default alignment to 16 byte boundary. */ align = 4; if (*input_line_pointer == ',') { ++input_line_pointer; SKIP_WHITESPACE (); if (*input_line_pointer != '"') exp = get_absolute_expression (); else { ++input_line_pointer; while (*input_line_pointer != '"' && ! is_end_of_line[(unsigned char) *input_line_pointer]) { switch (*input_line_pointer) { /* Section Contents */ case 'a': /* unknown */ as_bad (_("Unsupported section attribute -- 'a'")); break; case 'c': /* code section */ flags |= SEC_CODE; break; case 'd': /* section has initialized data */ flags |= SEC_DATA; break; case 'u': /* section has uninitialized data */ /* FIXME: This is IMAGE_SCN_CNT_UNINITIALIZED_DATA in winnt.h */ flags |= SEC_ROM; break; case 'i': /* section contains directives (info) */ /* FIXME: This is IMAGE_SCN_LNK_INFO in winnt.h */ flags |= SEC_HAS_CONTENTS; break; case 'n': /* section can be discarded */ flags &=~ SEC_LOAD; break; case 'R': /* Remove section at link time */ flags |= SEC_NEVER_LOAD; break; #if IFLICT_BRAIN_DAMAGE /* Section Protection */ case 'r': /* section is readable */ flags |= IMAGE_SCN_MEM_READ; break; case 'w': /* section is writeable */ flags |= IMAGE_SCN_MEM_WRITE; break; case 'x': /* section is executable */ flags |= IMAGE_SCN_MEM_EXECUTE; break; case 's': /* section is sharable */ flags |= IMAGE_SCN_MEM_SHARED; break; /* Section Alignment */ case '0': /* align to byte boundary */ flags |= IMAGE_SCN_ALIGN_1BYTES; align = 0; break; case '1': /* align to halfword boundary */ flags |= IMAGE_SCN_ALIGN_2BYTES; align = 1; break; case '2': /* align to word boundary */ flags |= IMAGE_SCN_ALIGN_4BYTES; align = 2; break; case '3': /* align to doubleword boundary */ flags |= IMAGE_SCN_ALIGN_8BYTES; align = 3; break; case '4': /* align to quadword boundary */ flags |= IMAGE_SCN_ALIGN_16BYTES; align = 4; break; case '5': /* align to 32 byte boundary */ flags |= IMAGE_SCN_ALIGN_32BYTES; align = 5; break; case '6': /* align to 64 byte boundary */ flags |= IMAGE_SCN_ALIGN_64BYTES; align = 6; break; #endif default: as_bad (_("unknown section attribute '%c'"), *input_line_pointer); break; } ++input_line_pointer; } if (*input_line_pointer == '"') ++input_line_pointer; } } sec = subseg_new (name, (subsegT) exp); ppc_set_current_section (sec); if (flags != SEC_NO_FLAGS) { if (! bfd_set_section_flags (stdoutput, sec, flags)) as_bad (_("error setting flags for \"%s\": %s"), bfd_section_name (stdoutput, sec), bfd_errmsg (bfd_get_error ())); } bfd_set_section_alignment (stdoutput, sec, align); } static void ppc_pe_function (int ignore ATTRIBUTE_UNUSED) { char *name; char endc; symbolS *ext_sym; name = input_line_pointer; endc = get_symbol_end (); ext_sym = symbol_find_or_make (name); *input_line_pointer = endc; S_SET_DATA_TYPE (ext_sym, DT_FCN << N_BTSHFT); SF_SET_FUNCTION (ext_sym); SF_SET_PROCESS (ext_sym); coff_add_linesym (ext_sym); demand_empty_rest_of_line (); } static void ppc_pe_tocd (int ignore ATTRIBUTE_UNUSED) { if (tocdata_section == 0) { tocdata_section = subseg_new (".tocd", 0); /* FIXME: section flags won't work. */ bfd_set_section_flags (stdoutput, tocdata_section, (SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_READONLY | SEC_DATA)); bfd_set_section_alignment (stdoutput, tocdata_section, 2); } else { rdata_section = subseg_new (".tocd", 0); } ppc_set_current_section (tocdata_section); demand_empty_rest_of_line (); } /* Don't adjust TOC relocs to use the section symbol. */ int ppc_pe_fix_adjustable (fixS *fix) { return fix->fx_r_type != BFD_RELOC_PPC_TOC16; } #endif #ifdef OBJ_XCOFF /* XCOFF specific symbol and file handling. */ /* Canonicalize the symbol name. We use the to force the suffix, if any, to use square brackets, and to be in upper case. */ char * ppc_canonicalize_symbol_name (char *name) { char *s; if (ppc_stab_symbol) return name; for (s = name; *s != '\0' && *s != '{' && *s != '['; s++) ; if (*s != '\0') { char brac; if (*s == '[') brac = ']'; else { *s = '['; brac = '}'; } for (s++; *s != '\0' && *s != brac; s++) *s = TOUPPER (*s); if (*s == '\0' || s[1] != '\0') as_bad (_("bad symbol suffix")); *s = ']'; } return name; } /* Set the class of a symbol based on the suffix, if any. This is called whenever a new symbol is created. */ void ppc_symbol_new_hook (symbolS *sym) { struct ppc_tc_sy *tc; const char *s; tc = symbol_get_tc (sym); tc->next = NULL; tc->output = 0; tc->class = -1; tc->real_name = NULL; tc->subseg = 0; tc->align = 0; tc->size = NULL; tc->within = NULL; if (ppc_stab_symbol) return; s = strchr (S_GET_NAME (sym), '['); if (s == (const char *) NULL) { /* There is no suffix. */ return; } ++s; switch (s[0]) { case 'B': if (strcmp (s, "BS]") == 0) tc->class = XMC_BS; break; case 'D': if (strcmp (s, "DB]") == 0) tc->class = XMC_DB; else if (strcmp (s, "DS]") == 0) tc->class = XMC_DS; break; case 'G': if (strcmp (s, "GL]") == 0) tc->class = XMC_GL; break; case 'P': if (strcmp (s, "PR]") == 0) tc->class = XMC_PR; break; case 'R': if (strcmp (s, "RO]") == 0) tc->class = XMC_RO; else if (strcmp (s, "RW]") == 0) tc->class = XMC_RW; break; case 'S': if (strcmp (s, "SV]") == 0) tc->class = XMC_SV; break; case 'T': if (strcmp (s, "TC]") == 0) tc->class = XMC_TC; else if (strcmp (s, "TI]") == 0) tc->class = XMC_TI; else if (strcmp (s, "TB]") == 0) tc->class = XMC_TB; else if (strcmp (s, "TC0]") == 0 || strcmp (s, "T0]") == 0) tc->class = XMC_TC0; break; case 'U': if (strcmp (s, "UA]") == 0) tc->class = XMC_UA; else if (strcmp (s, "UC]") == 0) tc->class = XMC_UC; break; case 'X': if (strcmp (s, "XO]") == 0) tc->class = XMC_XO; break; } if (tc->class == -1) as_bad (_("Unrecognized symbol suffix")); } /* Set the class of a label based on where it is defined. This handles symbols without suffixes. Also, move the symbol so that it follows the csect symbol. */ void ppc_frob_label (symbolS *sym) { if (ppc_current_csect != (symbolS *) NULL) { if (symbol_get_tc (sym)->class == -1) symbol_get_tc (sym)->class = symbol_get_tc (ppc_current_csect)->class; symbol_remove (sym, &symbol_rootP, &symbol_lastP); symbol_append (sym, symbol_get_tc (ppc_current_csect)->within, &symbol_rootP, &symbol_lastP); symbol_get_tc (ppc_current_csect)->within = sym; } #ifdef OBJ_ELF dwarf2_emit_label (sym); #endif } /* This variable is set by ppc_frob_symbol if any absolute symbols are seen. It tells ppc_adjust_symtab whether it needs to look through the symbols. */ static bfd_boolean ppc_saw_abs; /* Change the name of a symbol just before writing it out. Set the real name if the .rename pseudo-op was used. Otherwise, remove any class suffix. Return 1 if the symbol should not be included in the symbol table. */ int ppc_frob_symbol (symbolS *sym) { static symbolS *ppc_last_function; static symbolS *set_end; /* Discard symbols that should not be included in the output symbol table. */ if (! symbol_used_in_reloc_p (sym) && ((symbol_get_bfdsym (sym)->flags & BSF_SECTION_SYM) != 0 || (! (S_IS_EXTERNAL (sym) || S_IS_WEAK (sym)) && ! symbol_get_tc (sym)->output && S_GET_STORAGE_CLASS (sym) != C_FILE))) return 1; /* This one will disappear anyway. Don't make a csect sym for it. */ if (sym == abs_section_sym) return 1; if (symbol_get_tc (sym)->real_name != (char *) NULL) S_SET_NAME (sym, symbol_get_tc (sym)->real_name); else { const char *name; const char *s; name = S_GET_NAME (sym); s = strchr (name, '['); if (s != (char *) NULL) { unsigned int len; char *snew; len = s - name; snew = xmalloc (len + 1); memcpy (snew, name, len); snew[len] = '\0'; S_SET_NAME (sym, snew); } } if (set_end != (symbolS *) NULL) { SA_SET_SYM_ENDNDX (set_end, sym); set_end = NULL; } if (SF_GET_FUNCTION (sym)) { if (ppc_last_function != (symbolS *) NULL) as_bad (_("two .function pseudo-ops with no intervening .ef")); ppc_last_function = sym; if (symbol_get_tc (sym)->size != (symbolS *) NULL) { resolve_symbol_value (symbol_get_tc (sym)->size); SA_SET_SYM_FSIZE (sym, (long) S_GET_VALUE (symbol_get_tc (sym)->size)); } } else if (S_GET_STORAGE_CLASS (sym) == C_FCN && strcmp (S_GET_NAME (sym), ".ef") == 0) { if (ppc_last_function == (symbolS *) NULL) as_bad (_(".ef with no preceding .function")); else { set_end = ppc_last_function; ppc_last_function = NULL; /* We don't have a C_EFCN symbol, but we need to force the COFF backend to believe that it has seen one. */ coff_last_function = NULL; } } if (! (S_IS_EXTERNAL (sym) || S_IS_WEAK (sym)) && (symbol_get_bfdsym (sym)->flags & BSF_SECTION_SYM) == 0 && S_GET_STORAGE_CLASS (sym) != C_FILE && S_GET_STORAGE_CLASS (sym) != C_FCN && S_GET_STORAGE_CLASS (sym) != C_BLOCK && S_GET_STORAGE_CLASS (sym) != C_BSTAT && S_GET_STORAGE_CLASS (sym) != C_ESTAT && S_GET_STORAGE_CLASS (sym) != C_BINCL && S_GET_STORAGE_CLASS (sym) != C_EINCL && S_GET_SEGMENT (sym) != ppc_coff_debug_section) S_SET_STORAGE_CLASS (sym, C_HIDEXT); if (S_GET_STORAGE_CLASS (sym) == C_EXT || S_GET_STORAGE_CLASS (sym) == C_HIDEXT) { int i; union internal_auxent *a; /* Create a csect aux. */ i = S_GET_NUMBER_AUXILIARY (sym); S_SET_NUMBER_AUXILIARY (sym, i + 1); a = &coffsymbol (symbol_get_bfdsym (sym))->native[i + 1].u.auxent; if (symbol_get_tc (sym)->class == XMC_TC0) { /* This is the TOC table. */ know (strcmp (S_GET_NAME (sym), "TOC") == 0); a->x_csect.x_scnlen.l = 0; a->x_csect.x_smtyp = (2 << 3) | XTY_SD; } else if (symbol_get_tc (sym)->subseg != 0) { /* This is a csect symbol. x_scnlen is the size of the csect. */ if (symbol_get_tc (sym)->next == (symbolS *) NULL) a->x_csect.x_scnlen.l = (bfd_section_size (stdoutput, S_GET_SEGMENT (sym)) - S_GET_VALUE (sym)); else { resolve_symbol_value (symbol_get_tc (sym)->next); a->x_csect.x_scnlen.l = (S_GET_VALUE (symbol_get_tc (sym)->next) - S_GET_VALUE (sym)); } a->x_csect.x_smtyp = (symbol_get_tc (sym)->align << 3) | XTY_SD; } else if (S_GET_SEGMENT (sym) == bss_section) { /* This is a common symbol. */ a->x_csect.x_scnlen.l = symbol_get_frag (sym)->fr_offset; a->x_csect.x_smtyp = (symbol_get_tc (sym)->align << 3) | XTY_CM; if (S_IS_EXTERNAL (sym)) symbol_get_tc (sym)->class = XMC_RW; else symbol_get_tc (sym)->class = XMC_BS; } else if (S_GET_SEGMENT (sym) == absolute_section) { /* This is an absolute symbol. The csect will be created by ppc_adjust_symtab. */ ppc_saw_abs = TRUE; a->x_csect.x_smtyp = XTY_LD; if (symbol_get_tc (sym)->class == -1) symbol_get_tc (sym)->class = XMC_XO; } else if (! S_IS_DEFINED (sym)) { /* This is an external symbol. */ a->x_csect.x_scnlen.l = 0; a->x_csect.x_smtyp = XTY_ER; } else if (symbol_get_tc (sym)->class == XMC_TC) { symbolS *next; /* This is a TOC definition. x_scnlen is the size of the TOC entry. */ next = symbol_next (sym); while (symbol_get_tc (next)->class == XMC_TC0) next = symbol_next (next); if (next == (symbolS *) NULL || symbol_get_tc (next)->class != XMC_TC) { if (ppc_after_toc_frag == (fragS *) NULL) a->x_csect.x_scnlen.l = (bfd_section_size (stdoutput, data_section) - S_GET_VALUE (sym)); else a->x_csect.x_scnlen.l = (ppc_after_toc_frag->fr_address - S_GET_VALUE (sym)); } else { resolve_symbol_value (next); a->x_csect.x_scnlen.l = (S_GET_VALUE (next) - S_GET_VALUE (sym)); } a->x_csect.x_smtyp = (2 << 3) | XTY_SD; } else { symbolS *csect; /* This is a normal symbol definition. x_scnlen is the symbol index of the containing csect. */ if (S_GET_SEGMENT (sym) == text_section) csect = ppc_text_csects; else if (S_GET_SEGMENT (sym) == data_section) csect = ppc_data_csects; else abort (); /* Skip the initial dummy symbol. */ csect = symbol_get_tc (csect)->next; if (csect == (symbolS *) NULL) { as_warn (_("warning: symbol %s has no csect"), S_GET_NAME (sym)); a->x_csect.x_scnlen.l = 0; } else { while (symbol_get_tc (csect)->next != (symbolS *) NULL) { resolve_symbol_value (symbol_get_tc (csect)->next); if (S_GET_VALUE (symbol_get_tc (csect)->next) > S_GET_VALUE (sym)) break; csect = symbol_get_tc (csect)->next; } a->x_csect.x_scnlen.p = coffsymbol (symbol_get_bfdsym (csect))->native; coffsymbol (symbol_get_bfdsym (sym))->native[i + 1].fix_scnlen = 1; } a->x_csect.x_smtyp = XTY_LD; } a->x_csect.x_parmhash = 0; a->x_csect.x_snhash = 0; if (symbol_get_tc (sym)->class == -1) a->x_csect.x_smclas = XMC_PR; else a->x_csect.x_smclas = symbol_get_tc (sym)->class; a->x_csect.x_stab = 0; a->x_csect.x_snstab = 0; /* Don't let the COFF backend resort these symbols. */ symbol_get_bfdsym (sym)->flags |= BSF_NOT_AT_END; } else if (S_GET_STORAGE_CLASS (sym) == C_BSTAT) { /* We want the value to be the symbol index of the referenced csect symbol. BFD will do that for us if we set the right flags. */ asymbol *bsym = symbol_get_bfdsym (symbol_get_tc (sym)->within); combined_entry_type *c = coffsymbol (bsym)->native; S_SET_VALUE (sym, (valueT) (size_t) c); coffsymbol (symbol_get_bfdsym (sym))->native->fix_value = 1; } else if (S_GET_STORAGE_CLASS (sym) == C_STSYM) { symbolS *block; symbolS *csect; /* The value is the offset from the enclosing csect. */ block = symbol_get_tc (sym)->within; csect = symbol_get_tc (block)->within; resolve_symbol_value (csect); S_SET_VALUE (sym, S_GET_VALUE (sym) - S_GET_VALUE (csect)); } else if (S_GET_STORAGE_CLASS (sym) == C_BINCL || S_GET_STORAGE_CLASS (sym) == C_EINCL) { /* We want the value to be a file offset into the line numbers. BFD will do that for us if we set the right flags. We have already set the value correctly. */ coffsymbol (symbol_get_bfdsym (sym))->native->fix_line = 1; } return 0; } /* Adjust the symbol table. This creates csect symbols for all absolute symbols. */ void ppc_adjust_symtab (void) { symbolS *sym; if (! ppc_saw_abs) return; for (sym = symbol_rootP; sym != NULL; sym = symbol_next (sym)) { symbolS *csect; int i; union internal_auxent *a; if (S_GET_SEGMENT (sym) != absolute_section) continue; csect = symbol_create (".abs[XO]", absolute_section, S_GET_VALUE (sym), &zero_address_frag); symbol_get_bfdsym (csect)->value = S_GET_VALUE (sym); S_SET_STORAGE_CLASS (csect, C_HIDEXT); i = S_GET_NUMBER_AUXILIARY (csect); S_SET_NUMBER_AUXILIARY (csect, i + 1); a = &coffsymbol (symbol_get_bfdsym (csect))->native[i + 1].u.auxent; a->x_csect.x_scnlen.l = 0; a->x_csect.x_smtyp = XTY_SD; a->x_csect.x_parmhash = 0; a->x_csect.x_snhash = 0; a->x_csect.x_smclas = XMC_XO; a->x_csect.x_stab = 0; a->x_csect.x_snstab = 0; symbol_insert (csect, sym, &symbol_rootP, &symbol_lastP); i = S_GET_NUMBER_AUXILIARY (sym); a = &coffsymbol (symbol_get_bfdsym (sym))->native[i].u.auxent; a->x_csect.x_scnlen.p = coffsymbol (symbol_get_bfdsym (csect))->native; coffsymbol (symbol_get_bfdsym (sym))->native[i].fix_scnlen = 1; } ppc_saw_abs = FALSE; } /* Set the VMA for a section. This is called on all the sections in turn. */ void ppc_frob_section (asection *sec) { static bfd_vma vma = 0; vma = md_section_align (sec, vma); bfd_set_section_vma (stdoutput, sec, vma); vma += bfd_section_size (stdoutput, sec); } #endif /* OBJ_XCOFF */ /* Turn a string in input_line_pointer into a floating point constant of type TYPE, and store the appropriate bytes in *LITP. The number of LITTLENUMS emitted is stored in *SIZEP. An error message is returned, or NULL on OK. */ char * md_atof (int type, char *litp, int *sizep) { int prec; LITTLENUM_TYPE words[4]; char *t; int i; switch (type) { case 'f': prec = 2; break; case 'd': prec = 4; break; default: *sizep = 0; return _("bad call to md_atof"); } t = atof_ieee (input_line_pointer, type, words); if (t) input_line_pointer = t; *sizep = prec * 2; if (target_big_endian) { for (i = 0; i < prec; i++) { md_number_to_chars (litp, (valueT) words[i], 2); litp += 2; } } else { for (i = prec - 1; i >= 0; i--) { md_number_to_chars (litp, (valueT) words[i], 2); litp += 2; } } return NULL; } /* Write a value out to the object file, using the appropriate endianness. */ void md_number_to_chars (char *buf, valueT val, int n) { if (target_big_endian) number_to_chars_bigendian (buf, val, n); else number_to_chars_littleendian (buf, val, n); } /* Align a section (I don't know why this is machine dependent). */ valueT md_section_align (asection *seg ATTRIBUTE_UNUSED, valueT addr) { #ifdef OBJ_ELF return addr; #else int align = bfd_get_section_alignment (stdoutput, seg); return ((addr + (1 << align) - 1) & (-1 << align)); #endif } /* We don't have any form of relaxing. */ int md_estimate_size_before_relax (fragS *fragp ATTRIBUTE_UNUSED, asection *seg ATTRIBUTE_UNUSED) { abort (); return 0; } /* Convert a machine dependent frag. We never generate these. */ void md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, asection *sec ATTRIBUTE_UNUSED, fragS *fragp ATTRIBUTE_UNUSED) { abort (); } /* We have no need to default values of symbols. */ symbolS * md_undefined_symbol (char *name ATTRIBUTE_UNUSED) { return 0; } /* Functions concerning relocs. */ /* The location from which a PC relative jump should be calculated, given a PC relative reloc. */ long md_pcrel_from_section (fixS *fixp, segT sec ATTRIBUTE_UNUSED) { return fixp->fx_frag->fr_address + fixp->fx_where; } #ifdef OBJ_XCOFF /* This is called to see whether a fixup should be adjusted to use a section symbol. We take the opportunity to change a fixup against a symbol in the TOC subsegment into a reloc against the corresponding .tc symbol. */ int ppc_fix_adjustable (fixS *fix) { valueT val = resolve_symbol_value (fix->fx_addsy); segT symseg = S_GET_SEGMENT (fix->fx_addsy); TC_SYMFIELD_TYPE *tc; if (symseg == absolute_section) return 0; if (ppc_toc_csect != (symbolS *) NULL && fix->fx_addsy != ppc_toc_csect && symseg == data_section && val >= ppc_toc_frag->fr_address && (ppc_after_toc_frag == (fragS *) NULL || val < ppc_after_toc_frag->fr_address)) { symbolS *sy; for (sy = symbol_next (ppc_toc_csect); sy != (symbolS *) NULL; sy = symbol_next (sy)) { TC_SYMFIELD_TYPE *sy_tc = symbol_get_tc (sy); if (sy_tc->class == XMC_TC0) continue; if (sy_tc->class != XMC_TC) break; if (val == resolve_symbol_value (sy)) { fix->fx_addsy = sy; fix->fx_addnumber = val - ppc_toc_frag->fr_address; return 0; } } as_bad_where (fix->fx_file, fix->fx_line, _("symbol in .toc does not match any .tc")); } /* Possibly adjust the reloc to be against the csect. */ tc = symbol_get_tc (fix->fx_addsy); if (tc->subseg == 0 && tc->class != XMC_TC0 && tc->class != XMC_TC && symseg != bss_section /* Don't adjust if this is a reloc in the toc section. */ && (symseg != data_section || ppc_toc_csect == NULL || val < ppc_toc_frag->fr_address || (ppc_after_toc_frag != NULL && val >= ppc_after_toc_frag->fr_address))) { symbolS *csect; symbolS *next_csect; if (symseg == text_section) csect = ppc_text_csects; else if (symseg == data_section) csect = ppc_data_csects; else abort (); /* Skip the initial dummy symbol. */ csect = symbol_get_tc (csect)->next; if (csect != (symbolS *) NULL) { while ((next_csect = symbol_get_tc (csect)->next) != (symbolS *) NULL && (symbol_get_frag (next_csect)->fr_address <= val)) { /* If the csect address equals the symbol value, then we have to look through the full symbol table to see whether this is the csect we want. Note that we will only get here if the csect has zero length. */ if (symbol_get_frag (csect)->fr_address == val && S_GET_VALUE (csect) == val) { symbolS *scan; for (scan = symbol_next (csect); scan != NULL; scan = symbol_next (scan)) { if (symbol_get_tc (scan)->subseg != 0) break; if (scan == fix->fx_addsy) break; } /* If we found the symbol before the next csect symbol, then this is the csect we want. */ if (scan == fix->fx_addsy) break; } csect = next_csect; } fix->fx_offset += val - symbol_get_frag (csect)->fr_address; fix->fx_addsy = csect; } return 0; } /* Adjust a reloc against a .lcomm symbol to be against the base .lcomm. */ if (symseg == bss_section && ! S_IS_EXTERNAL (fix->fx_addsy)) { symbolS *sy = symbol_get_frag (fix->fx_addsy)->fr_symbol; fix->fx_offset += val - resolve_symbol_value (sy); fix->fx_addsy = sy; } return 0; } /* A reloc from one csect to another must be kept. The assembler will, of course, keep relocs between sections, and it will keep absolute relocs, but we need to force it to keep PC relative relocs between two csects in the same section. */ int ppc_force_relocation (fixS *fix) { /* At this point fix->fx_addsy should already have been converted to a csect symbol. If the csect does not include the fragment, then we need to force the relocation. */ if (fix->fx_pcrel && fix->fx_addsy != NULL && symbol_get_tc (fix->fx_addsy)->subseg != 0 && ((symbol_get_frag (fix->fx_addsy)->fr_address > fix->fx_frag->fr_address) || (symbol_get_tc (fix->fx_addsy)->next != NULL && (symbol_get_frag (symbol_get_tc (fix->fx_addsy)->next)->fr_address <= fix->fx_frag->fr_address)))) return 1; return generic_force_reloc (fix); } #endif /* OBJ_XCOFF */ #ifdef OBJ_ELF /* If this function returns non-zero, it guarantees that a relocation will be emitted for a fixup. */ int ppc_force_relocation (fixS *fix) { /* Branch prediction relocations must force a relocation, as must the vtable description relocs. */ switch (fix->fx_r_type) { case BFD_RELOC_PPC_B16_BRTAKEN: case BFD_RELOC_PPC_B16_BRNTAKEN: case BFD_RELOC_PPC_BA16_BRTAKEN: case BFD_RELOC_PPC_BA16_BRNTAKEN: case BFD_RELOC_24_PLT_PCREL: case BFD_RELOC_PPC64_TOC: return 1; default: break; } if (fix->fx_r_type >= BFD_RELOC_PPC_TLS && fix->fx_r_type <= BFD_RELOC_PPC64_DTPREL16_HIGHESTA) return 1; return generic_force_reloc (fix); } int ppc_fix_adjustable (fixS *fix) { return (fix->fx_r_type != BFD_RELOC_16_GOTOFF && fix->fx_r_type != BFD_RELOC_LO16_GOTOFF && fix->fx_r_type != BFD_RELOC_HI16_GOTOFF && fix->fx_r_type != BFD_RELOC_HI16_S_GOTOFF && fix->fx_r_type != BFD_RELOC_GPREL16 && fix->fx_r_type != BFD_RELOC_VTABLE_INHERIT && fix->fx_r_type != BFD_RELOC_VTABLE_ENTRY && !(fix->fx_r_type >= BFD_RELOC_PPC_TLS && fix->fx_r_type <= BFD_RELOC_PPC64_DTPREL16_HIGHESTA)); } #endif /* Implement HANDLE_ALIGN. This writes the NOP pattern into an rs_align_code frag. */ void ppc_handle_align (struct frag *fragP) { valueT count = (fragP->fr_next->fr_address - (fragP->fr_address + fragP->fr_fix)); if (count != 0 && (count & 3) == 0) { char *dest = fragP->fr_literal + fragP->fr_fix; fragP->fr_var = 4; md_number_to_chars (dest, 0x60000000, 4); if ((ppc_cpu & PPC_OPCODE_POWER6) != 0) { /* For power6, we want the last nop to be a group terminating one, "ori 1,1,0". Do this by inserting an rs_fill frag immediately after this one, with its address set to the last nop location. This will automatically reduce the number of nops in the current frag by one. */ if (count > 4) { struct frag *group_nop = xmalloc (SIZEOF_STRUCT_FRAG + 4); memcpy (group_nop, fragP, SIZEOF_STRUCT_FRAG); group_nop->fr_address = group_nop->fr_next->fr_address - 4; group_nop->fr_fix = 0; group_nop->fr_offset = 1; group_nop->fr_type = rs_fill; fragP->fr_next = group_nop; dest = group_nop->fr_literal; } md_number_to_chars (dest, 0x60210000, 4); } } } /* Apply a fixup to the object code. This is called for all the fixups we generated by the call to fix_new_exp, above. In the call above we used a reloc code which was the largest legal reloc code plus the operand index. Here we undo that to recover the operand index. At this point all symbol values should be fully resolved, and we attempt to completely resolve the reloc. If we can not do that, we determine the correct reloc code and put it back in the fixup. */ void md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) { valueT value = * valP; #ifdef OBJ_ELF if (fixP->fx_addsy != NULL) { /* Hack around bfd_install_relocation brain damage. */ if (fixP->fx_pcrel) value += fixP->fx_frag->fr_address + fixP->fx_where; } else fixP->fx_done = 1; #else /* FIXME FIXME FIXME: The value we are passed in *valP includes the symbol values. If we are doing this relocation the code in write.c is going to call bfd_install_relocation, which is also going to use the symbol value. That means that if the reloc is fully resolved we want to use *valP since bfd_install_relocation is not being used. However, if the reloc is not fully resolved we do not want to use *valP, and must use fx_offset instead. However, if the reloc is PC relative, we do want to use *valP since it includes the result of md_pcrel_from. This is confusing. */ if (fixP->fx_addsy == (symbolS *) NULL) fixP->fx_done = 1; else if (fixP->fx_pcrel) ; else value = fixP->fx_offset; #endif if (fixP->fx_subsy != (symbolS *) NULL) { /* We can't actually support subtracting a symbol. */ as_bad_where (fixP->fx_file, fixP->fx_line, _("expression too complex")); } if ((int) fixP->fx_r_type >= (int) BFD_RELOC_UNUSED) { int opindex; const struct powerpc_operand *operand; char *where; unsigned long insn; opindex = (int) fixP->fx_r_type - (int) BFD_RELOC_UNUSED; operand = &powerpc_operands[opindex]; #ifdef OBJ_XCOFF /* An instruction like `lwz 9,sym(30)' when `sym' is not a TOC symbol does not generate a reloc. It uses the offset of `sym' within its csect. Other usages, such as `.long sym', generate relocs. This is the documented behaviour of non-TOC symbols. */ if ((operand->flags & PPC_OPERAND_PARENS) != 0 && (operand->bitm & 0xfff0) == 0xfff0 && operand->shift == 0 && (operand->insert == NULL || ppc_obj64) && fixP->fx_addsy != NULL && symbol_get_tc (fixP->fx_addsy)->subseg != 0 && symbol_get_tc (fixP->fx_addsy)->class != XMC_TC && symbol_get_tc (fixP->fx_addsy)->class != XMC_TC0 && S_GET_SEGMENT (fixP->fx_addsy) != bss_section) { value = fixP->fx_offset; fixP->fx_done = 1; } #endif /* Fetch the instruction, insert the fully resolved operand value, and stuff the instruction back again. */ where = fixP->fx_frag->fr_literal + fixP->fx_where; if (target_big_endian) insn = bfd_getb32 ((unsigned char *) where); else insn = bfd_getl32 ((unsigned char *) where); insn = ppc_insert_operand (insn, operand, (offsetT) value, fixP->fx_file, fixP->fx_line); if (target_big_endian) bfd_putb32 ((bfd_vma) insn, (unsigned char *) where); else bfd_putl32 ((bfd_vma) insn, (unsigned char *) where); if (fixP->fx_done) /* Nothing else to do here. */ return; assert (fixP->fx_addsy != NULL); /* Determine a BFD reloc value based on the operand information. We are only prepared to turn a few of the operands into relocs. */ if ((operand->flags & PPC_OPERAND_RELATIVE) != 0 && operand->bitm == 0x3fffffc && operand->shift == 0) fixP->fx_r_type = BFD_RELOC_PPC_B26; else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0 && operand->bitm == 0xfffc && operand->shift == 0) { fixP->fx_r_type = BFD_RELOC_PPC_B16; #ifdef OBJ_XCOFF fixP->fx_size = 2; if (target_big_endian) fixP->fx_where += 2; #endif } else if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0 && operand->bitm == 0x3fffffc && operand->shift == 0) fixP->fx_r_type = BFD_RELOC_PPC_BA26; else if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0 && operand->bitm == 0xfffc && operand->shift == 0) { fixP->fx_r_type = BFD_RELOC_PPC_BA16; #ifdef OBJ_XCOFF fixP->fx_size = 2; if (target_big_endian) fixP->fx_where += 2; #endif } #if defined (OBJ_XCOFF) || defined (OBJ_ELF) else if ((operand->flags & PPC_OPERAND_PARENS) != 0 && (operand->bitm & 0xfff0) == 0xfff0 && operand->shift == 0) { if (ppc_is_toc_sym (fixP->fx_addsy)) { fixP->fx_r_type = BFD_RELOC_PPC_TOC16; #ifdef OBJ_ELF if (ppc_obj64 && (operand->flags & PPC_OPERAND_DS) != 0) fixP->fx_r_type = BFD_RELOC_PPC64_TOC16_DS; #endif } else { fixP->fx_r_type = BFD_RELOC_16; #ifdef OBJ_ELF if (ppc_obj64 && (operand->flags & PPC_OPERAND_DS) != 0) fixP->fx_r_type = BFD_RELOC_PPC64_ADDR16_DS; #endif } fixP->fx_size = 2; if (target_big_endian) fixP->fx_where += 2; } #endif /* defined (OBJ_XCOFF) || defined (OBJ_ELF) */ else { char *sfile; unsigned int sline; /* Use expr_symbol_where to see if this is an expression symbol. */ if (expr_symbol_where (fixP->fx_addsy, &sfile, &sline)) as_bad_where (fixP->fx_file, fixP->fx_line, _("unresolved expression that must be resolved")); else as_bad_where (fixP->fx_file, fixP->fx_line, _("unsupported relocation against %s"), S_GET_NAME (fixP->fx_addsy)); fixP->fx_done = 1; return; } } else { #ifdef OBJ_ELF ppc_elf_validate_fix (fixP, seg); #endif switch (fixP->fx_r_type) { case BFD_RELOC_CTOR: if (ppc_obj64) goto ctor64; /* fall through */ case BFD_RELOC_32: if (fixP->fx_pcrel) fixP->fx_r_type = BFD_RELOC_32_PCREL; /* fall through */ case BFD_RELOC_RVA: case BFD_RELOC_32_PCREL: case BFD_RELOC_PPC_EMB_NADDR32: md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where, value, 4); break; case BFD_RELOC_64: ctor64: if (fixP->fx_pcrel) fixP->fx_r_type = BFD_RELOC_64_PCREL; /* fall through */ case BFD_RELOC_64_PCREL: md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where, value, 8); break; case BFD_RELOC_GPREL16: case BFD_RELOC_16_GOT_PCREL: case BFD_RELOC_16_GOTOFF: case BFD_RELOC_LO16_GOTOFF: case BFD_RELOC_HI16_GOTOFF: case BFD_RELOC_HI16_S_GOTOFF: case BFD_RELOC_16_BASEREL: case BFD_RELOC_LO16_BASEREL: case BFD_RELOC_HI16_BASEREL: case BFD_RELOC_HI16_S_BASEREL: case BFD_RELOC_PPC_EMB_NADDR16: case BFD_RELOC_PPC_EMB_NADDR16_LO: case BFD_RELOC_PPC_EMB_NADDR16_HI: case BFD_RELOC_PPC_EMB_NADDR16_HA: case BFD_RELOC_PPC_EMB_SDAI16: case BFD_RELOC_PPC_EMB_SDA2REL: case BFD_RELOC_PPC_EMB_SDA2I16: case BFD_RELOC_PPC_EMB_RELSEC16: case BFD_RELOC_PPC_EMB_RELST_LO: case BFD_RELOC_PPC_EMB_RELST_HI: case BFD_RELOC_PPC_EMB_RELST_HA: case BFD_RELOC_PPC_EMB_RELSDA: case BFD_RELOC_PPC_TOC16: #ifdef OBJ_ELF case BFD_RELOC_PPC64_TOC16_LO: case BFD_RELOC_PPC64_TOC16_HI: case BFD_RELOC_PPC64_TOC16_HA: #endif if (fixP->fx_pcrel) { if (fixP->fx_addsy != NULL) as_bad_where (fixP->fx_file, fixP->fx_line, _("cannot emit PC relative %s relocation against %s"), bfd_get_reloc_code_name (fixP->fx_r_type), S_GET_NAME (fixP->fx_addsy)); else as_bad_where (fixP->fx_file, fixP->fx_line, _("cannot emit PC relative %s relocation"), bfd_get_reloc_code_name (fixP->fx_r_type)); } md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where, value, 2); break; case BFD_RELOC_16: if (fixP->fx_pcrel) fixP->fx_r_type = BFD_RELOC_16_PCREL; /* fall through */ case BFD_RELOC_16_PCREL: md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where, value, 2); break; case BFD_RELOC_LO16: if (fixP->fx_pcrel) fixP->fx_r_type = BFD_RELOC_LO16_PCREL; /* fall through */ case BFD_RELOC_LO16_PCREL: md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where, value, 2); break; /* This case happens when you write, for example, lis %r3,(L1-L2)@ha where L1 and L2 are defined later. */ case BFD_RELOC_HI16: if (fixP->fx_pcrel) fixP->fx_r_type = BFD_RELOC_HI16_PCREL; /* fall through */ case BFD_RELOC_HI16_PCREL: md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where, PPC_HI (value), 2); break; case BFD_RELOC_HI16_S: if (fixP->fx_pcrel) fixP->fx_r_type = BFD_RELOC_HI16_S_PCREL; /* fall through */ case BFD_RELOC_HI16_S_PCREL: md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where, PPC_HA (value), 2); break; #ifdef OBJ_ELF case BFD_RELOC_PPC64_HIGHER: if (fixP->fx_pcrel) abort (); md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where, PPC_HIGHER (value), 2); break; case BFD_RELOC_PPC64_HIGHER_S: if (fixP->fx_pcrel) abort (); md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where, PPC_HIGHERA (value), 2); break; case BFD_RELOC_PPC64_HIGHEST: if (fixP->fx_pcrel) abort (); md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where, PPC_HIGHEST (value), 2); break; case BFD_RELOC_PPC64_HIGHEST_S: if (fixP->fx_pcrel) abort (); md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where, PPC_HIGHESTA (value), 2); break; case BFD_RELOC_PPC64_ADDR16_DS: case BFD_RELOC_PPC64_ADDR16_LO_DS: case BFD_RELOC_PPC64_GOT16_DS: case BFD_RELOC_PPC64_GOT16_LO_DS: case BFD_RELOC_PPC64_PLT16_LO_DS: case BFD_RELOC_PPC64_SECTOFF_DS: case BFD_RELOC_PPC64_SECTOFF_LO_DS: case BFD_RELOC_PPC64_TOC16_DS: case BFD_RELOC_PPC64_TOC16_LO_DS: case BFD_RELOC_PPC64_PLTGOT16_DS: case BFD_RELOC_PPC64_PLTGOT16_LO_DS: if (fixP->fx_pcrel) abort (); { char *where = fixP->fx_frag->fr_literal + fixP->fx_where; unsigned long val, mask; if (target_big_endian) val = bfd_getb32 (where - 2); else val = bfd_getl32 (where); mask = 0xfffc; /* lq insns reserve the four lsbs. */ if ((ppc_cpu & PPC_OPCODE_POWER4) != 0 && (val & (0x3f << 26)) == (56u << 26)) mask = 0xfff0; val |= value & mask; if (target_big_endian) bfd_putb16 ((bfd_vma) val, where); else bfd_putl16 ((bfd_vma) val, where); } break; case BFD_RELOC_PPC_B16_BRTAKEN: case BFD_RELOC_PPC_B16_BRNTAKEN: case BFD_RELOC_PPC_BA16_BRTAKEN: case BFD_RELOC_PPC_BA16_BRNTAKEN: break; case BFD_RELOC_PPC_TLS: case BFD_RELOC_PPC_TLSLD: case BFD_RELOC_PPC_TLSGD: break; case BFD_RELOC_PPC_DTPMOD: case BFD_RELOC_PPC_TPREL16: case BFD_RELOC_PPC_TPREL16_LO: case BFD_RELOC_PPC_TPREL16_HI: case BFD_RELOC_PPC_TPREL16_HA: case BFD_RELOC_PPC_TPREL: case BFD_RELOC_PPC_DTPREL16: case BFD_RELOC_PPC_DTPREL16_LO: case BFD_RELOC_PPC_DTPREL16_HI: case BFD_RELOC_PPC_DTPREL16_HA: case BFD_RELOC_PPC_DTPREL: case BFD_RELOC_PPC_GOT_TLSGD16: case BFD_RELOC_PPC_GOT_TLSGD16_LO: case BFD_RELOC_PPC_GOT_TLSGD16_HI: case BFD_RELOC_PPC_GOT_TLSGD16_HA: case BFD_RELOC_PPC_GOT_TLSLD16: case BFD_RELOC_PPC_GOT_TLSLD16_LO: case BFD_RELOC_PPC_GOT_TLSLD16_HI: case BFD_RELOC_PPC_GOT_TLSLD16_HA: case BFD_RELOC_PPC_GOT_TPREL16: case BFD_RELOC_PPC_GOT_TPREL16_LO: case BFD_RELOC_PPC_GOT_TPREL16_HI: case BFD_RELOC_PPC_GOT_TPREL16_HA: case BFD_RELOC_PPC_GOT_DTPREL16: case BFD_RELOC_PPC_GOT_DTPREL16_LO: case BFD_RELOC_PPC_GOT_DTPREL16_HI: case BFD_RELOC_PPC_GOT_DTPREL16_HA: case BFD_RELOC_PPC64_TPREL16_DS: case BFD_RELOC_PPC64_TPREL16_LO_DS: case BFD_RELOC_PPC64_TPREL16_HIGHER: case BFD_RELOC_PPC64_TPREL16_HIGHERA: case BFD_RELOC_PPC64_TPREL16_HIGHEST: case BFD_RELOC_PPC64_TPREL16_HIGHESTA: case BFD_RELOC_PPC64_DTPREL16_DS: case BFD_RELOC_PPC64_DTPREL16_LO_DS: case BFD_RELOC_PPC64_DTPREL16_HIGHER: case BFD_RELOC_PPC64_DTPREL16_HIGHERA: case BFD_RELOC_PPC64_DTPREL16_HIGHEST: case BFD_RELOC_PPC64_DTPREL16_HIGHESTA: S_SET_THREAD_LOCAL (fixP->fx_addsy); break; #endif /* Because SDA21 modifies the register field, the size is set to 4 bytes, rather than 2, so offset it here appropriately. */ case BFD_RELOC_PPC_EMB_SDA21: if (fixP->fx_pcrel) abort (); md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where + ((target_big_endian) ? 2 : 0), value, 2); break; case BFD_RELOC_8: if (fixP->fx_pcrel) { /* This can occur if there is a bug in the input assembler, eg: ".byte - ." */ if (fixP->fx_addsy) as_bad (_("Unable to handle reference to symbol %s"), S_GET_NAME (fixP->fx_addsy)); else as_bad (_("Unable to resolve expression")); fixP->fx_done = 1; } else md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where, value, 1); break; case BFD_RELOC_24_PLT_PCREL: case BFD_RELOC_PPC_LOCAL24PC: if (!fixP->fx_pcrel && !fixP->fx_done) abort (); if (fixP->fx_done) { char *where; unsigned long insn; /* Fetch the instruction, insert the fully resolved operand value, and stuff the instruction back again. */ where = fixP->fx_frag->fr_literal + fixP->fx_where; if (target_big_endian) insn = bfd_getb32 ((unsigned char *) where); else insn = bfd_getl32 ((unsigned char *) where); if ((value & 3) != 0) as_bad_where (fixP->fx_file, fixP->fx_line, _("must branch to an address a multiple of 4")); if ((offsetT) value < -0x40000000 || (offsetT) value >= 0x40000000) as_bad_where (fixP->fx_file, fixP->fx_line, _("@local or @plt branch destination is too far away, %ld bytes"), (long) value); insn = insn | (value & 0x03fffffc); if (target_big_endian) bfd_putb32 ((bfd_vma) insn, (unsigned char *) where); else bfd_putl32 ((bfd_vma) insn, (unsigned char *) where); } break; case BFD_RELOC_VTABLE_INHERIT: fixP->fx_done = 0; if (fixP->fx_addsy && !S_IS_DEFINED (fixP->fx_addsy) && !S_IS_WEAK (fixP->fx_addsy)) S_SET_WEAK (fixP->fx_addsy); break; case BFD_RELOC_VTABLE_ENTRY: fixP->fx_done = 0; break; #ifdef OBJ_ELF /* Generated by reference to `sym@tocbase'. The sym is ignored by the linker. */ case BFD_RELOC_PPC64_TOC: fixP->fx_done = 0; break; #endif default: fprintf (stderr, _("Gas failure, reloc value %d\n"), fixP->fx_r_type); fflush (stderr); abort (); } } #ifdef OBJ_ELF fixP->fx_addnumber = value; /* PowerPC uses RELA relocs, ie. the reloc addend is stored separately from the section contents. If we are going to be emitting a reloc then the section contents are immaterial, so don't warn if they happen to overflow. Leave such warnings to ld. */ if (!fixP->fx_done) fixP->fx_no_overflow = 1; #else if (fixP->fx_r_type != BFD_RELOC_PPC_TOC16) fixP->fx_addnumber = 0; else { #ifdef TE_PE fixP->fx_addnumber = 0; #else /* We want to use the offset within the data segment of the symbol, not the actual VMA of the symbol. */ fixP->fx_addnumber = - bfd_get_section_vma (stdoutput, S_GET_SEGMENT (fixP->fx_addsy)); #endif } #endif } /* Generate a reloc for a fixup. */ arelent * tc_gen_reloc (asection *seg ATTRIBUTE_UNUSED, fixS *fixp) { arelent *reloc; reloc = (arelent *) xmalloc (sizeof (arelent)); reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *)); *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy); reloc->address = fixp->fx_frag->fr_address + fixp->fx_where; reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type); if (reloc->howto == (reloc_howto_type *) NULL) { as_bad_where (fixp->fx_file, fixp->fx_line, _("reloc %d not supported by object file format"), (int) fixp->fx_r_type); return NULL; } reloc->addend = fixp->fx_addnumber; return reloc; } void ppc_cfi_frame_initial_instructions (void) { cfi_add_CFA_def_cfa (1, 0); } int tc_ppc_regname_to_dw2regnum (char *regname) { unsigned int regnum = -1; unsigned int i; const char *p; char *q; static struct { char *name; int dw2regnum; } regnames[] = { { "sp", 1 }, { "r.sp", 1 }, { "rtoc", 2 }, { "r.toc", 2 }, { "mq", 64 }, { "lr", 65 }, { "ctr", 66 }, { "ap", 67 }, { "cr", 70 }, { "xer", 76 }, { "vrsave", 109 }, { "vscr", 110 }, { "spe_acc", 111 }, { "spefscr", 112 } }; for (i = 0; i < ARRAY_SIZE (regnames); ++i) if (strcmp (regnames[i].name, regname) == 0) return regnames[i].dw2regnum; if (regname[0] == 'r' || regname[0] == 'f' || regname[0] == 'v') { p = regname + 1 + (regname[1] == '.'); regnum = strtoul (p, &q, 10); if (p == q || *q || regnum >= 32) return -1; if (regname[0] == 'f') regnum += 32; else if (regname[0] == 'v') regnum += 77; } else if (regname[0] == 'c' && regname[1] == 'r') { p = regname + 2 + (regname[2] == '.'); if (p[0] < '0' || p[0] > '7' || p[1]) return -1; regnum = p[0] - '0' + 68; } return regnum; } Index: projects/clang350-import/contrib/binutils =================================================================== --- projects/clang350-import/contrib/binutils (revision 276347) +++ projects/clang350-import/contrib/binutils (revision 276348) Property changes on: projects/clang350-import/contrib/binutils ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/contrib/binutils:r276302-276346 Index: projects/clang350-import/share/man/man4/tcp.4 =================================================================== --- projects/clang350-import/share/man/man4/tcp.4 (revision 276347) +++ projects/clang350-import/share/man/man4/tcp.4 (revision 276348) @@ -1,619 +1,620 @@ .\" Copyright (c) 1983, 1991, 1993 .\" The Regents of the University of California. .\" Copyright (c) 2010-2011 The FreeBSD Foundation .\" All rights reserved. .\" .\" Portions of this documentation were written at the Centre for Advanced .\" Internet Architectures, Swinburne University of Technology, Melbourne, .\" Australia by David Hayes under sponsorship from the FreeBSD Foundation. .\" .\" 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: @(#)tcp.4 8.1 (Berkeley) 6/5/93 .\" $FreeBSD$ .\" .Dd October 13, 2014 .Dt TCP 4 .Os .Sh NAME .Nm tcp .Nd Internet Transmission Control Protocol .Sh SYNOPSIS .In sys/types.h .In sys/socket.h .In netinet/in.h .In netinet/tcp.h .Ft int .Fn socket AF_INET SOCK_STREAM 0 .Sh DESCRIPTION The .Tn TCP protocol provides reliable, flow-controlled, two-way transmission of data. It is a byte-stream protocol used to support the .Dv SOCK_STREAM abstraction. .Tn TCP uses the standard Internet address format and, in addition, provides a per-host collection of .Dq "port addresses" . Thus, each address is composed of an Internet address specifying the host and network, with a specific .Tn TCP port on the host identifying the peer entity. .Pp Sockets utilizing the .Tn TCP protocol are either .Dq active or .Dq passive . Active sockets initiate connections to passive sockets. By default, .Tn TCP sockets are created active; to create a passive socket, the .Xr listen 2 system call must be used after binding the socket with the .Xr bind 2 system call. Only passive sockets may use the .Xr accept 2 call to accept incoming connections. Only active sockets may use the .Xr connect 2 call to initiate connections. .Pp Passive sockets may .Dq underspecify their location to match incoming connection requests from multiple networks. This technique, termed .Dq "wildcard addressing" , allows a single server to provide service to clients on multiple networks. To create a socket which listens on all networks, the Internet address .Dv INADDR_ANY must be bound. The .Tn TCP port may still be specified at this time; if the port is not specified, the system will assign one. Once a connection has been established, the socket's address is fixed by the peer entity's location. The address assigned to the socket is the address associated with the network interface through which packets are being transmitted and received. Normally, this address corresponds to the peer entity's network. .Pp .Tn TCP supports a number of socket options which can be set with .Xr setsockopt 2 and tested with .Xr getsockopt 2 : .Bl -tag -width ".Dv TCP_CONGESTION" .It Dv TCP_INFO Information about a socket's underlying TCP session may be retrieved by passing the read-only option .Dv TCP_INFO to .Xr getsockopt 2 . It accepts a single argument: a pointer to an instance of .Vt "struct tcp_info" . .Pp This API is subject to change; consult the source to determine which fields are currently filled out by this option. .Fx specific additions include send window size, receive window size, and bandwidth-controlled window space. .It Dv TCP_CONGESTION Select or query the congestion control algorithm that TCP will use for the connection. See .Xr mod_cc 4 for details. .It Dv TCP_KEEPINIT This .Xr setsockopt 2 option accepts a per-socket timeout argument of .Vt "u_int" in seconds, for new, non-established .Tn TCP connections. For the global default in milliseconds see .Va keepinit in the .Sx MIB Variables section further down. .It Dv TCP_KEEPIDLE This .Xr setsockopt 2 option accepts an argument of .Vt "u_int" for the amount of time, in seconds, that the connection must be idle before keepalive probes (if enabled) are sent for the connection of this socket. If set on a listening socket, the value is inherited by the newly created socket upon .Xr accept 2 . For the global default in milliseconds see .Va keepidle in the .Sx MIB Variables section further down. .It Dv TCP_KEEPINTVL This .Xr setsockopt 2 option accepts an argument of .Vt "u_int" to set the per-socket interval, in seconds, between keepalive probes sent to a peer. If set on a listening socket, the value is inherited by the newly created socket upon .Xr accept 2 . For the global default in milliseconds see .Va keepintvl in the .Sx MIB Variables section further down. .It Dv TCP_KEEPCNT This .Xr setsockopt 2 option accepts an argument of .Vt "u_int" and allows a per-socket tuning of the number of probes sent, with no response, before the connection will be dropped. If set on a listening socket, the value is inherited by the newly created socket upon .Xr accept 2 . For the global default see the .Va keepcnt in the .Sx MIB Variables section further down. .It Dv TCP_NODELAY Under most circumstances, .Tn TCP sends data when it is presented; when outstanding data has not yet been acknowledged, it gathers small amounts of output to be sent in a single packet once an acknowledgement is received. For a small number of clients, such as window systems that send a stream of mouse events which receive no replies, this packetization may cause significant delays. The boolean option .Dv TCP_NODELAY defeats this algorithm. .It Dv TCP_MAXSEG By default, a sender- and .No receiver- Ns Tn TCP will negotiate among themselves to determine the maximum segment size to be used for each connection. The .Dv TCP_MAXSEG option allows the user to determine the result of this negotiation, and to reduce it if desired. .It Dv TCP_NOOPT .Tn TCP usually sends a number of options in each packet, corresponding to various .Tn TCP extensions which are provided in this implementation. The boolean option .Dv TCP_NOOPT is provided to disable .Tn TCP option use on a per-connection basis. .It Dv TCP_NOPUSH By convention, the .No sender- Ns Tn TCP will set the .Dq push bit, and begin transmission immediately (if permitted) at the end of every user call to .Xr write 2 or .Xr writev 2 . When this option is set to a non-zero value, .Tn TCP will delay sending any data at all until either the socket is closed, or the internal send buffer is filled. .It Dv TCP_MD5SIG This option enables the use of MD5 digests (also known as TCP-MD5) on writes to the specified socket. Outgoing traffic is digested; digests on incoming traffic are verified if the .Va net.inet.tcp.signature_verify_input sysctl is nonzero. The current default behavior for the system is to respond to a system advertising this option with TCP-MD5; this may change. .Pp One common use for this in a .Fx router deployment is to enable based routers to interwork with Cisco equipment at peering points. Support for this feature conforms to RFC 2385. Only IPv4 .Pq Dv AF_INET sessions are supported. .Pp In order for this option to function correctly, it is necessary for the administrator to add a tcp-md5 key entry to the system's security associations database (SADB) using the .Xr setkey 8 utility. This entry must have an SPI of 0x1000 and can therefore only be specified on a per-host basis at this time. .Pp If an SADB entry cannot be found for the destination, the outgoing traffic will have an invalid digest option prepended, and the following error message will be visible on the system console: .Em "tcp_signature_compute: SADB lookup failed for %d.%d.%d.%d" . .El .Pp The option level for the .Xr setsockopt 2 call is the protocol number for .Tn TCP , available from .Xr getprotobyname 3 , or .Dv IPPROTO_TCP . All options are declared in .In netinet/tcp.h . .Pp Options at the .Tn IP transport level may be used with .Tn TCP ; see .Xr ip 4 . Incoming connection requests that are source-routed are noted, and the reverse source route is used in responding. .Pp The default congestion control algorithm for .Tn TCP is .Xr cc_newreno 4 . Other congestion control algorithms can be made available using the .Xr mod_cc 4 framework. .Ss MIB Variables The .Tn TCP protocol implements a number of variables in the .Va net.inet.tcp branch of the .Xr sysctl 3 MIB. .Bl -tag -width ".Va TCPCTL_DO_RFC1323" .It Dv TCPCTL_DO_RFC1323 .Pq Va rfc1323 Implement the window scaling and timestamp options of RFC 1323 (default is true). .It Dv TCPCTL_MSSDFLT .Pq Va mssdflt The default value used for the maximum segment size .Pq Dq MSS when no advice to the contrary is received from MSS negotiation. .It Dv TCPCTL_SENDSPACE .Pq Va sendspace Maximum .Tn TCP send window. .It Dv TCPCTL_RECVSPACE .Pq Va recvspace Maximum .Tn TCP receive window. .It Va log_in_vain Log any connection attempts to ports where there is not a socket accepting connections. The value of 1 limits the logging to .Tn SYN (connection establishment) packets only. That of 2 results in any .Tn TCP packets to closed ports being logged. Any value unlisted above disables the logging (default is 0, i.e., the logging is disabled). .It Va msl The Maximum Segment Lifetime, in milliseconds, for a packet. .It Va keepinit Timeout, in milliseconds, for new, non-established .Tn TCP connections. The default is 75000 msec. .It Va keepidle Amount of time, in milliseconds, that the connection must be idle before keepalive probes (if enabled) are sent. The default is 7200000 msec (2 hours). .It Va keepintvl The interval, in milliseconds, between keepalive probes sent to remote machines, when no response is received on a .Va keepidle probe. The default is 75000 msec. .It Va keepcnt Number of probes sent, with no response, before a connection is dropped. The default is 8 packets. .It Va always_keepalive Assume that .Dv SO_KEEPALIVE is set on all .Tn TCP connections, the kernel will periodically send a packet to the remote host to verify the connection is still up. .It Va icmp_may_rst Certain .Tn ICMP unreachable messages may abort connections in .Tn SYN-SENT state. .It Va do_tcpdrain Flush packets in the .Tn TCP reassembly queue if the system is low on mbufs. .It Va blackhole If enabled, disable sending of RST when a connection is attempted to a port where there is not a socket accepting connections. See .Xr blackhole 4 . .It Va delayed_ack Delay ACK to try and piggyback it onto a data packet. .It Va delacktime Maximum amount of time, in milliseconds, before a delayed ACK is sent. .It Va path_mtu_discovery Enable Path MTU Discovery. .It Va tcbhashsize Size of the .Tn TCP control-block hash table (read-only). This may be tuned using the kernel option .Dv TCBHASHSIZE or by setting .Va net.inet.tcp.tcbhashsize in the .Xr loader 8 . .It Va pcbcount Number of active process control blocks (read-only). .It Va syncookies Determines whether or not .Tn SYN cookies should be generated for outbound .Tn SYN-ACK packets. .Tn SYN cookies are a great help during .Tn SYN flood attacks, and are enabled by default. (See .Xr syncookies 4 . ) .It Va isn_reseed_interval The interval (in seconds) specifying how often the secret data used in RFC 1948 initial sequence number calculations should be reseeded. By default, this variable is set to zero, indicating that no reseeding will occur. Reseeding should not be necessary, and will break .Dv TIME_WAIT recycling for a few minutes. .It Va rexmit_min , rexmit_slop Adjust the retransmit timer calculation for .Tn TCP . The slop is typically added to the raw calculation to take into account occasional variances that the .Tn SRTT (smoothed round-trip time) is unable to accommodate, while the minimum specifies an absolute minimum. While a number of .Tn TCP RFCs suggest a 1 second minimum, these RFCs tend to focus on streaming behavior, and fail to deal with the fact that a 1 second minimum has severe detrimental effects over lossy interactive connections, such as a 802.11b wireless link, and over very fast but lossy connections for those cases not covered by the fast retransmit code. For this reason, we use 200ms of slop and a near-0 minimum, which gives us an effective minimum of 200ms (similar to .Tn Linux ) . .It Va rfc3042 Enable the Limited Transmit algorithm as described in RFC 3042. It helps avoid timeouts on lossy links and also when the congestion window is small, as happens on short transfers. .It Va rfc3390 Enable support for RFC 3390, which allows for a variable-sized starting congestion window on new connections, depending on the maximum segment size. This helps throughput in general, but particularly affects short transfers and high-bandwidth large propagation-delay connections. .It Va sack.enable Enable support for RFC 2018, TCP Selective Acknowledgment option, which allows the receiver to inform the sender about all successfully arrived segments, allowing the sender to retransmit the missing segments only. .It Va sack.maxholes Maximum number of SACK holes per connection. Defaults to 128. .It Va sack.globalmaxholes Maximum number of SACK holes per system, across all connections. Defaults to 65536. .It Va maxtcptw When a TCP connection enters the .Dv TIME_WAIT state, its associated socket structure is freed, since it is of negligible size and use, and a new structure is allocated to contain a minimal amount of information necessary for sustaining a connection in this state, called the compressed TCP TIME_WAIT state. Since this structure is smaller than a socket structure, it can save a significant amount of system memory. The .Va net.inet.tcp.maxtcptw MIB variable controls the maximum number of these structures allocated. By default, it is initialized to .Va kern.ipc.maxsockets / 5. .It Va nolocaltimewait Suppress creating of compressed TCP TIME_WAIT states for connections in which both endpoints are local. .It Va fast_finwait2_recycle Recycle .Tn TCP .Dv FIN_WAIT_2 connections faster when the socket is marked as .Dv SBS_CANTRCVMORE (no user process has the socket open, data received on the socket cannot be read). The timeout used here is .Va finwait2_timeout . .It Va finwait2_timeout Timeout to use for fast recycling of .Tn TCP .Dv FIN_WAIT_2 connections. Defaults to 60 seconds. .It Va ecn.enable Enable support for TCP Explicit Congestion Notification (ECN). ECN allows a TCP sender to reduce the transmission rate in order to avoid packet drops. .It Va ecn.maxretries Number of retries (SYN or SYN/ACK retransmits) before disabling ECN on a specific connection. This is needed to help with connection establishment when a broken firewall is in the network path. .It Va pmtud_blackhole_detection Turn on automatic path MTU blackhole detection. -In case of retransmits we will +In case of retransmits OS will lower the MSS to check if it's MTU problem. If current MSS is greater than -configured value to try, it will be set to it, otherwise, MSS will be set to -default values +configured value to try, it will be set to configured value, otherwise, +MSS will be set to default values .Po Va net.inet.tcp.mssdflt and .Va net.inet.tcp.v6mssdflt .Pc . .It Va pmtud_blackhole_mss MSS to try for IPv4 if PMTU blackhole detection is turned on. .It Va v6pmtud_blackhole_mss MSS to try for IPv6 if PMTU blackhole detection is turned on. .It Va pmtud_blackhole_activated -Number of times the code was activated to attempt a MSS downshift. -.It Va pmtud_blackhole_min_activated -Number of times the blackhole MSS was used in an attempt to downshift. +Number of times configured values were used in an attempt to downshift. +.It Va pmtud_blackhole_activated_min_mss +Number of times default MSS was used in an attempt to downshift. .It Va pmtud_blackhole_failed -Number of times that we failed to connect after we downshifted the MSS. +Number of connections for which retransmits continued even after MSS +downshift. .El .Sh ERRORS A socket operation may fail with one of the following errors returned: .Bl -tag -width Er .It Bq Er EISCONN when trying to establish a connection on a socket which already has one; .It Bq Er ENOBUFS when the system runs out of memory for an internal data structure; .It Bq Er ETIMEDOUT when a connection was dropped due to excessive retransmissions; .It Bq Er ECONNRESET when the remote peer forces the connection to be closed; .It Bq Er ECONNREFUSED when the remote peer actively refuses connection establishment (usually because no process is listening to the port); .It Bq Er EADDRINUSE when an attempt is made to create a socket with a port which has already been allocated; .It Bq Er EADDRNOTAVAIL when an attempt is made to create a socket with a network address for which no network interface exists; .It Bq Er EAFNOSUPPORT when an attempt is made to bind or connect a socket to a multicast address. .El .Sh SEE ALSO .Xr getsockopt 2 , .Xr socket 2 , .Xr sysctl 3 , .Xr blackhole 4 , .Xr inet 4 , .Xr intro 4 , .Xr ip 4 , .Xr mod_cc 4 , .Xr siftr 4 , .Xr syncache 4 , .Xr setkey 8 .Rs .%A "V. Jacobson" .%A "R. Braden" .%A "D. Borman" .%T "TCP Extensions for High Performance" .%O "RFC 1323" .Re .Rs .%A "A. Heffernan" .%T "Protection of BGP Sessions via the TCP MD5 Signature Option" .%O "RFC 2385" .Re .Rs .%A "K. Ramakrishnan" .%A "S. Floyd" .%A "D. Black" .%T "The Addition of Explicit Congestion Notification (ECN) to IP" .%O "RFC 3168" .Re .Sh HISTORY The .Tn TCP protocol appeared in .Bx 4.2 . The RFC 1323 extensions for window scaling and timestamps were added in .Bx 4.4 . The .Dv TCP_INFO option was introduced in .Tn Linux 2.6 and is .Em subject to change . Index: projects/clang350-import/share/man/man4 =================================================================== --- projects/clang350-import/share/man/man4 (revision 276347) +++ projects/clang350-import/share/man/man4 (revision 276348) Property changes on: projects/clang350-import/share/man/man4 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/share/man/man4:r276302-276346 Index: projects/clang350-import/share =================================================================== --- projects/clang350-import/share (revision 276347) +++ projects/clang350-import/share (revision 276348) Property changes on: projects/clang350-import/share ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/share:r276343-276346 Index: projects/clang350-import/sys/dev/ahci/ahci.c =================================================================== --- projects/clang350-import/sys/dev/ahci/ahci.c (revision 276347) +++ projects/clang350-import/sys/dev/ahci/ahci.c (revision 276348) @@ -1,2674 +1,2674 @@ /*- * Copyright (c) 2009-2012 Alexander Motin * 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, * without modification, immediately at the beginning of the file. * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ahci.h" #include #include #include #include #include /* local prototypes */ static void ahci_intr(void *data); static void ahci_intr_one(void *data); static void ahci_intr_one_edge(void *data); static int ahci_ch_init(device_t dev); static int ahci_ch_deinit(device_t dev); static int ahci_ch_suspend(device_t dev); static int ahci_ch_resume(device_t dev); static void ahci_ch_pm(void *arg); static void ahci_ch_intr(void *arg); static void ahci_ch_intr_direct(void *arg); static void ahci_ch_intr_main(struct ahci_channel *ch, uint32_t istatus); static void ahci_begin_transaction(struct ahci_channel *ch, union ccb *ccb); static void ahci_dmasetprd(void *arg, bus_dma_segment_t *segs, int nsegs, int error); static void ahci_execute_transaction(struct ahci_slot *slot); static void ahci_timeout(struct ahci_slot *slot); static void ahci_end_transaction(struct ahci_slot *slot, enum ahci_err_type et); static int ahci_setup_fis(struct ahci_channel *ch, struct ahci_cmd_tab *ctp, union ccb *ccb, int tag); static void ahci_dmainit(device_t dev); static void ahci_dmasetupc_cb(void *xsc, bus_dma_segment_t *segs, int nsegs, int error); static void ahci_dmafini(device_t dev); static void ahci_slotsalloc(device_t dev); static void ahci_slotsfree(device_t dev); static void ahci_reset(struct ahci_channel *ch); static void ahci_start(struct ahci_channel *ch, int fbs); static void ahci_stop(struct ahci_channel *ch); static void ahci_clo(struct ahci_channel *ch); static void ahci_start_fr(struct ahci_channel *ch); static void ahci_stop_fr(struct ahci_channel *ch); static int ahci_sata_connect(struct ahci_channel *ch); static int ahci_sata_phy_reset(struct ahci_channel *ch); static int ahci_wait_ready(struct ahci_channel *ch, int t, int t0); static void ahci_issue_recovery(struct ahci_channel *ch); static void ahci_process_read_log(struct ahci_channel *ch, union ccb *ccb); static void ahci_process_request_sense(struct ahci_channel *ch, union ccb *ccb); static void ahciaction(struct cam_sim *sim, union ccb *ccb); static void ahcipoll(struct cam_sim *sim); static MALLOC_DEFINE(M_AHCI, "AHCI driver", "AHCI driver data buffers"); #define recovery_type spriv_field0 #define RECOVERY_NONE 0 #define RECOVERY_READ_LOG 1 #define RECOVERY_REQUEST_SENSE 2 #define recovery_slot spriv_field1 int ahci_ctlr_setup(device_t dev) { struct ahci_controller *ctlr = device_get_softc(dev); /* Clear interrupts */ ATA_OUTL(ctlr->r_mem, AHCI_IS, ATA_INL(ctlr->r_mem, AHCI_IS)); /* Configure CCC */ if (ctlr->ccc) { ATA_OUTL(ctlr->r_mem, AHCI_CCCP, ATA_INL(ctlr->r_mem, AHCI_PI)); ATA_OUTL(ctlr->r_mem, AHCI_CCCC, (ctlr->ccc << AHCI_CCCC_TV_SHIFT) | (4 << AHCI_CCCC_CC_SHIFT) | AHCI_CCCC_EN); ctlr->cccv = (ATA_INL(ctlr->r_mem, AHCI_CCCC) & AHCI_CCCC_INT_MASK) >> AHCI_CCCC_INT_SHIFT; if (bootverbose) { device_printf(dev, "CCC with %dms/4cmd enabled on vector %d\n", ctlr->ccc, ctlr->cccv); } } /* Enable AHCI interrupts */ ATA_OUTL(ctlr->r_mem, AHCI_GHC, ATA_INL(ctlr->r_mem, AHCI_GHC) | AHCI_GHC_IE); return (0); } int ahci_ctlr_reset(device_t dev) { struct ahci_controller *ctlr = device_get_softc(dev); int timeout; /* Enable AHCI mode */ ATA_OUTL(ctlr->r_mem, AHCI_GHC, AHCI_GHC_AE); /* Reset AHCI controller */ ATA_OUTL(ctlr->r_mem, AHCI_GHC, AHCI_GHC_AE|AHCI_GHC_HR); for (timeout = 1000; timeout > 0; timeout--) { DELAY(1000); if ((ATA_INL(ctlr->r_mem, AHCI_GHC) & AHCI_GHC_HR) == 0) break; } if (timeout == 0) { device_printf(dev, "AHCI controller reset failure\n"); return (ENXIO); } /* Reenable AHCI mode */ ATA_OUTL(ctlr->r_mem, AHCI_GHC, AHCI_GHC_AE); return (0); } int ahci_attach(device_t dev) { struct ahci_controller *ctlr = device_get_softc(dev); int error, i, u, speed, unit; u_int32_t version; device_t child; ctlr->dev = dev; ctlr->ccc = 0; resource_int_value(device_get_name(dev), device_get_unit(dev), "ccc", &ctlr->ccc); /* Setup our own memory management for channels. */ ctlr->sc_iomem.rm_start = rman_get_start(ctlr->r_mem); ctlr->sc_iomem.rm_end = rman_get_end(ctlr->r_mem); ctlr->sc_iomem.rm_type = RMAN_ARRAY; ctlr->sc_iomem.rm_descr = "I/O memory addresses"; if ((error = rman_init(&ctlr->sc_iomem)) != 0) { bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem); return (error); } if ((error = rman_manage_region(&ctlr->sc_iomem, rman_get_start(ctlr->r_mem), rman_get_end(ctlr->r_mem))) != 0) { bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem); rman_fini(&ctlr->sc_iomem); return (error); } /* Get the HW capabilities */ version = ATA_INL(ctlr->r_mem, AHCI_VS); ctlr->caps = ATA_INL(ctlr->r_mem, AHCI_CAP); if (version >= 0x00010200) ctlr->caps2 = ATA_INL(ctlr->r_mem, AHCI_CAP2); if (ctlr->caps & AHCI_CAP_EMS) ctlr->capsem = ATA_INL(ctlr->r_mem, AHCI_EM_CTL); ctlr->ichannels = ATA_INL(ctlr->r_mem, AHCI_PI); /* Identify and set separate quirks for HBA and RAID f/w Marvells. */ if ((ctlr->quirks & AHCI_Q_ALTSIG) && (ctlr->caps & AHCI_CAP_SPM) == 0) ctlr->quirks |= AHCI_Q_NOBSYRES; if (ctlr->quirks & AHCI_Q_1CH) { ctlr->caps &= ~AHCI_CAP_NPMASK; ctlr->ichannels &= 0x01; } if (ctlr->quirks & AHCI_Q_2CH) { ctlr->caps &= ~AHCI_CAP_NPMASK; ctlr->caps |= 1; ctlr->ichannels &= 0x03; } if (ctlr->quirks & AHCI_Q_4CH) { ctlr->caps &= ~AHCI_CAP_NPMASK; ctlr->caps |= 3; ctlr->ichannels &= 0x0f; } ctlr->channels = MAX(flsl(ctlr->ichannels), (ctlr->caps & AHCI_CAP_NPMASK) + 1); if (ctlr->quirks & AHCI_Q_NOPMP) ctlr->caps &= ~AHCI_CAP_SPM; if (ctlr->quirks & AHCI_Q_NONCQ) ctlr->caps &= ~AHCI_CAP_SNCQ; if ((ctlr->caps & AHCI_CAP_CCCS) == 0) ctlr->ccc = 0; ctlr->emloc = ATA_INL(ctlr->r_mem, AHCI_EM_LOC); /* Create controller-wide DMA tag. */ if (bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0, (ctlr->caps & AHCI_CAP_64BIT) ? BUS_SPACE_MAXADDR : BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, BUS_SPACE_MAXSIZE, BUS_SPACE_UNRESTRICTED, BUS_SPACE_MAXSIZE, 0, NULL, NULL, &ctlr->dma_tag)) { bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem); rman_fini(&ctlr->sc_iomem); return (ENXIO); } ahci_ctlr_setup(dev); /* Setup interrupts. */ if ((error = ahci_setup_interrupt(dev)) != 0) { bus_dma_tag_destroy(ctlr->dma_tag); bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem); rman_fini(&ctlr->sc_iomem); return (error); } i = 0; for (u = ctlr->ichannels; u != 0; u >>= 1) i += (u & 1); ctlr->direct = (ctlr->msi && (ctlr->numirqs > 1 || i <= 3)); resource_int_value(device_get_name(dev), device_get_unit(dev), "direct", &ctlr->direct); /* Announce HW capabilities. */ speed = (ctlr->caps & AHCI_CAP_ISS) >> AHCI_CAP_ISS_SHIFT; device_printf(dev, "AHCI v%x.%02x with %d %sGbps ports, Port Multiplier %s%s\n", ((version >> 20) & 0xf0) + ((version >> 16) & 0x0f), ((version >> 4) & 0xf0) + (version & 0x0f), (ctlr->caps & AHCI_CAP_NPMASK) + 1, ((speed == 1) ? "1.5":((speed == 2) ? "3": ((speed == 3) ? "6":"?"))), (ctlr->caps & AHCI_CAP_SPM) ? "supported" : "not supported", (ctlr->caps & AHCI_CAP_FBSS) ? " with FBS" : ""); if (ctlr->quirks != 0) { device_printf(dev, "quirks=0x%b\n", ctlr->quirks, AHCI_Q_BIT_STRING); } if (bootverbose) { device_printf(dev, "Caps:%s%s%s%s%s%s%s%s %sGbps", (ctlr->caps & AHCI_CAP_64BIT) ? " 64bit":"", (ctlr->caps & AHCI_CAP_SNCQ) ? " NCQ":"", (ctlr->caps & AHCI_CAP_SSNTF) ? " SNTF":"", (ctlr->caps & AHCI_CAP_SMPS) ? " MPS":"", (ctlr->caps & AHCI_CAP_SSS) ? " SS":"", (ctlr->caps & AHCI_CAP_SALP) ? " ALP":"", (ctlr->caps & AHCI_CAP_SAL) ? " AL":"", (ctlr->caps & AHCI_CAP_SCLO) ? " CLO":"", ((speed == 1) ? "1.5":((speed == 2) ? "3": ((speed == 3) ? "6":"?")))); printf("%s%s%s%s%s%s %dcmd%s%s%s %dports\n", (ctlr->caps & AHCI_CAP_SAM) ? " AM":"", (ctlr->caps & AHCI_CAP_SPM) ? " PM":"", (ctlr->caps & AHCI_CAP_FBSS) ? " FBS":"", (ctlr->caps & AHCI_CAP_PMD) ? " PMD":"", (ctlr->caps & AHCI_CAP_SSC) ? " SSC":"", (ctlr->caps & AHCI_CAP_PSC) ? " PSC":"", ((ctlr->caps & AHCI_CAP_NCS) >> AHCI_CAP_NCS_SHIFT) + 1, (ctlr->caps & AHCI_CAP_CCCS) ? " CCC":"", (ctlr->caps & AHCI_CAP_EMS) ? " EM":"", (ctlr->caps & AHCI_CAP_SXS) ? " eSATA":"", (ctlr->caps & AHCI_CAP_NPMASK) + 1); } if (bootverbose && version >= 0x00010200) { device_printf(dev, "Caps2:%s%s%s%s%s%s\n", (ctlr->caps2 & AHCI_CAP2_DESO) ? " DESO":"", (ctlr->caps2 & AHCI_CAP2_SADM) ? " SADM":"", (ctlr->caps2 & AHCI_CAP2_SDS) ? " SDS":"", (ctlr->caps2 & AHCI_CAP2_APST) ? " APST":"", (ctlr->caps2 & AHCI_CAP2_NVMP) ? " NVMP":"", (ctlr->caps2 & AHCI_CAP2_BOH) ? " BOH":""); } /* Attach all channels on this controller */ for (unit = 0; unit < ctlr->channels; unit++) { child = device_add_child(dev, "ahcich", -1); if (child == NULL) { device_printf(dev, "failed to add channel device\n"); continue; } device_set_ivars(child, (void *)(intptr_t)unit); if ((ctlr->ichannels & (1 << unit)) == 0) device_disable(child); } if (ctlr->caps & AHCI_CAP_EMS) { child = device_add_child(dev, "ahciem", -1); if (child == NULL) device_printf(dev, "failed to add enclosure device\n"); else device_set_ivars(child, (void *)(intptr_t)-1); } bus_generic_attach(dev); return (0); } int ahci_detach(device_t dev) { struct ahci_controller *ctlr = device_get_softc(dev); int i; /* Detach & delete all children */ device_delete_children(dev); /* Free interrupts. */ for (i = 0; i < ctlr->numirqs; i++) { if (ctlr->irqs[i].r_irq) { bus_teardown_intr(dev, ctlr->irqs[i].r_irq, ctlr->irqs[i].handle); bus_release_resource(dev, SYS_RES_IRQ, ctlr->irqs[i].r_irq_rid, ctlr->irqs[i].r_irq); } } bus_dma_tag_destroy(ctlr->dma_tag); /* Free memory. */ rman_fini(&ctlr->sc_iomem); if (ctlr->r_mem) bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem); return (0); } int ahci_setup_interrupt(device_t dev) { struct ahci_controller *ctlr = device_get_softc(dev); int i; /* Check for single MSI vector fallback. */ if (ctlr->numirqs > 1 && (ATA_INL(ctlr->r_mem, AHCI_GHC) & AHCI_GHC_MRSM) != 0) { device_printf(dev, "Falling back to one MSI\n"); ctlr->numirqs = 1; } /* Ensure we don't overrun irqs. */ if (ctlr->numirqs > AHCI_MAX_IRQS) { device_printf(dev, "Too many irqs %d > %d (clamping)\n", ctlr->numirqs, AHCI_MAX_IRQS); ctlr->numirqs = AHCI_MAX_IRQS; } /* Allocate all IRQs. */ for (i = 0; i < ctlr->numirqs; i++) { ctlr->irqs[i].ctlr = ctlr; ctlr->irqs[i].r_irq_rid = i + (ctlr->msi ? 1 : 0); if (ctlr->channels == 1 && !ctlr->ccc && ctlr->msi) ctlr->irqs[i].mode = AHCI_IRQ_MODE_ONE; else if (ctlr->numirqs == 1 || i >= ctlr->channels || (ctlr->ccc && i == ctlr->cccv)) ctlr->irqs[i].mode = AHCI_IRQ_MODE_ALL; else if (i == ctlr->numirqs - 1) ctlr->irqs[i].mode = AHCI_IRQ_MODE_AFTER; else ctlr->irqs[i].mode = AHCI_IRQ_MODE_ONE; if (!(ctlr->irqs[i].r_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &ctlr->irqs[i].r_irq_rid, RF_SHAREABLE | RF_ACTIVE))) { device_printf(dev, "unable to map interrupt\n"); return (ENXIO); } if ((bus_setup_intr(dev, ctlr->irqs[i].r_irq, ATA_INTR_FLAGS, NULL, (ctlr->irqs[i].mode != AHCI_IRQ_MODE_ONE) ? ahci_intr : ((ctlr->quirks & AHCI_Q_EDGEIS) ? ahci_intr_one_edge : ahci_intr_one), &ctlr->irqs[i], &ctlr->irqs[i].handle))) { /* SOS XXX release r_irq */ device_printf(dev, "unable to setup interrupt\n"); return (ENXIO); } if (ctlr->numirqs > 1) { bus_describe_intr(dev, ctlr->irqs[i].r_irq, ctlr->irqs[i].handle, ctlr->irqs[i].mode == AHCI_IRQ_MODE_ONE ? "ch%d" : "%d", i); } } return (0); } /* * Common case interrupt handler. */ static void ahci_intr(void *data) { struct ahci_controller_irq *irq = data; struct ahci_controller *ctlr = irq->ctlr; u_int32_t is, ise = 0; void *arg; int unit; if (irq->mode == AHCI_IRQ_MODE_ALL) { unit = 0; if (ctlr->ccc) is = ctlr->ichannels; else is = ATA_INL(ctlr->r_mem, AHCI_IS); } else { /* AHCI_IRQ_MODE_AFTER */ unit = irq->r_irq_rid - 1; is = ATA_INL(ctlr->r_mem, AHCI_IS); } /* CCC interrupt is edge triggered. */ if (ctlr->ccc) ise = 1 << ctlr->cccv; /* Some controllers have edge triggered IS. */ if (ctlr->quirks & AHCI_Q_EDGEIS) ise |= is; if (ise != 0) ATA_OUTL(ctlr->r_mem, AHCI_IS, ise); for (; unit < ctlr->channels; unit++) { if ((is & (1 << unit)) != 0 && (arg = ctlr->interrupt[unit].argument)) { ctlr->interrupt[unit].function(arg); } } /* AHCI declares level triggered IS. */ if (!(ctlr->quirks & AHCI_Q_EDGEIS)) ATA_OUTL(ctlr->r_mem, AHCI_IS, is); } /* * Simplified interrupt handler for multivector MSI mode. */ static void ahci_intr_one(void *data) { struct ahci_controller_irq *irq = data; struct ahci_controller *ctlr = irq->ctlr; void *arg; int unit; unit = irq->r_irq_rid - 1; if ((arg = ctlr->interrupt[unit].argument)) ctlr->interrupt[unit].function(arg); /* AHCI declares level triggered IS. */ ATA_OUTL(ctlr->r_mem, AHCI_IS, 1 << unit); } static void ahci_intr_one_edge(void *data) { struct ahci_controller_irq *irq = data; struct ahci_controller *ctlr = irq->ctlr; void *arg; int unit; unit = irq->r_irq_rid - 1; /* Some controllers have edge triggered IS. */ ATA_OUTL(ctlr->r_mem, AHCI_IS, 1 << unit); if ((arg = ctlr->interrupt[unit].argument)) ctlr->interrupt[unit].function(arg); } struct resource * ahci_alloc_resource(device_t dev, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { struct ahci_controller *ctlr = device_get_softc(dev); struct resource *res; long st; int offset, size, unit; unit = (intptr_t)device_get_ivars(child); res = NULL; switch (type) { case SYS_RES_MEMORY: if (unit >= 0) { offset = AHCI_OFFSET + (unit << 7); size = 128; } else if (*rid == 0) { offset = AHCI_EM_CTL; size = 4; } else { offset = (ctlr->emloc & 0xffff0000) >> 14; size = (ctlr->emloc & 0x0000ffff) << 2; if (*rid != 1) { if (*rid == 2 && (ctlr->capsem & (AHCI_EM_XMT | AHCI_EM_SMB)) == 0) offset += size; else break; } } st = rman_get_start(ctlr->r_mem); res = rman_reserve_resource(&ctlr->sc_iomem, st + offset, st + offset + size - 1, size, RF_ACTIVE, child); if (res) { bus_space_handle_t bsh; bus_space_tag_t bst; bsh = rman_get_bushandle(ctlr->r_mem); bst = rman_get_bustag(ctlr->r_mem); bus_space_subregion(bst, bsh, offset, 128, &bsh); rman_set_bushandle(res, bsh); rman_set_bustag(res, bst); } break; case SYS_RES_IRQ: if (*rid == ATA_IRQ_RID) res = ctlr->irqs[0].r_irq; break; } return (res); } int ahci_release_resource(device_t dev, device_t child, int type, int rid, struct resource *r) { switch (type) { case SYS_RES_MEMORY: rman_release_resource(r); return (0); case SYS_RES_IRQ: if (rid != ATA_IRQ_RID) return (ENOENT); return (0); } return (EINVAL); } int ahci_setup_intr(device_t dev, device_t child, struct resource *irq, int flags, driver_filter_t *filter, driver_intr_t *function, void *argument, void **cookiep) { struct ahci_controller *ctlr = device_get_softc(dev); int unit = (intptr_t)device_get_ivars(child); if (filter != NULL) { printf("ahci.c: we cannot use a filter here\n"); return (EINVAL); } ctlr->interrupt[unit].function = function; ctlr->interrupt[unit].argument = argument; return (0); } int ahci_teardown_intr(device_t dev, device_t child, struct resource *irq, void *cookie) { struct ahci_controller *ctlr = device_get_softc(dev); int unit = (intptr_t)device_get_ivars(child); ctlr->interrupt[unit].function = NULL; ctlr->interrupt[unit].argument = NULL; return (0); } int ahci_print_child(device_t dev, device_t child) { int retval, channel; retval = bus_print_child_header(dev, child); channel = (int)(intptr_t)device_get_ivars(child); if (channel >= 0) retval += printf(" at channel %d", channel); retval += bus_print_child_footer(dev, child); return (retval); } int ahci_child_location_str(device_t dev, device_t child, char *buf, size_t buflen) { int channel; channel = (int)(intptr_t)device_get_ivars(child); if (channel >= 0) snprintf(buf, buflen, "channel=%d", channel); return (0); } bus_dma_tag_t ahci_get_dma_tag(device_t dev, device_t child) { struct ahci_controller *ctlr = device_get_softc(dev); return (ctlr->dma_tag); } static int ahci_ch_probe(device_t dev) { device_set_desc_copy(dev, "AHCI channel"); return (0); } static int ahci_ch_attach(device_t dev) { struct ahci_controller *ctlr = device_get_softc(device_get_parent(dev)); struct ahci_channel *ch = device_get_softc(dev); struct cam_devq *devq; int rid, error, i, sata_rev = 0; u_int32_t version; ch->dev = dev; ch->unit = (intptr_t)device_get_ivars(dev); ch->caps = ctlr->caps; ch->caps2 = ctlr->caps2; ch->quirks = ctlr->quirks; ch->vendorid = ctlr->vendorid; ch->deviceid = ctlr->deviceid; ch->subvendorid = ctlr->subvendorid; ch->subdeviceid = ctlr->subdeviceid; ch->numslots = ((ch->caps & AHCI_CAP_NCS) >> AHCI_CAP_NCS_SHIFT) + 1; mtx_init(&ch->mtx, "AHCI channel lock", NULL, MTX_DEF); ch->pm_level = 0; resource_int_value(device_get_name(dev), device_get_unit(dev), "pm_level", &ch->pm_level); STAILQ_INIT(&ch->doneq); if (ch->pm_level > 3) callout_init_mtx(&ch->pm_timer, &ch->mtx, 0); callout_init_mtx(&ch->reset_timer, &ch->mtx, 0); /* JMicron external ports (0) sometimes limited */ if ((ctlr->quirks & AHCI_Q_SATA1_UNIT0) && ch->unit == 0) sata_rev = 1; if (ch->quirks & AHCI_Q_SATA2) sata_rev = 2; resource_int_value(device_get_name(dev), device_get_unit(dev), "sata_rev", &sata_rev); for (i = 0; i < 16; i++) { ch->user[i].revision = sata_rev; ch->user[i].mode = 0; ch->user[i].bytecount = 8192; ch->user[i].tags = ch->numslots; ch->user[i].caps = 0; ch->curr[i] = ch->user[i]; if (ch->pm_level) { ch->user[i].caps = CTS_SATA_CAPS_H_PMREQ | CTS_SATA_CAPS_H_APST | CTS_SATA_CAPS_D_PMREQ | CTS_SATA_CAPS_D_APST; } ch->user[i].caps |= CTS_SATA_CAPS_H_DMAAA | CTS_SATA_CAPS_H_AN; } rid = 0; if (!(ch->r_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE))) return (ENXIO); ahci_dmainit(dev); ahci_slotsalloc(dev); ahci_ch_init(dev); mtx_lock(&ch->mtx); rid = ATA_IRQ_RID; if (!(ch->r_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE))) { device_printf(dev, "Unable to map interrupt\n"); error = ENXIO; goto err0; } if ((bus_setup_intr(dev, ch->r_irq, ATA_INTR_FLAGS, NULL, ctlr->direct ? ahci_ch_intr_direct : ahci_ch_intr, ch, &ch->ih))) { device_printf(dev, "Unable to setup interrupt\n"); error = ENXIO; goto err1; } ch->chcaps = ATA_INL(ch->r_mem, AHCI_P_CMD); version = ATA_INL(ctlr->r_mem, AHCI_VS); if (version < 0x00010200 && (ctlr->caps & AHCI_CAP_FBSS)) ch->chcaps |= AHCI_P_CMD_FBSCP; if (ch->caps2 & AHCI_CAP2_SDS) ch->chscaps = ATA_INL(ch->r_mem, AHCI_P_DEVSLP); if (bootverbose) { device_printf(dev, "Caps:%s%s%s%s%s%s\n", (ch->chcaps & AHCI_P_CMD_HPCP) ? " HPCP":"", (ch->chcaps & AHCI_P_CMD_MPSP) ? " MPSP":"", (ch->chcaps & AHCI_P_CMD_CPD) ? " CPD":"", (ch->chcaps & AHCI_P_CMD_ESP) ? " ESP":"", (ch->chcaps & AHCI_P_CMD_FBSCP) ? " FBSCP":"", (ch->chscaps & AHCI_P_DEVSLP_DSP) ? " DSP":""); } /* Create the device queue for our SIM. */ devq = cam_simq_alloc(ch->numslots); if (devq == NULL) { device_printf(dev, "Unable to allocate simq\n"); error = ENOMEM; goto err1; } /* Construct SIM entry */ ch->sim = cam_sim_alloc(ahciaction, ahcipoll, "ahcich", ch, device_get_unit(dev), (struct mtx *)&ch->mtx, min(2, ch->numslots), (ch->caps & AHCI_CAP_SNCQ) ? ch->numslots : 0, devq); if (ch->sim == NULL) { cam_simq_free(devq); device_printf(dev, "unable to allocate sim\n"); error = ENOMEM; goto err1; } if (xpt_bus_register(ch->sim, dev, 0) != CAM_SUCCESS) { device_printf(dev, "unable to register xpt bus\n"); error = ENXIO; goto err2; } if (xpt_create_path(&ch->path, /*periph*/NULL, cam_sim_path(ch->sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { device_printf(dev, "unable to create path\n"); error = ENXIO; goto err3; } if (ch->pm_level > 3) { callout_reset(&ch->pm_timer, (ch->pm_level == 4) ? hz / 1000 : hz / 8, ahci_ch_pm, ch); } mtx_unlock(&ch->mtx); return (0); err3: xpt_bus_deregister(cam_sim_path(ch->sim)); err2: cam_sim_free(ch->sim, /*free_devq*/TRUE); err1: bus_release_resource(dev, SYS_RES_IRQ, ATA_IRQ_RID, ch->r_irq); err0: bus_release_resource(dev, SYS_RES_MEMORY, ch->unit, ch->r_mem); mtx_unlock(&ch->mtx); mtx_destroy(&ch->mtx); return (error); } static int ahci_ch_detach(device_t dev) { struct ahci_channel *ch = device_get_softc(dev); mtx_lock(&ch->mtx); xpt_async(AC_LOST_DEVICE, ch->path, NULL); /* Forget about reset. */ if (ch->resetting) { ch->resetting = 0; xpt_release_simq(ch->sim, TRUE); } xpt_free_path(ch->path); xpt_bus_deregister(cam_sim_path(ch->sim)); cam_sim_free(ch->sim, /*free_devq*/TRUE); mtx_unlock(&ch->mtx); if (ch->pm_level > 3) callout_drain(&ch->pm_timer); callout_drain(&ch->reset_timer); bus_teardown_intr(dev, ch->r_irq, ch->ih); bus_release_resource(dev, SYS_RES_IRQ, ATA_IRQ_RID, ch->r_irq); ahci_ch_deinit(dev); ahci_slotsfree(dev); ahci_dmafini(dev); bus_release_resource(dev, SYS_RES_MEMORY, ch->unit, ch->r_mem); mtx_destroy(&ch->mtx); return (0); } static int ahci_ch_init(device_t dev) { struct ahci_channel *ch = device_get_softc(dev); uint64_t work; /* Disable port interrupts */ ATA_OUTL(ch->r_mem, AHCI_P_IE, 0); /* Setup work areas */ work = ch->dma.work_bus + AHCI_CL_OFFSET; ATA_OUTL(ch->r_mem, AHCI_P_CLB, work & 0xffffffff); ATA_OUTL(ch->r_mem, AHCI_P_CLBU, work >> 32); work = ch->dma.rfis_bus; ATA_OUTL(ch->r_mem, AHCI_P_FB, work & 0xffffffff); ATA_OUTL(ch->r_mem, AHCI_P_FBU, work >> 32); /* Activate the channel and power/spin up device */ ATA_OUTL(ch->r_mem, AHCI_P_CMD, (AHCI_P_CMD_ACTIVE | AHCI_P_CMD_POD | AHCI_P_CMD_SUD | ((ch->pm_level == 2 || ch->pm_level == 3) ? AHCI_P_CMD_ALPE : 0) | ((ch->pm_level > 2) ? AHCI_P_CMD_ASP : 0 ))); ahci_start_fr(ch); ahci_start(ch, 1); return (0); } static int ahci_ch_deinit(device_t dev) { struct ahci_channel *ch = device_get_softc(dev); /* Disable port interrupts. */ ATA_OUTL(ch->r_mem, AHCI_P_IE, 0); /* Reset command register. */ ahci_stop(ch); ahci_stop_fr(ch); ATA_OUTL(ch->r_mem, AHCI_P_CMD, 0); /* Allow everything, including partial and slumber modes. */ ATA_OUTL(ch->r_mem, AHCI_P_SCTL, 0); /* Request slumber mode transition and give some time to get there. */ ATA_OUTL(ch->r_mem, AHCI_P_CMD, AHCI_P_CMD_SLUMBER); DELAY(100); /* Disable PHY. */ ATA_OUTL(ch->r_mem, AHCI_P_SCTL, ATA_SC_DET_DISABLE); return (0); } static int ahci_ch_suspend(device_t dev) { struct ahci_channel *ch = device_get_softc(dev); mtx_lock(&ch->mtx); xpt_freeze_simq(ch->sim, 1); /* Forget about reset. */ if (ch->resetting) { ch->resetting = 0; callout_stop(&ch->reset_timer); xpt_release_simq(ch->sim, TRUE); } while (ch->oslots) msleep(ch, &ch->mtx, PRIBIO, "ahcisusp", hz/100); ahci_ch_deinit(dev); mtx_unlock(&ch->mtx); return (0); } static int ahci_ch_resume(device_t dev) { struct ahci_channel *ch = device_get_softc(dev); mtx_lock(&ch->mtx); ahci_ch_init(dev); ahci_reset(ch); xpt_release_simq(ch->sim, TRUE); mtx_unlock(&ch->mtx); return (0); } devclass_t ahcich_devclass; static device_method_t ahcich_methods[] = { DEVMETHOD(device_probe, ahci_ch_probe), DEVMETHOD(device_attach, ahci_ch_attach), DEVMETHOD(device_detach, ahci_ch_detach), DEVMETHOD(device_suspend, ahci_ch_suspend), DEVMETHOD(device_resume, ahci_ch_resume), - { 0, 0 } + DEVMETHOD_END }; static driver_t ahcich_driver = { "ahcich", ahcich_methods, sizeof(struct ahci_channel) }; -DRIVER_MODULE(ahcich, ahci, ahcich_driver, ahcich_devclass, 0, 0); +DRIVER_MODULE(ahcich, ahci, ahcich_driver, ahcich_devclass, NULL, NULL); struct ahci_dc_cb_args { bus_addr_t maddr; int error; }; static void ahci_dmainit(device_t dev) { struct ahci_channel *ch = device_get_softc(dev); struct ahci_dc_cb_args dcba; size_t rfsize; /* Command area. */ if (bus_dma_tag_create(bus_get_dma_tag(dev), 1024, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, AHCI_WORK_SIZE, 1, AHCI_WORK_SIZE, 0, NULL, NULL, &ch->dma.work_tag)) goto error; if (bus_dmamem_alloc(ch->dma.work_tag, (void **)&ch->dma.work, BUS_DMA_ZERO, &ch->dma.work_map)) goto error; if (bus_dmamap_load(ch->dma.work_tag, ch->dma.work_map, ch->dma.work, AHCI_WORK_SIZE, ahci_dmasetupc_cb, &dcba, 0) || dcba.error) { bus_dmamem_free(ch->dma.work_tag, ch->dma.work, ch->dma.work_map); goto error; } ch->dma.work_bus = dcba.maddr; /* FIS receive area. */ if (ch->chcaps & AHCI_P_CMD_FBSCP) rfsize = 4096; else rfsize = 256; if (bus_dma_tag_create(bus_get_dma_tag(dev), rfsize, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, rfsize, 1, rfsize, 0, NULL, NULL, &ch->dma.rfis_tag)) goto error; if (bus_dmamem_alloc(ch->dma.rfis_tag, (void **)&ch->dma.rfis, 0, &ch->dma.rfis_map)) goto error; if (bus_dmamap_load(ch->dma.rfis_tag, ch->dma.rfis_map, ch->dma.rfis, rfsize, ahci_dmasetupc_cb, &dcba, 0) || dcba.error) { bus_dmamem_free(ch->dma.rfis_tag, ch->dma.rfis, ch->dma.rfis_map); goto error; } ch->dma.rfis_bus = dcba.maddr; /* Data area. */ if (bus_dma_tag_create(bus_get_dma_tag(dev), 2, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, AHCI_SG_ENTRIES * PAGE_SIZE * ch->numslots, AHCI_SG_ENTRIES, AHCI_PRD_MAX, 0, busdma_lock_mutex, &ch->mtx, &ch->dma.data_tag)) { goto error; } return; error: device_printf(dev, "WARNING - DMA initialization failed\n"); ahci_dmafini(dev); } static void ahci_dmasetupc_cb(void *xsc, bus_dma_segment_t *segs, int nsegs, int error) { struct ahci_dc_cb_args *dcba = (struct ahci_dc_cb_args *)xsc; if (!(dcba->error = error)) dcba->maddr = segs[0].ds_addr; } static void ahci_dmafini(device_t dev) { struct ahci_channel *ch = device_get_softc(dev); if (ch->dma.data_tag) { bus_dma_tag_destroy(ch->dma.data_tag); ch->dma.data_tag = NULL; } if (ch->dma.rfis_bus) { bus_dmamap_unload(ch->dma.rfis_tag, ch->dma.rfis_map); bus_dmamem_free(ch->dma.rfis_tag, ch->dma.rfis, ch->dma.rfis_map); ch->dma.rfis_bus = 0; ch->dma.rfis = NULL; } if (ch->dma.work_bus) { bus_dmamap_unload(ch->dma.work_tag, ch->dma.work_map); bus_dmamem_free(ch->dma.work_tag, ch->dma.work, ch->dma.work_map); ch->dma.work_bus = 0; ch->dma.work = NULL; } if (ch->dma.work_tag) { bus_dma_tag_destroy(ch->dma.work_tag); ch->dma.work_tag = NULL; } } static void ahci_slotsalloc(device_t dev) { struct ahci_channel *ch = device_get_softc(dev); int i; /* Alloc and setup command/dma slots */ bzero(ch->slot, sizeof(ch->slot)); for (i = 0; i < ch->numslots; i++) { struct ahci_slot *slot = &ch->slot[i]; slot->ch = ch; slot->slot = i; slot->state = AHCI_SLOT_EMPTY; slot->ccb = NULL; callout_init_mtx(&slot->timeout, &ch->mtx, 0); if (bus_dmamap_create(ch->dma.data_tag, 0, &slot->dma.data_map)) device_printf(ch->dev, "FAILURE - create data_map\n"); } } static void ahci_slotsfree(device_t dev) { struct ahci_channel *ch = device_get_softc(dev); int i; /* Free all dma slots */ for (i = 0; i < ch->numslots; i++) { struct ahci_slot *slot = &ch->slot[i]; callout_drain(&slot->timeout); if (slot->dma.data_map) { bus_dmamap_destroy(ch->dma.data_tag, slot->dma.data_map); slot->dma.data_map = NULL; } } } static int ahci_phy_check_events(struct ahci_channel *ch, u_int32_t serr) { if (((ch->pm_level == 0) && (serr & ATA_SE_PHY_CHANGED)) || ((ch->pm_level != 0 || ch->listening) && (serr & ATA_SE_EXCHANGED))) { u_int32_t status = ATA_INL(ch->r_mem, AHCI_P_SSTS); union ccb *ccb; if (bootverbose) { if ((status & ATA_SS_DET_MASK) != ATA_SS_DET_NO_DEVICE) device_printf(ch->dev, "CONNECT requested\n"); else device_printf(ch->dev, "DISCONNECT requested\n"); } ahci_reset(ch); if ((ccb = xpt_alloc_ccb_nowait()) == NULL) return (0); if (xpt_create_path(&ccb->ccb_h.path, NULL, cam_sim_path(ch->sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { xpt_free_ccb(ccb); return (0); } xpt_rescan(ccb); return (1); } return (0); } static void ahci_cpd_check_events(struct ahci_channel *ch) { u_int32_t status; union ccb *ccb; device_t dev; if (ch->pm_level == 0) return; status = ATA_INL(ch->r_mem, AHCI_P_CMD); if ((status & AHCI_P_CMD_CPD) == 0) return; if (bootverbose) { dev = ch->dev; if (status & AHCI_P_CMD_CPS) { device_printf(dev, "COLD CONNECT requested\n"); } else device_printf(dev, "COLD DISCONNECT requested\n"); } ahci_reset(ch); if ((ccb = xpt_alloc_ccb_nowait()) == NULL) return; if (xpt_create_path(&ccb->ccb_h.path, NULL, cam_sim_path(ch->sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { xpt_free_ccb(ccb); return; } xpt_rescan(ccb); } static void ahci_notify_events(struct ahci_channel *ch, u_int32_t status) { struct cam_path *dpath; int i; if (ch->caps & AHCI_CAP_SSNTF) ATA_OUTL(ch->r_mem, AHCI_P_SNTF, status); if (bootverbose) device_printf(ch->dev, "SNTF 0x%04x\n", status); for (i = 0; i < 16; i++) { if ((status & (1 << i)) == 0) continue; if (xpt_create_path(&dpath, NULL, xpt_path_path_id(ch->path), i, 0) == CAM_REQ_CMP) { xpt_async(AC_SCSI_AEN, dpath, NULL); xpt_free_path(dpath); } } } static void ahci_done(struct ahci_channel *ch, union ccb *ccb) { mtx_assert(&ch->mtx, MA_OWNED); if ((ccb->ccb_h.func_code & XPT_FC_QUEUED) == 0 || ch->batch == 0) { xpt_done(ccb); return; } STAILQ_INSERT_TAIL(&ch->doneq, &ccb->ccb_h, sim_links.stqe); } static void ahci_ch_intr(void *arg) { struct ahci_channel *ch = (struct ahci_channel *)arg; uint32_t istatus; /* Read interrupt statuses. */ istatus = ATA_INL(ch->r_mem, AHCI_P_IS); if (istatus == 0) return; mtx_lock(&ch->mtx); ahci_ch_intr_main(ch, istatus); mtx_unlock(&ch->mtx); } static void ahci_ch_intr_direct(void *arg) { struct ahci_channel *ch = (struct ahci_channel *)arg; struct ccb_hdr *ccb_h; uint32_t istatus; STAILQ_HEAD(, ccb_hdr) tmp_doneq = STAILQ_HEAD_INITIALIZER(tmp_doneq); /* Read interrupt statuses. */ istatus = ATA_INL(ch->r_mem, AHCI_P_IS); if (istatus == 0) return; mtx_lock(&ch->mtx); ch->batch = 1; ahci_ch_intr_main(ch, istatus); ch->batch = 0; /* * Prevent the possibility of issues caused by processing the queue * while unlocked below by moving the contents to a local queue. */ STAILQ_CONCAT(&tmp_doneq, &ch->doneq); mtx_unlock(&ch->mtx); while ((ccb_h = STAILQ_FIRST(&tmp_doneq)) != NULL) { STAILQ_REMOVE_HEAD(&tmp_doneq, sim_links.stqe); xpt_done_direct((union ccb *)ccb_h); } } static void ahci_ch_pm(void *arg) { struct ahci_channel *ch = (struct ahci_channel *)arg; uint32_t work; if (ch->numrslots != 0) return; work = ATA_INL(ch->r_mem, AHCI_P_CMD); if (ch->pm_level == 4) work |= AHCI_P_CMD_PARTIAL; else work |= AHCI_P_CMD_SLUMBER; ATA_OUTL(ch->r_mem, AHCI_P_CMD, work); } static void ahci_ch_intr_main(struct ahci_channel *ch, uint32_t istatus) { uint32_t cstatus, serr = 0, sntf = 0, ok, err; enum ahci_err_type et; int i, ccs, port, reset = 0; /* Clear interrupt statuses. */ ATA_OUTL(ch->r_mem, AHCI_P_IS, istatus); /* Read command statuses. */ if (ch->numtslots != 0) cstatus = ATA_INL(ch->r_mem, AHCI_P_SACT); else cstatus = 0; if (ch->numrslots != ch->numtslots) cstatus |= ATA_INL(ch->r_mem, AHCI_P_CI); /* Read SNTF in one of possible ways. */ if ((istatus & AHCI_P_IX_SDB) && (ch->pm_present || ch->curr[0].atapi != 0)) { if (ch->caps & AHCI_CAP_SSNTF) sntf = ATA_INL(ch->r_mem, AHCI_P_SNTF); else if (ch->fbs_enabled) { u_int8_t *fis = ch->dma.rfis + 0x58; for (i = 0; i < 16; i++) { if (fis[1] & 0x80) { fis[1] &= 0x7f; sntf |= 1 << i; } fis += 256; } } else { u_int8_t *fis = ch->dma.rfis + 0x58; if (fis[1] & 0x80) sntf = (1 << (fis[1] & 0x0f)); } } /* Process PHY events */ if (istatus & (AHCI_P_IX_PC | AHCI_P_IX_PRC | AHCI_P_IX_OF | AHCI_P_IX_IF | AHCI_P_IX_HBD | AHCI_P_IX_HBF | AHCI_P_IX_TFE)) { serr = ATA_INL(ch->r_mem, AHCI_P_SERR); if (serr) { ATA_OUTL(ch->r_mem, AHCI_P_SERR, serr); reset = ahci_phy_check_events(ch, serr); } } /* Process cold presence detection events */ if ((istatus & AHCI_P_IX_CPD) && !reset) ahci_cpd_check_events(ch); /* Process command errors */ if (istatus & (AHCI_P_IX_OF | AHCI_P_IX_IF | AHCI_P_IX_HBD | AHCI_P_IX_HBF | AHCI_P_IX_TFE)) { ccs = (ATA_INL(ch->r_mem, AHCI_P_CMD) & AHCI_P_CMD_CCS_MASK) >> AHCI_P_CMD_CCS_SHIFT; //device_printf(dev, "%s ERROR is %08x cs %08x ss %08x rs %08x tfd %02x serr %08x fbs %08x ccs %d\n", // __func__, istatus, cstatus, sstatus, ch->rslots, ATA_INL(ch->r_mem, AHCI_P_TFD), // serr, ATA_INL(ch->r_mem, AHCI_P_FBS), ccs); port = -1; if (ch->fbs_enabled) { uint32_t fbs = ATA_INL(ch->r_mem, AHCI_P_FBS); if (fbs & AHCI_P_FBS_SDE) { port = (fbs & AHCI_P_FBS_DWE) >> AHCI_P_FBS_DWE_SHIFT; } else { for (i = 0; i < 16; i++) { if (ch->numrslotspd[i] == 0) continue; if (port == -1) port = i; else if (port != i) { port = -2; break; } } } } err = ch->rslots & cstatus; } else { ccs = 0; err = 0; port = -1; } /* Complete all successfull commands. */ ok = ch->rslots & ~cstatus; for (i = 0; i < ch->numslots; i++) { if ((ok >> i) & 1) ahci_end_transaction(&ch->slot[i], AHCI_ERR_NONE); } /* On error, complete the rest of commands with error statuses. */ if (err) { if (ch->frozen) { union ccb *fccb = ch->frozen; ch->frozen = NULL; fccb->ccb_h.status = CAM_REQUEUE_REQ | CAM_RELEASE_SIMQ; if (!(fccb->ccb_h.status & CAM_DEV_QFRZN)) { xpt_freeze_devq(fccb->ccb_h.path, 1); fccb->ccb_h.status |= CAM_DEV_QFRZN; } ahci_done(ch, fccb); } for (i = 0; i < ch->numslots; i++) { /* XXX: reqests in loading state. */ if (((err >> i) & 1) == 0) continue; if (port >= 0 && ch->slot[i].ccb->ccb_h.target_id != port) continue; if (istatus & AHCI_P_IX_TFE) { if (port != -2) { /* Task File Error */ if (ch->numtslotspd[ ch->slot[i].ccb->ccb_h.target_id] == 0) { /* Untagged operation. */ if (i == ccs) et = AHCI_ERR_TFE; else et = AHCI_ERR_INNOCENT; } else { /* Tagged operation. */ et = AHCI_ERR_NCQ; } } else { et = AHCI_ERR_TFE; ch->fatalerr = 1; } } else if (istatus & AHCI_P_IX_IF) { if (ch->numtslots == 0 && i != ccs && port != -2) et = AHCI_ERR_INNOCENT; else et = AHCI_ERR_SATA; } else et = AHCI_ERR_INVALID; ahci_end_transaction(&ch->slot[i], et); } /* * We can't reinit port if there are some other * commands active, use resume to complete them. */ if (ch->rslots != 0 && !ch->recoverycmd) ATA_OUTL(ch->r_mem, AHCI_P_FBS, AHCI_P_FBS_EN | AHCI_P_FBS_DEC); } /* Process NOTIFY events */ if (sntf) ahci_notify_events(ch, sntf); } /* Must be called with channel locked. */ static int ahci_check_collision(struct ahci_channel *ch, union ccb *ccb) { int t = ccb->ccb_h.target_id; if ((ccb->ccb_h.func_code == XPT_ATA_IO) && (ccb->ataio.cmd.flags & CAM_ATAIO_FPDMA)) { /* Tagged command while we have no supported tag free. */ if (((~ch->oslots) & (0xffffffff >> (32 - ch->curr[t].tags))) == 0) return (1); /* If we have FBS */ if (ch->fbs_enabled) { /* Tagged command while untagged are active. */ if (ch->numrslotspd[t] != 0 && ch->numtslotspd[t] == 0) return (1); } else { /* Tagged command while untagged are active. */ if (ch->numrslots != 0 && ch->numtslots == 0) return (1); /* Tagged command while tagged to other target is active. */ if (ch->numtslots != 0 && ch->taggedtarget != ccb->ccb_h.target_id) return (1); } } else { /* If we have FBS */ if (ch->fbs_enabled) { /* Untagged command while tagged are active. */ if (ch->numrslotspd[t] != 0 && ch->numtslotspd[t] != 0) return (1); } else { /* Untagged command while tagged are active. */ if (ch->numrslots != 0 && ch->numtslots != 0) return (1); } } if ((ccb->ccb_h.func_code == XPT_ATA_IO) && (ccb->ataio.cmd.flags & (CAM_ATAIO_CONTROL | CAM_ATAIO_NEEDRESULT))) { /* Atomic command while anything active. */ if (ch->numrslots != 0) return (1); } /* We have some atomic command running. */ if (ch->aslots != 0) return (1); return (0); } /* Must be called with channel locked. */ static void ahci_begin_transaction(struct ahci_channel *ch, union ccb *ccb) { struct ahci_slot *slot; int tag, tags; /* Choose empty slot. */ tags = ch->numslots; if ((ccb->ccb_h.func_code == XPT_ATA_IO) && (ccb->ataio.cmd.flags & CAM_ATAIO_FPDMA)) tags = ch->curr[ccb->ccb_h.target_id].tags; if (ch->lastslot + 1 < tags) tag = ffs(~(ch->oslots >> (ch->lastslot + 1))); else tag = 0; if (tag == 0 || tag + ch->lastslot >= tags) tag = ffs(~ch->oslots) - 1; else tag += ch->lastslot; ch->lastslot = tag; /* Occupy chosen slot. */ slot = &ch->slot[tag]; slot->ccb = ccb; /* Stop PM timer. */ if (ch->numrslots == 0 && ch->pm_level > 3) callout_stop(&ch->pm_timer); /* Update channel stats. */ ch->oslots |= (1 << tag); ch->numrslots++; ch->numrslotspd[ccb->ccb_h.target_id]++; if ((ccb->ccb_h.func_code == XPT_ATA_IO) && (ccb->ataio.cmd.flags & CAM_ATAIO_FPDMA)) { ch->numtslots++; ch->numtslotspd[ccb->ccb_h.target_id]++; ch->taggedtarget = ccb->ccb_h.target_id; } if ((ccb->ccb_h.func_code == XPT_ATA_IO) && (ccb->ataio.cmd.flags & (CAM_ATAIO_CONTROL | CAM_ATAIO_NEEDRESULT))) ch->aslots |= (1 << tag); if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) { slot->state = AHCI_SLOT_LOADING; bus_dmamap_load_ccb(ch->dma.data_tag, slot->dma.data_map, ccb, ahci_dmasetprd, slot, 0); } else { slot->dma.nsegs = 0; ahci_execute_transaction(slot); } } /* Locked by busdma engine. */ static void ahci_dmasetprd(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { struct ahci_slot *slot = arg; struct ahci_channel *ch = slot->ch; struct ahci_cmd_tab *ctp; struct ahci_dma_prd *prd; int i; if (error) { device_printf(ch->dev, "DMA load error\n"); ahci_end_transaction(slot, AHCI_ERR_INVALID); return; } KASSERT(nsegs <= AHCI_SG_ENTRIES, ("too many DMA segment entries\n")); /* Get a piece of the workspace for this request */ ctp = (struct ahci_cmd_tab *) (ch->dma.work + AHCI_CT_OFFSET + (AHCI_CT_SIZE * slot->slot)); /* Fill S/G table */ prd = &ctp->prd_tab[0]; for (i = 0; i < nsegs; i++) { prd[i].dba = htole64(segs[i].ds_addr); prd[i].dbc = htole32((segs[i].ds_len - 1) & AHCI_PRD_MASK); } slot->dma.nsegs = nsegs; bus_dmamap_sync(ch->dma.data_tag, slot->dma.data_map, ((slot->ccb->ccb_h.flags & CAM_DIR_IN) ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE)); ahci_execute_transaction(slot); } /* Must be called with channel locked. */ static void ahci_execute_transaction(struct ahci_slot *slot) { struct ahci_channel *ch = slot->ch; struct ahci_cmd_tab *ctp; struct ahci_cmd_list *clp; union ccb *ccb = slot->ccb; int port = ccb->ccb_h.target_id & 0x0f; int fis_size, i, softreset; uint8_t *fis = ch->dma.rfis + 0x40; uint8_t val; /* Get a piece of the workspace for this request */ ctp = (struct ahci_cmd_tab *) (ch->dma.work + AHCI_CT_OFFSET + (AHCI_CT_SIZE * slot->slot)); /* Setup the FIS for this request */ if (!(fis_size = ahci_setup_fis(ch, ctp, ccb, slot->slot))) { device_printf(ch->dev, "Setting up SATA FIS failed\n"); ahci_end_transaction(slot, AHCI_ERR_INVALID); return; } /* Setup the command list entry */ clp = (struct ahci_cmd_list *) (ch->dma.work + AHCI_CL_OFFSET + (AHCI_CL_SIZE * slot->slot)); clp->cmd_flags = htole16( (ccb->ccb_h.flags & CAM_DIR_OUT ? AHCI_CMD_WRITE : 0) | (ccb->ccb_h.func_code == XPT_SCSI_IO ? (AHCI_CMD_ATAPI | AHCI_CMD_PREFETCH) : 0) | (fis_size / sizeof(u_int32_t)) | (port << 12)); clp->prd_length = htole16(slot->dma.nsegs); /* Special handling for Soft Reset command. */ if ((ccb->ccb_h.func_code == XPT_ATA_IO) && (ccb->ataio.cmd.flags & CAM_ATAIO_CONTROL)) { if (ccb->ataio.cmd.control & ATA_A_RESET) { softreset = 1; /* Kick controller into sane state */ ahci_stop(ch); ahci_clo(ch); ahci_start(ch, 0); clp->cmd_flags |= AHCI_CMD_RESET | AHCI_CMD_CLR_BUSY; } else { softreset = 2; /* Prepare FIS receive area for check. */ for (i = 0; i < 20; i++) fis[i] = 0xff; } } else softreset = 0; clp->bytecount = 0; clp->cmd_table_phys = htole64(ch->dma.work_bus + AHCI_CT_OFFSET + (AHCI_CT_SIZE * slot->slot)); bus_dmamap_sync(ch->dma.work_tag, ch->dma.work_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); bus_dmamap_sync(ch->dma.rfis_tag, ch->dma.rfis_map, BUS_DMASYNC_PREREAD); /* Set ACTIVE bit for NCQ commands. */ if ((ccb->ccb_h.func_code == XPT_ATA_IO) && (ccb->ataio.cmd.flags & CAM_ATAIO_FPDMA)) { ATA_OUTL(ch->r_mem, AHCI_P_SACT, 1 << slot->slot); } /* If FBS is enabled, set PMP port. */ if (ch->fbs_enabled) { ATA_OUTL(ch->r_mem, AHCI_P_FBS, AHCI_P_FBS_EN | (port << AHCI_P_FBS_DEV_SHIFT)); } /* Issue command to the controller. */ slot->state = AHCI_SLOT_RUNNING; ch->rslots |= (1 << slot->slot); ATA_OUTL(ch->r_mem, AHCI_P_CI, (1 << slot->slot)); /* Device reset commands doesn't interrupt. Poll them. */ if (ccb->ccb_h.func_code == XPT_ATA_IO && (ccb->ataio.cmd.command == ATA_DEVICE_RESET || softreset)) { int count, timeout = ccb->ccb_h.timeout * 100; enum ahci_err_type et = AHCI_ERR_NONE; for (count = 0; count < timeout; count++) { DELAY(10); if (!(ATA_INL(ch->r_mem, AHCI_P_CI) & (1 << slot->slot))) break; if ((ATA_INL(ch->r_mem, AHCI_P_TFD) & ATA_S_ERROR) && softreset != 1) { #if 0 device_printf(ch->dev, "Poll error on slot %d, TFD: %04x\n", slot->slot, ATA_INL(ch->r_mem, AHCI_P_TFD)); #endif et = AHCI_ERR_TFE; break; } /* Workaround for ATI SB600/SB700 chipsets. */ if (ccb->ccb_h.target_id == 15 && (ch->quirks & AHCI_Q_ATI_PMP_BUG) && (ATA_INL(ch->r_mem, AHCI_P_IS) & AHCI_P_IX_IPM)) { et = AHCI_ERR_TIMEOUT; break; } } /* * Marvell HBAs with non-RAID firmware do not wait for * readiness after soft reset, so we have to wait here. * Marvell RAIDs do not have this problem, but instead * sometimes forget to update FIS receive area, breaking * this wait. */ if ((ch->quirks & AHCI_Q_NOBSYRES) == 0 && (ch->quirks & AHCI_Q_ATI_PMP_BUG) == 0 && softreset == 2 && et == AHCI_ERR_NONE) { while ((val = fis[2]) & ATA_S_BUSY) { DELAY(10); if (count++ >= timeout) break; } } if (timeout && (count >= timeout)) { device_printf(ch->dev, "Poll timeout on slot %d port %d\n", slot->slot, port); device_printf(ch->dev, "is %08x cs %08x ss %08x " "rs %08x tfd %02x serr %08x cmd %08x\n", ATA_INL(ch->r_mem, AHCI_P_IS), ATA_INL(ch->r_mem, AHCI_P_CI), ATA_INL(ch->r_mem, AHCI_P_SACT), ch->rslots, ATA_INL(ch->r_mem, AHCI_P_TFD), ATA_INL(ch->r_mem, AHCI_P_SERR), ATA_INL(ch->r_mem, AHCI_P_CMD)); et = AHCI_ERR_TIMEOUT; } /* Kick controller into sane state and enable FBS. */ if (softreset == 2) ch->eslots |= (1 << slot->slot); ahci_end_transaction(slot, et); return; } /* Start command execution timeout */ callout_reset_sbt(&slot->timeout, SBT_1MS * ccb->ccb_h.timeout / 2, 0, (timeout_t*)ahci_timeout, slot, 0); return; } /* Must be called with channel locked. */ static void ahci_process_timeout(struct ahci_channel *ch) { int i; mtx_assert(&ch->mtx, MA_OWNED); /* Handle the rest of commands. */ for (i = 0; i < ch->numslots; i++) { /* Do we have a running request on slot? */ if (ch->slot[i].state < AHCI_SLOT_RUNNING) continue; ahci_end_transaction(&ch->slot[i], AHCI_ERR_TIMEOUT); } } /* Must be called with channel locked. */ static void ahci_rearm_timeout(struct ahci_channel *ch) { int i; mtx_assert(&ch->mtx, MA_OWNED); for (i = 0; i < ch->numslots; i++) { struct ahci_slot *slot = &ch->slot[i]; /* Do we have a running request on slot? */ if (slot->state < AHCI_SLOT_RUNNING) continue; if ((ch->toslots & (1 << i)) == 0) continue; callout_reset_sbt(&slot->timeout, SBT_1MS * slot->ccb->ccb_h.timeout / 2, 0, (timeout_t*)ahci_timeout, slot, 0); } } /* Locked by callout mechanism. */ static void ahci_timeout(struct ahci_slot *slot) { struct ahci_channel *ch = slot->ch; device_t dev = ch->dev; uint32_t sstatus; int ccs; int i; /* Check for stale timeout. */ if (slot->state < AHCI_SLOT_RUNNING) return; /* Check if slot was not being executed last time we checked. */ if (slot->state < AHCI_SLOT_EXECUTING) { /* Check if slot started executing. */ sstatus = ATA_INL(ch->r_mem, AHCI_P_SACT); ccs = (ATA_INL(ch->r_mem, AHCI_P_CMD) & AHCI_P_CMD_CCS_MASK) >> AHCI_P_CMD_CCS_SHIFT; if ((sstatus & (1 << slot->slot)) != 0 || ccs == slot->slot || ch->fbs_enabled || ch->wrongccs) slot->state = AHCI_SLOT_EXECUTING; else if ((ch->rslots & (1 << ccs)) == 0) { ch->wrongccs = 1; slot->state = AHCI_SLOT_EXECUTING; } callout_reset_sbt(&slot->timeout, SBT_1MS * slot->ccb->ccb_h.timeout / 2, 0, (timeout_t*)ahci_timeout, slot, 0); return; } device_printf(dev, "Timeout on slot %d port %d\n", slot->slot, slot->ccb->ccb_h.target_id & 0x0f); device_printf(dev, "is %08x cs %08x ss %08x rs %08x tfd %02x " "serr %08x cmd %08x\n", ATA_INL(ch->r_mem, AHCI_P_IS), ATA_INL(ch->r_mem, AHCI_P_CI), ATA_INL(ch->r_mem, AHCI_P_SACT), ch->rslots, ATA_INL(ch->r_mem, AHCI_P_TFD), ATA_INL(ch->r_mem, AHCI_P_SERR), ATA_INL(ch->r_mem, AHCI_P_CMD)); /* Handle frozen command. */ if (ch->frozen) { union ccb *fccb = ch->frozen; ch->frozen = NULL; fccb->ccb_h.status = CAM_REQUEUE_REQ | CAM_RELEASE_SIMQ; if (!(fccb->ccb_h.status & CAM_DEV_QFRZN)) { xpt_freeze_devq(fccb->ccb_h.path, 1); fccb->ccb_h.status |= CAM_DEV_QFRZN; } ahci_done(ch, fccb); } if (!ch->fbs_enabled && !ch->wrongccs) { /* Without FBS we know real timeout source. */ ch->fatalerr = 1; /* Handle command with timeout. */ ahci_end_transaction(&ch->slot[slot->slot], AHCI_ERR_TIMEOUT); /* Handle the rest of commands. */ for (i = 0; i < ch->numslots; i++) { /* Do we have a running request on slot? */ if (ch->slot[i].state < AHCI_SLOT_RUNNING) continue; ahci_end_transaction(&ch->slot[i], AHCI_ERR_INNOCENT); } } else { /* With FBS we wait for other commands timeout and pray. */ if (ch->toslots == 0) xpt_freeze_simq(ch->sim, 1); ch->toslots |= (1 << slot->slot); if ((ch->rslots & ~ch->toslots) == 0) ahci_process_timeout(ch); else device_printf(dev, " ... waiting for slots %08x\n", ch->rslots & ~ch->toslots); } } /* Must be called with channel locked. */ static void ahci_end_transaction(struct ahci_slot *slot, enum ahci_err_type et) { struct ahci_channel *ch = slot->ch; union ccb *ccb = slot->ccb; struct ahci_cmd_list *clp; int lastto; uint32_t sig; bus_dmamap_sync(ch->dma.work_tag, ch->dma.work_map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); clp = (struct ahci_cmd_list *) (ch->dma.work + AHCI_CL_OFFSET + (AHCI_CL_SIZE * slot->slot)); /* Read result registers to the result struct * May be incorrect if several commands finished same time, * so read only when sure or have to. */ if (ccb->ccb_h.func_code == XPT_ATA_IO) { struct ata_res *res = &ccb->ataio.res; if ((et == AHCI_ERR_TFE) || (ccb->ataio.cmd.flags & CAM_ATAIO_NEEDRESULT)) { u_int8_t *fis = ch->dma.rfis + 0x40; bus_dmamap_sync(ch->dma.rfis_tag, ch->dma.rfis_map, BUS_DMASYNC_POSTREAD); if (ch->fbs_enabled) { fis += ccb->ccb_h.target_id * 256; res->status = fis[2]; res->error = fis[3]; } else { uint16_t tfd = ATA_INL(ch->r_mem, AHCI_P_TFD); res->status = tfd; res->error = tfd >> 8; } res->lba_low = fis[4]; res->lba_mid = fis[5]; res->lba_high = fis[6]; res->device = fis[7]; res->lba_low_exp = fis[8]; res->lba_mid_exp = fis[9]; res->lba_high_exp = fis[10]; res->sector_count = fis[12]; res->sector_count_exp = fis[13]; /* * Some weird controllers do not return signature in * FIS receive area. Read it from PxSIG register. */ if ((ch->quirks & AHCI_Q_ALTSIG) && (ccb->ataio.cmd.flags & CAM_ATAIO_CONTROL) && (ccb->ataio.cmd.control & ATA_A_RESET) == 0) { sig = ATA_INL(ch->r_mem, AHCI_P_SIG); res->lba_high = sig >> 24; res->lba_mid = sig >> 16; res->lba_low = sig >> 8; res->sector_count = sig; } } else bzero(res, sizeof(*res)); if ((ccb->ataio.cmd.flags & CAM_ATAIO_FPDMA) == 0 && (ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE && (ch->quirks & AHCI_Q_NOCOUNT) == 0) { ccb->ataio.resid = ccb->ataio.dxfer_len - le32toh(clp->bytecount); } } else { if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE && (ch->quirks & AHCI_Q_NOCOUNT) == 0) { ccb->csio.resid = ccb->csio.dxfer_len - le32toh(clp->bytecount); } } if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) { bus_dmamap_sync(ch->dma.data_tag, slot->dma.data_map, (ccb->ccb_h.flags & CAM_DIR_IN) ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(ch->dma.data_tag, slot->dma.data_map); } if (et != AHCI_ERR_NONE) ch->eslots |= (1 << slot->slot); /* In case of error, freeze device for proper recovery. */ if ((et != AHCI_ERR_NONE) && (!ch->recoverycmd) && !(ccb->ccb_h.status & CAM_DEV_QFRZN)) { xpt_freeze_devq(ccb->ccb_h.path, 1); ccb->ccb_h.status |= CAM_DEV_QFRZN; } /* Set proper result status. */ ccb->ccb_h.status &= ~CAM_STATUS_MASK; switch (et) { case AHCI_ERR_NONE: ccb->ccb_h.status |= CAM_REQ_CMP; if (ccb->ccb_h.func_code == XPT_SCSI_IO) ccb->csio.scsi_status = SCSI_STATUS_OK; break; case AHCI_ERR_INVALID: ch->fatalerr = 1; ccb->ccb_h.status |= CAM_REQ_INVALID; break; case AHCI_ERR_INNOCENT: ccb->ccb_h.status |= CAM_REQUEUE_REQ; break; case AHCI_ERR_TFE: case AHCI_ERR_NCQ: if (ccb->ccb_h.func_code == XPT_SCSI_IO) { ccb->ccb_h.status |= CAM_SCSI_STATUS_ERROR; ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; } else { ccb->ccb_h.status |= CAM_ATA_STATUS_ERROR; } break; case AHCI_ERR_SATA: ch->fatalerr = 1; if (!ch->recoverycmd) { xpt_freeze_simq(ch->sim, 1); ccb->ccb_h.status &= ~CAM_STATUS_MASK; ccb->ccb_h.status |= CAM_RELEASE_SIMQ; } ccb->ccb_h.status |= CAM_UNCOR_PARITY; break; case AHCI_ERR_TIMEOUT: if (!ch->recoverycmd) { xpt_freeze_simq(ch->sim, 1); ccb->ccb_h.status &= ~CAM_STATUS_MASK; ccb->ccb_h.status |= CAM_RELEASE_SIMQ; } ccb->ccb_h.status |= CAM_CMD_TIMEOUT; break; default: ch->fatalerr = 1; ccb->ccb_h.status |= CAM_REQ_CMP_ERR; } /* Free slot. */ ch->oslots &= ~(1 << slot->slot); ch->rslots &= ~(1 << slot->slot); ch->aslots &= ~(1 << slot->slot); slot->state = AHCI_SLOT_EMPTY; slot->ccb = NULL; /* Update channel stats. */ ch->numrslots--; ch->numrslotspd[ccb->ccb_h.target_id]--; if ((ccb->ccb_h.func_code == XPT_ATA_IO) && (ccb->ataio.cmd.flags & CAM_ATAIO_FPDMA)) { ch->numtslots--; ch->numtslotspd[ccb->ccb_h.target_id]--; } /* Cancel timeout state if request completed normally. */ if (et != AHCI_ERR_TIMEOUT) { lastto = (ch->toslots == (1 << slot->slot)); ch->toslots &= ~(1 << slot->slot); if (lastto) xpt_release_simq(ch->sim, TRUE); } /* If it was first request of reset sequence and there is no error, * proceed to second request. */ if ((ccb->ccb_h.func_code == XPT_ATA_IO) && (ccb->ataio.cmd.flags & CAM_ATAIO_CONTROL) && (ccb->ataio.cmd.control & ATA_A_RESET) && et == AHCI_ERR_NONE) { ccb->ataio.cmd.control &= ~ATA_A_RESET; ahci_begin_transaction(ch, ccb); return; } /* If it was our READ LOG command - process it. */ if (ccb->ccb_h.recovery_type == RECOVERY_READ_LOG) { ahci_process_read_log(ch, ccb); /* If it was our REQUEST SENSE command - process it. */ } else if (ccb->ccb_h.recovery_type == RECOVERY_REQUEST_SENSE) { ahci_process_request_sense(ch, ccb); /* If it was NCQ or ATAPI command error, put result on hold. */ } else if (et == AHCI_ERR_NCQ || ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_SCSI_STATUS_ERROR && (ccb->ccb_h.flags & CAM_DIS_AUTOSENSE) == 0)) { ch->hold[slot->slot] = ccb; ch->numhslots++; } else ahci_done(ch, ccb); /* If we have no other active commands, ... */ if (ch->rslots == 0) { /* if there was fatal error - reset port. */ if (ch->toslots != 0 || ch->fatalerr) { ahci_reset(ch); } else { /* if we have slots in error, we can reinit port. */ if (ch->eslots != 0) { ahci_stop(ch); ahci_clo(ch); ahci_start(ch, 1); } /* if there commands on hold, we can do READ LOG. */ if (!ch->recoverycmd && ch->numhslots) ahci_issue_recovery(ch); } /* If all the rest of commands are in timeout - give them chance. */ } else if ((ch->rslots & ~ch->toslots) == 0 && et != AHCI_ERR_TIMEOUT) ahci_rearm_timeout(ch); /* Unfreeze frozen command. */ if (ch->frozen && !ahci_check_collision(ch, ch->frozen)) { union ccb *fccb = ch->frozen; ch->frozen = NULL; ahci_begin_transaction(ch, fccb); xpt_release_simq(ch->sim, TRUE); } /* Start PM timer. */ if (ch->numrslots == 0 && ch->pm_level > 3 && (ch->curr[ch->pm_present ? 15 : 0].caps & CTS_SATA_CAPS_D_PMREQ)) { callout_schedule(&ch->pm_timer, (ch->pm_level == 4) ? hz / 1000 : hz / 8); } } static void ahci_issue_recovery(struct ahci_channel *ch) { union ccb *ccb; struct ccb_ataio *ataio; struct ccb_scsiio *csio; int i; /* Find some held command. */ for (i = 0; i < ch->numslots; i++) { if (ch->hold[i]) break; } ccb = xpt_alloc_ccb_nowait(); if (ccb == NULL) { device_printf(ch->dev, "Unable to allocate recovery command\n"); completeall: /* We can't do anything -- complete held commands. */ for (i = 0; i < ch->numslots; i++) { if (ch->hold[i] == NULL) continue; ch->hold[i]->ccb_h.status &= ~CAM_STATUS_MASK; ch->hold[i]->ccb_h.status |= CAM_RESRC_UNAVAIL; ahci_done(ch, ch->hold[i]); ch->hold[i] = NULL; ch->numhslots--; } ahci_reset(ch); return; } ccb->ccb_h = ch->hold[i]->ccb_h; /* Reuse old header. */ if (ccb->ccb_h.func_code == XPT_ATA_IO) { /* READ LOG */ ccb->ccb_h.recovery_type = RECOVERY_READ_LOG; ccb->ccb_h.func_code = XPT_ATA_IO; ccb->ccb_h.flags = CAM_DIR_IN; ccb->ccb_h.timeout = 1000; /* 1s should be enough. */ ataio = &ccb->ataio; ataio->data_ptr = malloc(512, M_AHCI, M_NOWAIT); if (ataio->data_ptr == NULL) { xpt_free_ccb(ccb); device_printf(ch->dev, "Unable to allocate memory for READ LOG command\n"); goto completeall; } ataio->dxfer_len = 512; bzero(&ataio->cmd, sizeof(ataio->cmd)); ataio->cmd.flags = CAM_ATAIO_48BIT; ataio->cmd.command = 0x2F; /* READ LOG EXT */ ataio->cmd.sector_count = 1; ataio->cmd.sector_count_exp = 0; ataio->cmd.lba_low = 0x10; ataio->cmd.lba_mid = 0; ataio->cmd.lba_mid_exp = 0; } else { /* REQUEST SENSE */ ccb->ccb_h.recovery_type = RECOVERY_REQUEST_SENSE; ccb->ccb_h.recovery_slot = i; ccb->ccb_h.func_code = XPT_SCSI_IO; ccb->ccb_h.flags = CAM_DIR_IN; ccb->ccb_h.status = 0; ccb->ccb_h.timeout = 1000; /* 1s should be enough. */ csio = &ccb->csio; csio->data_ptr = (void *)&ch->hold[i]->csio.sense_data; csio->dxfer_len = ch->hold[i]->csio.sense_len; csio->cdb_len = 6; bzero(&csio->cdb_io, sizeof(csio->cdb_io)); csio->cdb_io.cdb_bytes[0] = 0x03; csio->cdb_io.cdb_bytes[4] = csio->dxfer_len; } /* Freeze SIM while doing recovery. */ ch->recoverycmd = 1; xpt_freeze_simq(ch->sim, 1); ahci_begin_transaction(ch, ccb); } static void ahci_process_read_log(struct ahci_channel *ch, union ccb *ccb) { uint8_t *data; struct ata_res *res; int i; ch->recoverycmd = 0; data = ccb->ataio.data_ptr; if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP && (data[0] & 0x80) == 0) { for (i = 0; i < ch->numslots; i++) { if (!ch->hold[i]) continue; if (ch->hold[i]->ccb_h.func_code != XPT_ATA_IO) continue; if ((data[0] & 0x1F) == i) { res = &ch->hold[i]->ataio.res; res->status = data[2]; res->error = data[3]; res->lba_low = data[4]; res->lba_mid = data[5]; res->lba_high = data[6]; res->device = data[7]; res->lba_low_exp = data[8]; res->lba_mid_exp = data[9]; res->lba_high_exp = data[10]; res->sector_count = data[12]; res->sector_count_exp = data[13]; } else { ch->hold[i]->ccb_h.status &= ~CAM_STATUS_MASK; ch->hold[i]->ccb_h.status |= CAM_REQUEUE_REQ; } ahci_done(ch, ch->hold[i]); ch->hold[i] = NULL; ch->numhslots--; } } else { if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) device_printf(ch->dev, "Error while READ LOG EXT\n"); else if ((data[0] & 0x80) == 0) { device_printf(ch->dev, "Non-queued command error in READ LOG EXT\n"); } for (i = 0; i < ch->numslots; i++) { if (!ch->hold[i]) continue; if (ch->hold[i]->ccb_h.func_code != XPT_ATA_IO) continue; ahci_done(ch, ch->hold[i]); ch->hold[i] = NULL; ch->numhslots--; } } free(ccb->ataio.data_ptr, M_AHCI); xpt_free_ccb(ccb); xpt_release_simq(ch->sim, TRUE); } static void ahci_process_request_sense(struct ahci_channel *ch, union ccb *ccb) { int i; ch->recoverycmd = 0; i = ccb->ccb_h.recovery_slot; if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) { ch->hold[i]->ccb_h.status |= CAM_AUTOSNS_VALID; } else { ch->hold[i]->ccb_h.status &= ~CAM_STATUS_MASK; ch->hold[i]->ccb_h.status |= CAM_AUTOSENSE_FAIL; } ahci_done(ch, ch->hold[i]); ch->hold[i] = NULL; ch->numhslots--; xpt_free_ccb(ccb); xpt_release_simq(ch->sim, TRUE); } static void ahci_start(struct ahci_channel *ch, int fbs) { u_int32_t cmd; /* Clear SATA error register */ ATA_OUTL(ch->r_mem, AHCI_P_SERR, 0xFFFFFFFF); /* Clear any interrupts pending on this channel */ ATA_OUTL(ch->r_mem, AHCI_P_IS, 0xFFFFFFFF); /* Configure FIS-based switching if supported. */ if (ch->chcaps & AHCI_P_CMD_FBSCP) { ch->fbs_enabled = (fbs && ch->pm_present) ? 1 : 0; ATA_OUTL(ch->r_mem, AHCI_P_FBS, ch->fbs_enabled ? AHCI_P_FBS_EN : 0); } /* Start operations on this channel */ cmd = ATA_INL(ch->r_mem, AHCI_P_CMD); cmd &= ~AHCI_P_CMD_PMA; ATA_OUTL(ch->r_mem, AHCI_P_CMD, cmd | AHCI_P_CMD_ST | (ch->pm_present ? AHCI_P_CMD_PMA : 0)); } static void ahci_stop(struct ahci_channel *ch) { u_int32_t cmd; int timeout; /* Kill all activity on this channel */ cmd = ATA_INL(ch->r_mem, AHCI_P_CMD); ATA_OUTL(ch->r_mem, AHCI_P_CMD, cmd & ~AHCI_P_CMD_ST); /* Wait for activity stop. */ timeout = 0; do { DELAY(10); if (timeout++ > 50000) { device_printf(ch->dev, "stopping AHCI engine failed\n"); break; } } while (ATA_INL(ch->r_mem, AHCI_P_CMD) & AHCI_P_CMD_CR); ch->eslots = 0; } static void ahci_clo(struct ahci_channel *ch) { u_int32_t cmd; int timeout; /* Issue Command List Override if supported */ if (ch->caps & AHCI_CAP_SCLO) { cmd = ATA_INL(ch->r_mem, AHCI_P_CMD); cmd |= AHCI_P_CMD_CLO; ATA_OUTL(ch->r_mem, AHCI_P_CMD, cmd); timeout = 0; do { DELAY(10); if (timeout++ > 50000) { device_printf(ch->dev, "executing CLO failed\n"); break; } } while (ATA_INL(ch->r_mem, AHCI_P_CMD) & AHCI_P_CMD_CLO); } } static void ahci_stop_fr(struct ahci_channel *ch) { u_int32_t cmd; int timeout; /* Kill all FIS reception on this channel */ cmd = ATA_INL(ch->r_mem, AHCI_P_CMD); ATA_OUTL(ch->r_mem, AHCI_P_CMD, cmd & ~AHCI_P_CMD_FRE); /* Wait for FIS reception stop. */ timeout = 0; do { DELAY(10); if (timeout++ > 50000) { device_printf(ch->dev, "stopping AHCI FR engine failed\n"); break; } } while (ATA_INL(ch->r_mem, AHCI_P_CMD) & AHCI_P_CMD_FR); } static void ahci_start_fr(struct ahci_channel *ch) { u_int32_t cmd; /* Start FIS reception on this channel */ cmd = ATA_INL(ch->r_mem, AHCI_P_CMD); ATA_OUTL(ch->r_mem, AHCI_P_CMD, cmd | AHCI_P_CMD_FRE); } static int ahci_wait_ready(struct ahci_channel *ch, int t, int t0) { int timeout = 0; uint32_t val; while ((val = ATA_INL(ch->r_mem, AHCI_P_TFD)) & (ATA_S_BUSY | ATA_S_DRQ)) { if (timeout > t) { if (t != 0) { device_printf(ch->dev, "AHCI reset: device not ready after %dms " "(tfd = %08x)\n", MAX(t, 0) + t0, val); } return (EBUSY); } DELAY(1000); timeout++; } if (bootverbose) device_printf(ch->dev, "AHCI reset: device ready after %dms\n", timeout + t0); return (0); } static void ahci_reset_to(void *arg) { struct ahci_channel *ch = arg; if (ch->resetting == 0) return; ch->resetting--; if (ahci_wait_ready(ch, ch->resetting == 0 ? -1 : 0, (310 - ch->resetting) * 100) == 0) { ch->resetting = 0; ahci_start(ch, 1); xpt_release_simq(ch->sim, TRUE); return; } if (ch->resetting == 0) { ahci_clo(ch); ahci_start(ch, 1); xpt_release_simq(ch->sim, TRUE); return; } callout_schedule(&ch->reset_timer, hz / 10); } static void ahci_reset(struct ahci_channel *ch) { struct ahci_controller *ctlr = device_get_softc(device_get_parent(ch->dev)); int i; xpt_freeze_simq(ch->sim, 1); if (bootverbose) device_printf(ch->dev, "AHCI reset...\n"); /* Forget about previous reset. */ if (ch->resetting) { ch->resetting = 0; callout_stop(&ch->reset_timer); xpt_release_simq(ch->sim, TRUE); } /* Requeue freezed command. */ if (ch->frozen) { union ccb *fccb = ch->frozen; ch->frozen = NULL; fccb->ccb_h.status = CAM_REQUEUE_REQ | CAM_RELEASE_SIMQ; if (!(fccb->ccb_h.status & CAM_DEV_QFRZN)) { xpt_freeze_devq(fccb->ccb_h.path, 1); fccb->ccb_h.status |= CAM_DEV_QFRZN; } ahci_done(ch, fccb); } /* Kill the engine and requeue all running commands. */ ahci_stop(ch); for (i = 0; i < ch->numslots; i++) { /* Do we have a running request on slot? */ if (ch->slot[i].state < AHCI_SLOT_RUNNING) continue; /* XXX; Commands in loading state. */ ahci_end_transaction(&ch->slot[i], AHCI_ERR_INNOCENT); } for (i = 0; i < ch->numslots; i++) { if (!ch->hold[i]) continue; ahci_done(ch, ch->hold[i]); ch->hold[i] = NULL; ch->numhslots--; } if (ch->toslots != 0) xpt_release_simq(ch->sim, TRUE); ch->eslots = 0; ch->toslots = 0; ch->wrongccs = 0; ch->fatalerr = 0; /* Tell the XPT about the event */ xpt_async(AC_BUS_RESET, ch->path, NULL); /* Disable port interrupts */ ATA_OUTL(ch->r_mem, AHCI_P_IE, 0); /* Reset and reconnect PHY, */ if (!ahci_sata_phy_reset(ch)) { if (bootverbose) device_printf(ch->dev, "AHCI reset: device not found\n"); ch->devices = 0; /* Enable wanted port interrupts */ ATA_OUTL(ch->r_mem, AHCI_P_IE, (((ch->pm_level != 0) ? AHCI_P_IX_CPD | AHCI_P_IX_MP : 0) | AHCI_P_IX_PRC | AHCI_P_IX_PC)); xpt_release_simq(ch->sim, TRUE); return; } if (bootverbose) device_printf(ch->dev, "AHCI reset: device found\n"); /* Wait for clearing busy status. */ if (ahci_wait_ready(ch, dumping ? 31000 : 0, 0)) { if (dumping) ahci_clo(ch); else ch->resetting = 310; } ch->devices = 1; /* Enable wanted port interrupts */ ATA_OUTL(ch->r_mem, AHCI_P_IE, (((ch->pm_level != 0) ? AHCI_P_IX_CPD | AHCI_P_IX_MP : 0) | AHCI_P_IX_TFE | AHCI_P_IX_HBF | AHCI_P_IX_HBD | AHCI_P_IX_IF | AHCI_P_IX_OF | ((ch->pm_level == 0) ? AHCI_P_IX_PRC : 0) | AHCI_P_IX_PC | AHCI_P_IX_DP | AHCI_P_IX_UF | (ctlr->ccc ? 0 : AHCI_P_IX_SDB) | AHCI_P_IX_DS | AHCI_P_IX_PS | (ctlr->ccc ? 0 : AHCI_P_IX_DHR))); if (ch->resetting) callout_reset(&ch->reset_timer, hz / 10, ahci_reset_to, ch); else { ahci_start(ch, 1); xpt_release_simq(ch->sim, TRUE); } } static int ahci_setup_fis(struct ahci_channel *ch, struct ahci_cmd_tab *ctp, union ccb *ccb, int tag) { u_int8_t *fis = &ctp->cfis[0]; bzero(fis, 20); fis[0] = 0x27; /* host to device */ fis[1] = (ccb->ccb_h.target_id & 0x0f); if (ccb->ccb_h.func_code == XPT_SCSI_IO) { fis[1] |= 0x80; fis[2] = ATA_PACKET_CMD; if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE && ch->curr[ccb->ccb_h.target_id].mode >= ATA_DMA) fis[3] = ATA_F_DMA; else { fis[5] = ccb->csio.dxfer_len; fis[6] = ccb->csio.dxfer_len >> 8; } fis[7] = ATA_D_LBA; fis[15] = ATA_A_4BIT; bcopy((ccb->ccb_h.flags & CAM_CDB_POINTER) ? ccb->csio.cdb_io.cdb_ptr : ccb->csio.cdb_io.cdb_bytes, ctp->acmd, ccb->csio.cdb_len); bzero(ctp->acmd + ccb->csio.cdb_len, 32 - ccb->csio.cdb_len); } else if ((ccb->ataio.cmd.flags & CAM_ATAIO_CONTROL) == 0) { fis[1] |= 0x80; fis[2] = ccb->ataio.cmd.command; fis[3] = ccb->ataio.cmd.features; fis[4] = ccb->ataio.cmd.lba_low; fis[5] = ccb->ataio.cmd.lba_mid; fis[6] = ccb->ataio.cmd.lba_high; fis[7] = ccb->ataio.cmd.device; fis[8] = ccb->ataio.cmd.lba_low_exp; fis[9] = ccb->ataio.cmd.lba_mid_exp; fis[10] = ccb->ataio.cmd.lba_high_exp; fis[11] = ccb->ataio.cmd.features_exp; if (ccb->ataio.cmd.flags & CAM_ATAIO_FPDMA) { fis[12] = tag << 3; fis[13] = 0; } else { fis[12] = ccb->ataio.cmd.sector_count; fis[13] = ccb->ataio.cmd.sector_count_exp; } fis[15] = ATA_A_4BIT; } else { fis[15] = ccb->ataio.cmd.control; } return (20); } static int ahci_sata_connect(struct ahci_channel *ch) { u_int32_t status; int timeout, found = 0; /* Wait up to 100ms for "connect well" */ for (timeout = 0; timeout < 1000 ; timeout++) { status = ATA_INL(ch->r_mem, AHCI_P_SSTS); if ((status & ATA_SS_DET_MASK) != ATA_SS_DET_NO_DEVICE) found = 1; if (((status & ATA_SS_DET_MASK) == ATA_SS_DET_PHY_ONLINE) && ((status & ATA_SS_SPD_MASK) != ATA_SS_SPD_NO_SPEED) && ((status & ATA_SS_IPM_MASK) == ATA_SS_IPM_ACTIVE)) break; if ((status & ATA_SS_DET_MASK) == ATA_SS_DET_PHY_OFFLINE) { if (bootverbose) { device_printf(ch->dev, "SATA offline status=%08x\n", status); } return (0); } if (found == 0 && timeout >= 100) break; DELAY(100); } if (timeout >= 1000 || !found) { if (bootverbose) { device_printf(ch->dev, "SATA connect timeout time=%dus status=%08x\n", timeout * 100, status); } return (0); } if (bootverbose) { device_printf(ch->dev, "SATA connect time=%dus status=%08x\n", timeout * 100, status); } /* Clear SATA error register */ ATA_OUTL(ch->r_mem, AHCI_P_SERR, 0xffffffff); return (1); } static int ahci_sata_phy_reset(struct ahci_channel *ch) { int sata_rev; uint32_t val; if (ch->listening) { val = ATA_INL(ch->r_mem, AHCI_P_CMD); val |= AHCI_P_CMD_SUD; ATA_OUTL(ch->r_mem, AHCI_P_CMD, val); ch->listening = 0; } sata_rev = ch->user[ch->pm_present ? 15 : 0].revision; if (sata_rev == 1) val = ATA_SC_SPD_SPEED_GEN1; else if (sata_rev == 2) val = ATA_SC_SPD_SPEED_GEN2; else if (sata_rev == 3) val = ATA_SC_SPD_SPEED_GEN3; else val = 0; ATA_OUTL(ch->r_mem, AHCI_P_SCTL, ATA_SC_DET_RESET | val | ATA_SC_IPM_DIS_PARTIAL | ATA_SC_IPM_DIS_SLUMBER); DELAY(1000); ATA_OUTL(ch->r_mem, AHCI_P_SCTL, ATA_SC_DET_IDLE | val | ((ch->pm_level > 0) ? 0 : (ATA_SC_IPM_DIS_PARTIAL | ATA_SC_IPM_DIS_SLUMBER))); if (!ahci_sata_connect(ch)) { if (ch->caps & AHCI_CAP_SSS) { val = ATA_INL(ch->r_mem, AHCI_P_CMD); val &= ~AHCI_P_CMD_SUD; ATA_OUTL(ch->r_mem, AHCI_P_CMD, val); ch->listening = 1; } else if (ch->pm_level > 0) ATA_OUTL(ch->r_mem, AHCI_P_SCTL, ATA_SC_DET_DISABLE); return (0); } return (1); } static int ahci_check_ids(struct ahci_channel *ch, union ccb *ccb) { if (ccb->ccb_h.target_id > ((ch->caps & AHCI_CAP_SPM) ? 15 : 0)) { ccb->ccb_h.status = CAM_TID_INVALID; ahci_done(ch, ccb); return (-1); } if (ccb->ccb_h.target_lun != 0) { ccb->ccb_h.status = CAM_LUN_INVALID; ahci_done(ch, ccb); return (-1); } return (0); } static void ahciaction(struct cam_sim *sim, union ccb *ccb) { struct ahci_channel *ch; CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("ahciaction func_code=%x\n", ccb->ccb_h.func_code)); ch = (struct ahci_channel *)cam_sim_softc(sim); switch (ccb->ccb_h.func_code) { /* Common cases first */ case XPT_ATA_IO: /* Execute the requested I/O operation */ case XPT_SCSI_IO: if (ahci_check_ids(ch, ccb)) return; if (ch->devices == 0 || (ch->pm_present == 0 && ccb->ccb_h.target_id > 0 && ccb->ccb_h.target_id < 15)) { ccb->ccb_h.status = CAM_SEL_TIMEOUT; break; } ccb->ccb_h.recovery_type = RECOVERY_NONE; /* Check for command collision. */ if (ahci_check_collision(ch, ccb)) { /* Freeze command. */ ch->frozen = ccb; /* We have only one frozen slot, so freeze simq also. */ xpt_freeze_simq(ch->sim, 1); return; } ahci_begin_transaction(ch, ccb); return; case XPT_EN_LUN: /* Enable LUN as a target */ case XPT_TARGET_IO: /* Execute target I/O request */ case XPT_ACCEPT_TARGET_IO: /* Accept Host Target Mode CDB */ case XPT_CONT_TARGET_IO: /* Continue Host Target I/O Connection*/ case XPT_ABORT: /* Abort the specified CCB */ /* XXX Implement */ ccb->ccb_h.status = CAM_REQ_INVALID; break; case XPT_SET_TRAN_SETTINGS: { struct ccb_trans_settings *cts = &ccb->cts; struct ahci_device *d; if (ahci_check_ids(ch, ccb)) return; if (cts->type == CTS_TYPE_CURRENT_SETTINGS) d = &ch->curr[ccb->ccb_h.target_id]; else d = &ch->user[ccb->ccb_h.target_id]; if (cts->xport_specific.sata.valid & CTS_SATA_VALID_REVISION) d->revision = cts->xport_specific.sata.revision; if (cts->xport_specific.sata.valid & CTS_SATA_VALID_MODE) d->mode = cts->xport_specific.sata.mode; if (cts->xport_specific.sata.valid & CTS_SATA_VALID_BYTECOUNT) d->bytecount = min(8192, cts->xport_specific.sata.bytecount); if (cts->xport_specific.sata.valid & CTS_SATA_VALID_TAGS) d->tags = min(ch->numslots, cts->xport_specific.sata.tags); if (cts->xport_specific.sata.valid & CTS_SATA_VALID_PM) ch->pm_present = cts->xport_specific.sata.pm_present; if (cts->xport_specific.sata.valid & CTS_SATA_VALID_ATAPI) d->atapi = cts->xport_specific.sata.atapi; if (cts->xport_specific.sata.valid & CTS_SATA_VALID_CAPS) d->caps = cts->xport_specific.sata.caps; ccb->ccb_h.status = CAM_REQ_CMP; break; } case XPT_GET_TRAN_SETTINGS: /* Get default/user set transfer settings for the target */ { struct ccb_trans_settings *cts = &ccb->cts; struct ahci_device *d; uint32_t status; if (ahci_check_ids(ch, ccb)) return; if (cts->type == CTS_TYPE_CURRENT_SETTINGS) d = &ch->curr[ccb->ccb_h.target_id]; else d = &ch->user[ccb->ccb_h.target_id]; cts->protocol = PROTO_UNSPECIFIED; cts->protocol_version = PROTO_VERSION_UNSPECIFIED; cts->transport = XPORT_SATA; cts->transport_version = XPORT_VERSION_UNSPECIFIED; cts->proto_specific.valid = 0; cts->xport_specific.sata.valid = 0; if (cts->type == CTS_TYPE_CURRENT_SETTINGS && (ccb->ccb_h.target_id == 15 || (ccb->ccb_h.target_id == 0 && !ch->pm_present))) { status = ATA_INL(ch->r_mem, AHCI_P_SSTS) & ATA_SS_SPD_MASK; if (status & 0x0f0) { cts->xport_specific.sata.revision = (status & 0x0f0) >> 4; cts->xport_specific.sata.valid |= CTS_SATA_VALID_REVISION; } cts->xport_specific.sata.caps = d->caps & CTS_SATA_CAPS_D; if (ch->pm_level) { if (ch->caps & (AHCI_CAP_PSC | AHCI_CAP_SSC)) cts->xport_specific.sata.caps |= CTS_SATA_CAPS_H_PMREQ; if (ch->caps2 & AHCI_CAP2_APST) cts->xport_specific.sata.caps |= CTS_SATA_CAPS_H_APST; } if ((ch->caps & AHCI_CAP_SNCQ) && (ch->quirks & AHCI_Q_NOAA) == 0) cts->xport_specific.sata.caps |= CTS_SATA_CAPS_H_DMAAA; cts->xport_specific.sata.caps |= CTS_SATA_CAPS_H_AN; cts->xport_specific.sata.caps &= ch->user[ccb->ccb_h.target_id].caps; cts->xport_specific.sata.valid |= CTS_SATA_VALID_CAPS; } else { cts->xport_specific.sata.revision = d->revision; cts->xport_specific.sata.valid |= CTS_SATA_VALID_REVISION; cts->xport_specific.sata.caps = d->caps; cts->xport_specific.sata.valid |= CTS_SATA_VALID_CAPS; } cts->xport_specific.sata.mode = d->mode; cts->xport_specific.sata.valid |= CTS_SATA_VALID_MODE; cts->xport_specific.sata.bytecount = d->bytecount; cts->xport_specific.sata.valid |= CTS_SATA_VALID_BYTECOUNT; cts->xport_specific.sata.pm_present = ch->pm_present; cts->xport_specific.sata.valid |= CTS_SATA_VALID_PM; cts->xport_specific.sata.tags = d->tags; cts->xport_specific.sata.valid |= CTS_SATA_VALID_TAGS; cts->xport_specific.sata.atapi = d->atapi; cts->xport_specific.sata.valid |= CTS_SATA_VALID_ATAPI; ccb->ccb_h.status = CAM_REQ_CMP; break; } case XPT_RESET_BUS: /* Reset the specified SCSI bus */ case XPT_RESET_DEV: /* Bus Device Reset the specified SCSI device */ ahci_reset(ch); ccb->ccb_h.status = CAM_REQ_CMP; break; case XPT_TERM_IO: /* Terminate the I/O process */ /* XXX Implement */ ccb->ccb_h.status = CAM_REQ_INVALID; break; case XPT_PATH_INQ: /* Path routing inquiry */ { struct ccb_pathinq *cpi = &ccb->cpi; cpi->version_num = 1; /* XXX??? */ cpi->hba_inquiry = PI_SDTR_ABLE; if (ch->caps & AHCI_CAP_SNCQ) cpi->hba_inquiry |= PI_TAG_ABLE; if (ch->caps & AHCI_CAP_SPM) cpi->hba_inquiry |= PI_SATAPM; cpi->target_sprt = 0; cpi->hba_misc = PIM_SEQSCAN | PIM_UNMAPPED; cpi->hba_eng_cnt = 0; if (ch->caps & AHCI_CAP_SPM) cpi->max_target = 15; else cpi->max_target = 0; cpi->max_lun = 0; cpi->initiator_id = 0; cpi->bus_id = cam_sim_bus(sim); cpi->base_transfer_speed = 150000; strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "AHCI", HBA_IDLEN); strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); cpi->unit_number = cam_sim_unit(sim); cpi->transport = XPORT_SATA; cpi->transport_version = XPORT_VERSION_UNSPECIFIED; cpi->protocol = PROTO_ATA; cpi->protocol_version = PROTO_VERSION_UNSPECIFIED; cpi->maxio = MAXPHYS; /* ATI SB600 can't handle 256 sectors with FPDMA (NCQ). */ if (ch->quirks & AHCI_Q_MAXIO_64K) cpi->maxio = min(cpi->maxio, 128 * 512); cpi->hba_vendor = ch->vendorid; cpi->hba_device = ch->deviceid; cpi->hba_subvendor = ch->subvendorid; cpi->hba_subdevice = ch->subdeviceid; cpi->ccb_h.status = CAM_REQ_CMP; break; } default: ccb->ccb_h.status = CAM_REQ_INVALID; break; } ahci_done(ch, ccb); } static void ahcipoll(struct cam_sim *sim) { struct ahci_channel *ch = (struct ahci_channel *)cam_sim_softc(sim); uint32_t istatus; /* Read interrupt statuses and process if any. */ istatus = ATA_INL(ch->r_mem, AHCI_P_IS); if (istatus != 0) ahci_ch_intr_main(ch, istatus); if (ch->resetting != 0 && (--ch->resetpolldiv <= 0 || !callout_pending(&ch->reset_timer))) { ch->resetpolldiv = 1000; ahci_reset_to(ch); } } MODULE_VERSION(ahci, 1); MODULE_DEPEND(ahci, cam, 1, 1, 1); Index: projects/clang350-import/sys/dev/ahci/ahci_pci.c =================================================================== --- projects/clang350-import/sys/dev/ahci/ahci_pci.c (revision 276347) +++ projects/clang350-import/sys/dev/ahci/ahci_pci.c (revision 276348) @@ -1,509 +1,509 @@ /*- * Copyright (c) 2009-2012 Alexander Motin * 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, * without modification, immediately at the beginning of the file. * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ahci.h" static int force_ahci = 1; TUNABLE_INT("hw.ahci.force", &force_ahci); -static struct { +static const struct { uint32_t id; uint8_t rev; const char *name; int quirks; } ahci_ids[] = { {0x43801002, 0x00, "AMD SB600", AHCI_Q_NOMSI | AHCI_Q_ATI_PMP_BUG | AHCI_Q_MAXIO_64K}, {0x43901002, 0x00, "AMD SB7x0/SB8x0/SB9x0", AHCI_Q_ATI_PMP_BUG}, {0x43911002, 0x00, "AMD SB7x0/SB8x0/SB9x0", AHCI_Q_ATI_PMP_BUG}, {0x43921002, 0x00, "AMD SB7x0/SB8x0/SB9x0", AHCI_Q_ATI_PMP_BUG}, {0x43931002, 0x00, "AMD SB7x0/SB8x0/SB9x0", AHCI_Q_ATI_PMP_BUG}, {0x43941002, 0x00, "AMD SB7x0/SB8x0/SB9x0", AHCI_Q_ATI_PMP_BUG}, /* Not sure SB8x0/SB9x0 needs this quirk. Be conservative though */ {0x43951002, 0x00, "AMD SB8x0/SB9x0", AHCI_Q_ATI_PMP_BUG}, {0x78001022, 0x00, "AMD Hudson-2", 0}, {0x78011022, 0x00, "AMD Hudson-2", 0}, {0x78021022, 0x00, "AMD Hudson-2", 0}, {0x78031022, 0x00, "AMD Hudson-2", 0}, {0x78041022, 0x00, "AMD Hudson-2", 0}, {0x06111b21, 0x00, "ASMedia ASM2106", 0}, {0x06121b21, 0x00, "ASMedia ASM1061", 0}, {0x26528086, 0x00, "Intel ICH6", AHCI_Q_NOFORCE}, {0x26538086, 0x00, "Intel ICH6M", AHCI_Q_NOFORCE}, {0x26818086, 0x00, "Intel ESB2", 0}, {0x26828086, 0x00, "Intel ESB2", 0}, {0x26838086, 0x00, "Intel ESB2", 0}, {0x27c18086, 0x00, "Intel ICH7", 0}, {0x27c38086, 0x00, "Intel ICH7", 0}, {0x27c58086, 0x00, "Intel ICH7M", 0}, {0x27c68086, 0x00, "Intel ICH7M", 0}, {0x28218086, 0x00, "Intel ICH8", 0}, {0x28228086, 0x00, "Intel ICH8", 0}, {0x28248086, 0x00, "Intel ICH8", 0}, {0x28298086, 0x00, "Intel ICH8M", 0}, {0x282a8086, 0x00, "Intel ICH8M", 0}, {0x29228086, 0x00, "Intel ICH9", 0}, {0x29238086, 0x00, "Intel ICH9", 0}, {0x29248086, 0x00, "Intel ICH9", 0}, {0x29258086, 0x00, "Intel ICH9", 0}, {0x29278086, 0x00, "Intel ICH9", 0}, {0x29298086, 0x00, "Intel ICH9M", 0}, {0x292a8086, 0x00, "Intel ICH9M", 0}, {0x292b8086, 0x00, "Intel ICH9M", 0}, {0x292c8086, 0x00, "Intel ICH9M", 0}, {0x292f8086, 0x00, "Intel ICH9M", 0}, {0x294d8086, 0x00, "Intel ICH9", 0}, {0x294e8086, 0x00, "Intel ICH9M", 0}, {0x3a058086, 0x00, "Intel ICH10", 0}, {0x3a228086, 0x00, "Intel ICH10", 0}, {0x3a258086, 0x00, "Intel ICH10", 0}, {0x3b228086, 0x00, "Intel 5 Series/3400 Series", 0}, {0x3b238086, 0x00, "Intel 5 Series/3400 Series", 0}, {0x3b258086, 0x00, "Intel 5 Series/3400 Series", 0}, {0x3b298086, 0x00, "Intel 5 Series/3400 Series", 0}, {0x3b2c8086, 0x00, "Intel 5 Series/3400 Series", 0}, {0x3b2f8086, 0x00, "Intel 5 Series/3400 Series", 0}, {0x1c028086, 0x00, "Intel Cougar Point", 0}, {0x1c038086, 0x00, "Intel Cougar Point", 0}, {0x1c048086, 0x00, "Intel Cougar Point", 0}, {0x1c058086, 0x00, "Intel Cougar Point", 0}, {0x1d028086, 0x00, "Intel Patsburg", 0}, {0x1d048086, 0x00, "Intel Patsburg", 0}, {0x1d068086, 0x00, "Intel Patsburg", 0}, {0x28268086, 0x00, "Intel Patsburg (RAID)", 0}, {0x1e028086, 0x00, "Intel Panther Point", 0}, {0x1e038086, 0x00, "Intel Panther Point", 0}, {0x1e048086, 0x00, "Intel Panther Point (RAID)", 0}, {0x1e058086, 0x00, "Intel Panther Point (RAID)", 0}, {0x1e068086, 0x00, "Intel Panther Point (RAID)", 0}, {0x1e078086, 0x00, "Intel Panther Point (RAID)", 0}, {0x1e0e8086, 0x00, "Intel Panther Point (RAID)", 0}, {0x1e0f8086, 0x00, "Intel Panther Point (RAID)", 0}, {0x1f228086, 0x00, "Intel Avoton", 0}, {0x1f238086, 0x00, "Intel Avoton", 0}, {0x1f248086, 0x00, "Intel Avoton (RAID)", 0}, {0x1f258086, 0x00, "Intel Avoton (RAID)", 0}, {0x1f268086, 0x00, "Intel Avoton (RAID)", 0}, {0x1f278086, 0x00, "Intel Avoton (RAID)", 0}, {0x1f2e8086, 0x00, "Intel Avoton (RAID)", 0}, {0x1f2f8086, 0x00, "Intel Avoton (RAID)", 0}, {0x1f328086, 0x00, "Intel Avoton", 0}, {0x1f338086, 0x00, "Intel Avoton", 0}, {0x1f348086, 0x00, "Intel Avoton (RAID)", 0}, {0x1f358086, 0x00, "Intel Avoton (RAID)", 0}, {0x1f368086, 0x00, "Intel Avoton (RAID)", 0}, {0x1f378086, 0x00, "Intel Avoton (RAID)", 0}, {0x1f3e8086, 0x00, "Intel Avoton (RAID)", 0}, {0x1f3f8086, 0x00, "Intel Avoton (RAID)", 0}, {0x23a38086, 0x00, "Intel Coleto Creek", 0}, {0x28238086, 0x00, "Intel Wellsburg (RAID)", 0}, {0x28278086, 0x00, "Intel Wellsburg (RAID)", 0}, {0x8c028086, 0x00, "Intel Lynx Point", 0}, {0x8c038086, 0x00, "Intel Lynx Point", 0}, {0x8c048086, 0x00, "Intel Lynx Point (RAID)", 0}, {0x8c058086, 0x00, "Intel Lynx Point (RAID)", 0}, {0x8c068086, 0x00, "Intel Lynx Point (RAID)", 0}, {0x8c078086, 0x00, "Intel Lynx Point (RAID)", 0}, {0x8c0e8086, 0x00, "Intel Lynx Point (RAID)", 0}, {0x8c0f8086, 0x00, "Intel Lynx Point (RAID)", 0}, {0x8c828086, 0x00, "Intel Wildcat Point", 0}, {0x8c838086, 0x00, "Intel Wildcat Point", 0}, {0x8c848086, 0x00, "Intel Wildcat Point (RAID)", 0}, {0x8c858086, 0x00, "Intel Wildcat Point (RAID)", 0}, {0x8c868086, 0x00, "Intel Wildcat Point (RAID)", 0}, {0x8c878086, 0x00, "Intel Wildcat Point (RAID)", 0}, {0x8c8e8086, 0x00, "Intel Wildcat Point (RAID)", 0}, {0x8c8f8086, 0x00, "Intel Wildcat Point (RAID)", 0}, {0x8d028086, 0x00, "Intel Wellsburg", 0}, {0x8d048086, 0x00, "Intel Wellsburg (RAID)", 0}, {0x8d068086, 0x00, "Intel Wellsburg (RAID)", 0}, {0x8d628086, 0x00, "Intel Wellsburg", 0}, {0x8d648086, 0x00, "Intel Wellsburg (RAID)", 0}, {0x8d668086, 0x00, "Intel Wellsburg (RAID)", 0}, {0x8d6e8086, 0x00, "Intel Wellsburg (RAID)", 0}, {0x9c028086, 0x00, "Intel Lynx Point-LP", 0}, {0x9c038086, 0x00, "Intel Lynx Point-LP", 0}, {0x9c048086, 0x00, "Intel Lynx Point-LP (RAID)", 0}, {0x9c058086, 0x00, "Intel Lynx Point-LP (RAID)", 0}, {0x9c068086, 0x00, "Intel Lynx Point-LP (RAID)", 0}, {0x9c078086, 0x00, "Intel Lynx Point-LP (RAID)", 0}, {0x9c0e8086, 0x00, "Intel Lynx Point-LP (RAID)", 0}, {0x9c0f8086, 0x00, "Intel Lynx Point-LP (RAID)", 0}, {0x23238086, 0x00, "Intel DH89xxCC", 0}, {0x2360197b, 0x00, "JMicron JMB360", 0}, {0x2361197b, 0x00, "JMicron JMB361", AHCI_Q_NOFORCE}, {0x2362197b, 0x00, "JMicron JMB362", 0}, {0x2363197b, 0x00, "JMicron JMB363", AHCI_Q_NOFORCE}, {0x2365197b, 0x00, "JMicron JMB365", AHCI_Q_NOFORCE}, {0x2366197b, 0x00, "JMicron JMB366", AHCI_Q_NOFORCE}, {0x2368197b, 0x00, "JMicron JMB368", AHCI_Q_NOFORCE}, {0x611111ab, 0x00, "Marvell 88SE6111", AHCI_Q_NOFORCE | AHCI_Q_1CH | AHCI_Q_EDGEIS}, {0x612111ab, 0x00, "Marvell 88SE6121", AHCI_Q_NOFORCE | AHCI_Q_2CH | AHCI_Q_EDGEIS | AHCI_Q_NONCQ | AHCI_Q_NOCOUNT}, {0x614111ab, 0x00, "Marvell 88SE6141", AHCI_Q_NOFORCE | AHCI_Q_4CH | AHCI_Q_EDGEIS | AHCI_Q_NONCQ | AHCI_Q_NOCOUNT}, {0x614511ab, 0x00, "Marvell 88SE6145", AHCI_Q_NOFORCE | AHCI_Q_4CH | AHCI_Q_EDGEIS | AHCI_Q_NONCQ | AHCI_Q_NOCOUNT}, {0x91201b4b, 0x00, "Marvell 88SE912x", AHCI_Q_EDGEIS}, {0x91231b4b, 0x11, "Marvell 88SE912x", AHCI_Q_ALTSIG}, {0x91231b4b, 0x00, "Marvell 88SE912x", AHCI_Q_EDGEIS|AHCI_Q_SATA2}, {0x91251b4b, 0x00, "Marvell 88SE9125", 0}, {0x91281b4b, 0x00, "Marvell 88SE9128", AHCI_Q_ALTSIG}, {0x91301b4b, 0x00, "Marvell 88SE9130", AHCI_Q_ALTSIG}, {0x91721b4b, 0x00, "Marvell 88SE9172", 0}, {0x91821b4b, 0x00, "Marvell 88SE9182", 0}, {0x91831b4b, 0x00, "Marvell 88SS9183", 0}, {0x91a01b4b, 0x00, "Marvell 88SE91Ax", 0}, {0x92151b4b, 0x00, "Marvell 88SE9215", 0}, {0x92201b4b, 0x00, "Marvell 88SE9220", AHCI_Q_ALTSIG}, {0x92301b4b, 0x00, "Marvell 88SE9230", AHCI_Q_ALTSIG}, {0x92351b4b, 0x00, "Marvell 88SE9235", 0}, {0x06201103, 0x00, "HighPoint RocketRAID 620", 0}, {0x06201b4b, 0x00, "HighPoint RocketRAID 620", 0}, {0x06221103, 0x00, "HighPoint RocketRAID 622", 0}, {0x06221b4b, 0x00, "HighPoint RocketRAID 622", 0}, {0x06401103, 0x00, "HighPoint RocketRAID 640", 0}, {0x06401b4b, 0x00, "HighPoint RocketRAID 640", 0}, {0x06441103, 0x00, "HighPoint RocketRAID 644", 0}, {0x06441b4b, 0x00, "HighPoint RocketRAID 644", 0}, {0x06411103, 0x00, "HighPoint RocketRAID 640L", 0}, {0x06421103, 0x00, "HighPoint RocketRAID 642L", 0}, {0x06451103, 0x00, "HighPoint RocketRAID 644L", 0}, {0x044c10de, 0x00, "NVIDIA MCP65", AHCI_Q_NOAA}, {0x044d10de, 0x00, "NVIDIA MCP65", AHCI_Q_NOAA}, {0x044e10de, 0x00, "NVIDIA MCP65", AHCI_Q_NOAA}, {0x044f10de, 0x00, "NVIDIA MCP65", AHCI_Q_NOAA}, {0x045c10de, 0x00, "NVIDIA MCP65", AHCI_Q_NOAA}, {0x045d10de, 0x00, "NVIDIA MCP65", AHCI_Q_NOAA}, {0x045e10de, 0x00, "NVIDIA MCP65", AHCI_Q_NOAA}, {0x045f10de, 0x00, "NVIDIA MCP65", AHCI_Q_NOAA}, {0x055010de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x055110de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x055210de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x055310de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x055410de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x055510de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x055610de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x055710de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x055810de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x055910de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x055A10de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x055B10de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x058410de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x07f010de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x07f110de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x07f210de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x07f310de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x07f410de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x07f510de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x07f610de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x07f710de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x07f810de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x07f910de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x07fa10de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x07fb10de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x0ad010de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0ad110de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0ad210de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0ad310de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0ad410de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0ad510de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0ad610de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0ad710de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0ad810de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0ad910de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0ada10de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0adb10de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0ab410de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0ab510de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0ab610de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0ab710de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0ab810de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0ab910de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0aba10de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0abb10de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0abc10de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0abd10de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0abe10de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0abf10de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0d8410de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x0d8510de, 0x00, "NVIDIA MCP89", AHCI_Q_NOFORCE|AHCI_Q_NOAA}, {0x0d8610de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x0d8710de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x0d8810de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x0d8910de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x0d8a10de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x0d8b10de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x0d8c10de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x0d8d10de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x0d8e10de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x0d8f10de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x3781105a, 0x00, "Promise TX8660", 0}, {0x33491106, 0x00, "VIA VT8251", AHCI_Q_NOPMP|AHCI_Q_NONCQ}, {0x62871106, 0x00, "VIA VT8251", AHCI_Q_NOPMP|AHCI_Q_NONCQ}, {0x11841039, 0x00, "SiS 966", 0}, {0x11851039, 0x00, "SiS 968", 0}, {0x01861039, 0x00, "SiS 968", 0}, {0x00000000, 0x00, NULL, 0} }; static int ahci_pci_ctlr_reset(device_t dev) { if (pci_read_config(dev, PCIR_DEVVENDOR, 4) == 0x28298086 && (pci_read_config(dev, 0x92, 1) & 0xfe) == 0x04) pci_write_config(dev, 0x92, 0x01, 1); return ahci_ctlr_reset(dev); } static int ahci_probe(device_t dev) { char buf[64]; int i, valid = 0; uint32_t devid = pci_get_devid(dev); uint8_t revid = pci_get_revid(dev); /* * Ensure it is not a PCI bridge (some vendors use * the same PID and VID in PCI bridge and AHCI cards). */ if (pci_get_class(dev) == PCIC_BRIDGE) return (ENXIO); /* Is this a possible AHCI candidate? */ if (pci_get_class(dev) == PCIC_STORAGE && pci_get_subclass(dev) == PCIS_STORAGE_SATA && pci_get_progif(dev) == PCIP_STORAGE_SATA_AHCI_1_0) valid = 1; /* Is this a known AHCI chip? */ for (i = 0; ahci_ids[i].id != 0; i++) { if (ahci_ids[i].id == devid && ahci_ids[i].rev <= revid && (valid || (force_ahci == 1 && !(ahci_ids[i].quirks & AHCI_Q_NOFORCE)))) { /* Do not attach JMicrons with single PCI function. */ if (pci_get_vendor(dev) == 0x197b && (pci_read_config(dev, 0xdf, 1) & 0x40) == 0) return (ENXIO); snprintf(buf, sizeof(buf), "%s AHCI SATA controller", ahci_ids[i].name); device_set_desc_copy(dev, buf); return (BUS_PROBE_VENDOR); } } if (!valid) return (ENXIO); device_set_desc_copy(dev, "AHCI SATA controller"); return (BUS_PROBE_VENDOR); } static int ahci_ata_probe(device_t dev) { char buf[64]; int i; uint32_t devid = pci_get_devid(dev); uint8_t revid = pci_get_revid(dev); if ((intptr_t)device_get_ivars(dev) >= 0) return (ENXIO); /* Is this a known AHCI chip? */ for (i = 0; ahci_ids[i].id != 0; i++) { if (ahci_ids[i].id == devid && ahci_ids[i].rev <= revid) { snprintf(buf, sizeof(buf), "%s AHCI SATA controller", ahci_ids[i].name); device_set_desc_copy(dev, buf); return (BUS_PROBE_VENDOR); } } device_set_desc_copy(dev, "AHCI SATA controller"); return (BUS_PROBE_VENDOR); } static int ahci_pci_attach(device_t dev) { struct ahci_controller *ctlr = device_get_softc(dev); int error, i; uint32_t devid = pci_get_devid(dev); uint8_t revid = pci_get_revid(dev); i = 0; while (ahci_ids[i].id != 0 && (ahci_ids[i].id != devid || ahci_ids[i].rev > revid)) i++; ctlr->quirks = ahci_ids[i].quirks; /* Limit speed for my onboard JMicron external port. * It is not eSATA really, limit to SATA 1 */ if (pci_get_devid(dev) == 0x2363197b && pci_get_subvendor(dev) == 0x1043 && pci_get_subdevice(dev) == 0x81e4) ctlr->quirks |= AHCI_Q_SATA1_UNIT0; /* if we have a memory BAR(5) we are likely on an AHCI part */ ctlr->vendorid = pci_get_vendor(dev); ctlr->deviceid = pci_get_device(dev); ctlr->subvendorid = pci_get_subvendor(dev); ctlr->subdeviceid = pci_get_subdevice(dev); ctlr->r_rid = PCIR_BAR(5); if (!(ctlr->r_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &ctlr->r_rid, RF_ACTIVE))) return ENXIO; pci_enable_busmaster(dev); /* Reset controller */ if ((error = ahci_pci_ctlr_reset(dev)) != 0) { bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem); return (error); }; /* Setup interrupts. */ /* Setup MSI register parameters */ ctlr->msi = 2; /* Process hints. */ if (ctlr->quirks & AHCI_Q_NOMSI) ctlr->msi = 0; resource_int_value(device_get_name(dev), device_get_unit(dev), "msi", &ctlr->msi); ctlr->numirqs = 1; if (ctlr->msi < 0) ctlr->msi = 0; else if (ctlr->msi == 1) ctlr->msi = min(1, pci_msi_count(dev)); else if (ctlr->msi > 1) { ctlr->msi = 2; ctlr->numirqs = pci_msi_count(dev); } /* Allocate MSI if needed/present. */ if (ctlr->msi && pci_alloc_msi(dev, &ctlr->numirqs) != 0) { ctlr->msi = 0; ctlr->numirqs = 1; } error = ahci_attach(dev); if (error != 0) if (ctlr->msi) pci_release_msi(dev); return error; } static int ahci_pci_detach(device_t dev) { ahci_detach(dev); pci_release_msi(dev); return (0); } static int ahci_pci_suspend(device_t dev) { struct ahci_controller *ctlr = device_get_softc(dev); bus_generic_suspend(dev); /* Disable interupts, so the state change(s) doesn't trigger */ ATA_OUTL(ctlr->r_mem, AHCI_GHC, ATA_INL(ctlr->r_mem, AHCI_GHC) & (~AHCI_GHC_IE)); return 0; } static int ahci_pci_resume(device_t dev) { int res; if ((res = ahci_pci_ctlr_reset(dev)) != 0) return (res); ahci_ctlr_setup(dev); return (bus_generic_resume(dev)); } devclass_t ahci_devclass; static device_method_t ahci_methods[] = { DEVMETHOD(device_probe, ahci_probe), DEVMETHOD(device_attach, ahci_pci_attach), DEVMETHOD(device_detach, ahci_pci_detach), DEVMETHOD(device_suspend, ahci_pci_suspend), DEVMETHOD(device_resume, ahci_pci_resume), DEVMETHOD(bus_print_child, ahci_print_child), DEVMETHOD(bus_alloc_resource, ahci_alloc_resource), DEVMETHOD(bus_release_resource, ahci_release_resource), DEVMETHOD(bus_setup_intr, ahci_setup_intr), DEVMETHOD(bus_teardown_intr,ahci_teardown_intr), DEVMETHOD(bus_child_location_str, ahci_child_location_str), DEVMETHOD(bus_get_dma_tag, ahci_get_dma_tag), - { 0, 0 } + DEVMETHOD_END }; static driver_t ahci_driver = { "ahci", ahci_methods, sizeof(struct ahci_controller) }; -DRIVER_MODULE(ahci, pci, ahci_driver, ahci_devclass, 0, 0); +DRIVER_MODULE(ahci, pci, ahci_driver, ahci_devclass, NULL, NULL); static device_method_t ahci_ata_methods[] = { DEVMETHOD(device_probe, ahci_ata_probe), DEVMETHOD(device_attach, ahci_pci_attach), DEVMETHOD(device_detach, ahci_pci_detach), DEVMETHOD(device_suspend, ahci_pci_suspend), DEVMETHOD(device_resume, ahci_pci_resume), DEVMETHOD(bus_print_child, ahci_print_child), DEVMETHOD(bus_alloc_resource, ahci_alloc_resource), DEVMETHOD(bus_release_resource, ahci_release_resource), DEVMETHOD(bus_setup_intr, ahci_setup_intr), DEVMETHOD(bus_teardown_intr,ahci_teardown_intr), DEVMETHOD(bus_child_location_str, ahci_child_location_str), - { 0, 0 } + DEVMETHOD_END }; static driver_t ahci_ata_driver = { "ahci", ahci_ata_methods, sizeof(struct ahci_controller) }; -DRIVER_MODULE(ahci, atapci, ahci_ata_driver, ahci_devclass, 0, 0); +DRIVER_MODULE(ahci, atapci, ahci_ata_driver, ahci_devclass, NULL, NULL); Index: projects/clang350-import/sys/dev/ahci/ahciem.c =================================================================== --- projects/clang350-import/sys/dev/ahci/ahciem.c (revision 276347) +++ projects/clang350-import/sys/dev/ahci/ahciem.c (revision 276348) @@ -1,608 +1,608 @@ /*- * Copyright (c) 2012 Alexander Motin * 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, * without modification, immediately at the beginning of the file. * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ahci.h" #include #include #include #include #include #include /* local prototypes */ static void ahciemaction(struct cam_sim *sim, union ccb *ccb); static void ahciempoll(struct cam_sim *sim); static int ahci_em_reset(device_t dev); static void ahci_em_led(void *priv, int onoff); static void ahci_em_setleds(device_t dev, int c); static int ahci_em_probe(device_t dev) { device_set_desc_copy(dev, "AHCI enclosure management bridge"); return (0); } static int ahci_em_attach(device_t dev) { device_t parent = device_get_parent(dev); struct ahci_controller *ctlr = device_get_softc(parent); struct ahci_enclosure *enc = device_get_softc(dev); struct cam_devq *devq; int i, c, rid, error; char buf[32]; enc->dev = dev; enc->quirks = ctlr->quirks; enc->channels = ctlr->channels; enc->ichannels = ctlr->ichannels; mtx_init(&enc->mtx, "AHCI enclosure lock", NULL, MTX_DEF); rid = 0; if (!(enc->r_memc = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE))) { mtx_destroy(&enc->mtx); return (ENXIO); } enc->capsem = ATA_INL(enc->r_memc, 0); rid = 1; if (!(enc->r_memt = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE))) { error = ENXIO; goto err0; } if ((enc->capsem & (AHCI_EM_XMT | AHCI_EM_SMB)) == 0) { rid = 2; if (!(enc->r_memr = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE))) { error = ENXIO; goto err0; } } else enc->r_memr = NULL; mtx_lock(&enc->mtx); if (ahci_em_reset(dev) != 0) { error = ENXIO; goto err1; } rid = ATA_IRQ_RID; /* Create the device queue for our SIM. */ devq = cam_simq_alloc(1); if (devq == NULL) { device_printf(dev, "Unable to allocate SIM queue\n"); error = ENOMEM; goto err1; } /* Construct SIM entry */ enc->sim = cam_sim_alloc(ahciemaction, ahciempoll, "ahciem", enc, device_get_unit(dev), &enc->mtx, 1, 0, devq); if (enc->sim == NULL) { cam_simq_free(devq); device_printf(dev, "Unable to allocate SIM\n"); error = ENOMEM; goto err1; } if (xpt_bus_register(enc->sim, dev, 0) != CAM_SUCCESS) { device_printf(dev, "unable to register xpt bus\n"); error = ENXIO; goto err2; } if (xpt_create_path(&enc->path, /*periph*/NULL, cam_sim_path(enc->sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { device_printf(dev, "Unable to create path\n"); error = ENXIO; goto err3; } mtx_unlock(&enc->mtx); if (bootverbose) { device_printf(dev, "Caps:%s%s%s%s%s%s%s%s\n", (enc->capsem & AHCI_EM_PM) ? " PM":"", (enc->capsem & AHCI_EM_ALHD) ? " ALHD":"", (enc->capsem & AHCI_EM_XMT) ? " XMT":"", (enc->capsem & AHCI_EM_SMB) ? " SMB":"", (enc->capsem & AHCI_EM_SGPIO) ? " SGPIO":"", (enc->capsem & AHCI_EM_SES2) ? " SES-2":"", (enc->capsem & AHCI_EM_SAFTE) ? " SAF-TE":"", (enc->capsem & AHCI_EM_LED) ? " LED":""); } if ((enc->capsem & AHCI_EM_LED)) { for (c = 0; c < enc->channels; c++) { if ((enc->ichannels & (1 << c)) == 0) continue; for (i = 0; i < AHCI_NUM_LEDS; i++) { enc->leds[c * AHCI_NUM_LEDS + i].dev = dev; enc->leds[c * AHCI_NUM_LEDS + i].num = c * AHCI_NUM_LEDS + i; } if ((enc->capsem & AHCI_EM_ALHD) == 0) { snprintf(buf, sizeof(buf), "%s.%d.act", device_get_nameunit(parent), c); enc->leds[c * AHCI_NUM_LEDS + 0].led = led_create(ahci_em_led, &enc->leds[c * AHCI_NUM_LEDS + 0], buf); } snprintf(buf, sizeof(buf), "%s.%d.locate", device_get_nameunit(parent), c); enc->leds[c * AHCI_NUM_LEDS + 1].led = led_create(ahci_em_led, &enc->leds[c * AHCI_NUM_LEDS + 1], buf); snprintf(buf, sizeof(buf), "%s.%d.fault", device_get_nameunit(parent), c); enc->leds[c * AHCI_NUM_LEDS + 2].led = led_create(ahci_em_led, &enc->leds[c * AHCI_NUM_LEDS + 2], buf); } } return (0); err3: xpt_bus_deregister(cam_sim_path(enc->sim)); err2: cam_sim_free(enc->sim, /*free_devq*/TRUE); err1: mtx_unlock(&enc->mtx); if (enc->r_memr) bus_release_resource(dev, SYS_RES_MEMORY, 2, enc->r_memr); err0: if (enc->r_memt) bus_release_resource(dev, SYS_RES_MEMORY, 1, enc->r_memt); bus_release_resource(dev, SYS_RES_MEMORY, 0, enc->r_memc); mtx_destroy(&enc->mtx); return (error); } static int ahci_em_detach(device_t dev) { struct ahci_enclosure *enc = device_get_softc(dev); int i; for (i = 0; i < enc->channels * AHCI_NUM_LEDS; i++) { if (enc->leds[i].led) led_destroy(enc->leds[i].led); } mtx_lock(&enc->mtx); xpt_async(AC_LOST_DEVICE, enc->path, NULL); xpt_free_path(enc->path); xpt_bus_deregister(cam_sim_path(enc->sim)); cam_sim_free(enc->sim, /*free_devq*/TRUE); mtx_unlock(&enc->mtx); bus_release_resource(dev, SYS_RES_MEMORY, 0, enc->r_memc); bus_release_resource(dev, SYS_RES_MEMORY, 1, enc->r_memt); if (enc->r_memr) bus_release_resource(dev, SYS_RES_MEMORY, 2, enc->r_memr); mtx_destroy(&enc->mtx); return (0); } static int ahci_em_reset(device_t dev) { struct ahci_enclosure *enc; int i, timeout; enc = device_get_softc(dev); ATA_OUTL(enc->r_memc, 0, AHCI_EM_RST); timeout = 1000; while ((ATA_INL(enc->r_memc, 0) & AHCI_EM_RST) && --timeout > 0) DELAY(1000); if (timeout == 0) { device_printf(dev, "EM timeout\n"); return (1); } for (i = 0; i < enc->channels; i++) ahci_em_setleds(dev, i); return (0); } static int ahci_em_suspend(device_t dev) { struct ahci_enclosure *enc = device_get_softc(dev); mtx_lock(&enc->mtx); xpt_freeze_simq(enc->sim, 1); mtx_unlock(&enc->mtx); return (0); } static int ahci_em_resume(device_t dev) { struct ahci_enclosure *enc = device_get_softc(dev); mtx_lock(&enc->mtx); ahci_em_reset(dev); xpt_release_simq(enc->sim, TRUE); mtx_unlock(&enc->mtx); return (0); } devclass_t ahciem_devclass; static device_method_t ahciem_methods[] = { DEVMETHOD(device_probe, ahci_em_probe), DEVMETHOD(device_attach, ahci_em_attach), DEVMETHOD(device_detach, ahci_em_detach), DEVMETHOD(device_suspend, ahci_em_suspend), DEVMETHOD(device_resume, ahci_em_resume), - { 0, 0 } + DEVMETHOD_END }; static driver_t ahciem_driver = { "ahciem", ahciem_methods, sizeof(struct ahci_enclosure) }; -DRIVER_MODULE(ahciem, ahci, ahciem_driver, ahciem_devclass, 0, 0); +DRIVER_MODULE(ahciem, ahci, ahciem_driver, ahciem_devclass, NULL, NULL); static void ahci_em_setleds(device_t dev, int c) { struct ahci_enclosure *enc; int timeout; int16_t val; enc = device_get_softc(dev); val = 0; if (enc->status[c][2] & 0x80) /* Activity */ val |= (1 << 0); if (enc->status[c][2] & SESCTL_RQSID) /* Identification */ val |= (1 << 3); else if (enc->status[c][3] & SESCTL_RQSFLT) /* Fault */ val |= (1 << 6); else if (enc->status[c][1] & 0x02) /* Rebuild */ val |= (1 << 6) | (1 << 3); timeout = 10000; while (ATA_INL(enc->r_memc, 0) & (AHCI_EM_TM | AHCI_EM_RST) && --timeout > 0) DELAY(100); if (timeout == 0) device_printf(dev, "Transmit timeout\n"); ATA_OUTL(enc->r_memt, 0, (1 << 8) | (0 << 16) | (0 << 24)); ATA_OUTL(enc->r_memt, 4, c | (0 << 8) | (val << 16)); ATA_OUTL(enc->r_memc, 0, AHCI_EM_TM); } static void ahci_em_led(void *priv, int onoff) { struct ahci_led *led; struct ahci_enclosure *enc; int c, l; led = (struct ahci_led *)priv; enc = device_get_softc(led->dev); c = led->num / AHCI_NUM_LEDS; l = led->num % AHCI_NUM_LEDS; if (l == 0) { if (onoff) enc->status[c][2] |= 0x80; else enc->status[c][2] &= ~0x80; } else if (l == 1) { if (onoff) enc->status[c][2] |= SESCTL_RQSID; else enc->status[c][2] &= ~SESCTL_RQSID; } else if (l == 2) { if (onoff) enc->status[c][3] |= SESCTL_RQSFLT; else enc->status[c][3] &= SESCTL_RQSFLT; } ahci_em_setleds(led->dev, c); } static int ahci_check_ids(union ccb *ccb) { if (ccb->ccb_h.target_id != 0) { ccb->ccb_h.status = CAM_TID_INVALID; xpt_done(ccb); return (-1); } if (ccb->ccb_h.target_lun != 0) { ccb->ccb_h.status = CAM_LUN_INVALID; xpt_done(ccb); return (-1); } return (0); } static void ahci_em_emulate_ses_on_led(device_t dev, union ccb *ccb) { struct ahci_enclosure *enc; struct ses_status_page *page; struct ses_status_array_dev_slot *ads, *ads0; struct ses_elm_desc_hdr *elmd; uint8_t *buf; int i; enc = device_get_softc(dev); buf = ccb->ataio.data_ptr; /* General request validation. */ if (ccb->ataio.cmd.command != ATA_SEP_ATTN || ccb->ataio.dxfer_len < ccb->ataio.cmd.sector_count * 4) { ccb->ccb_h.status = CAM_REQ_INVALID; goto out; } /* SEMB IDENTIFY */ if (ccb->ataio.cmd.features == 0xEC && ccb->ataio.cmd.sector_count >= 16) { bzero(buf, ccb->ataio.dxfer_len); buf[0] = 64; /* Valid bytes. */ buf[2] = 0x30; /* NAA Locally Assigned. */ strncpy(&buf[3], device_get_nameunit(dev), 7); strncpy(&buf[10], "AHCI ", SID_VENDOR_SIZE); strncpy(&buf[18], "SGPIO Enclosure ", SID_PRODUCT_SIZE); strncpy(&buf[34], "1.00", SID_REVISION_SIZE); strncpy(&buf[39], "0001", 4); strncpy(&buf[43], "S-E-S ", 6); strncpy(&buf[49], "2.00", 4); ccb->ccb_h.status = CAM_REQ_CMP; goto out; } /* SEMB RECEIVE DIAGNOSTIC RESULT (0) */ page = (struct ses_status_page *)buf; if (ccb->ataio.cmd.lba_low == 0x02 && ccb->ataio.cmd.features == 0x00 && ccb->ataio.cmd.sector_count >= 2) { bzero(buf, ccb->ataio.dxfer_len); page->hdr.page_code = 0; scsi_ulto2b(4, page->hdr.length); buf[4] = 0; buf[5] = 1; buf[6] = 2; buf[7] = 7; ccb->ccb_h.status = CAM_REQ_CMP; goto out; } /* SEMB RECEIVE DIAGNOSTIC RESULT (1) */ if (ccb->ataio.cmd.lba_low == 0x02 && ccb->ataio.cmd.features == 0x01 && ccb->ataio.cmd.sector_count >= 13) { struct ses_enc_desc *ed; struct ses_elm_type_desc *td; bzero(buf, ccb->ataio.dxfer_len); page->hdr.page_code = 0x01; scsi_ulto2b(4 + 4 + 36 + 4, page->hdr.length); ed = (struct ses_enc_desc *)&buf[8]; ed->byte0 = 0x11; ed->subenc_id = 0; ed->num_types = 1; ed->length = 36; strncpy(ed->vendor_id, "AHCI ", SID_VENDOR_SIZE); strncpy(ed->product_id, "SGPIO Enclosure ", SID_PRODUCT_SIZE); strncpy(ed->product_rev, " ", SID_REVISION_SIZE); td = (struct ses_elm_type_desc *)ses_enc_desc_next(ed); td->etype_elm_type = 0x17; td->etype_maxelt = enc->channels; td->etype_subenc = 0; td->etype_txt_len = 0; ccb->ccb_h.status = CAM_REQ_CMP; goto out; } /* SEMB RECEIVE DIAGNOSTIC RESULT (2) */ if (ccb->ataio.cmd.lba_low == 0x02 && ccb->ataio.cmd.features == 0x02 && ccb->ataio.cmd.sector_count >= (3 + enc->channels)) { bzero(buf, ccb->ataio.dxfer_len); page->hdr.page_code = 0x02; scsi_ulto2b(4 + 4 * (1 + enc->channels), page->hdr.length); for (i = 0; i < enc->channels; i++) { ads = &page->elements[i + 1].array_dev_slot; memcpy(ads, enc->status[i], 4); ads->common.bytes[0] |= (enc->ichannels & (1 << i)) ? SES_OBJSTAT_UNKNOWN : SES_OBJSTAT_NOTINSTALLED; } ccb->ccb_h.status = CAM_REQ_CMP; goto out; } /* SEMB SEND DIAGNOSTIC (2) */ if (ccb->ataio.cmd.lba_low == 0x82 && ccb->ataio.cmd.features == 0x02 && ccb->ataio.cmd.sector_count >= (3 + enc->channels)) { ads0 = &page->elements[0].array_dev_slot; for (i = 0; i < enc->channels; i++) { ads = &page->elements[i + 1].array_dev_slot; if (ads->common.bytes[0] & SESCTL_CSEL) { enc->status[i][0] = 0; enc->status[i][1] = ads->bytes[0] & 0x02; enc->status[i][2] = ads->bytes[1] & (0x80 | SESCTL_RQSID); enc->status[i][3] = ads->bytes[2] & SESCTL_RQSFLT; ahci_em_setleds(dev, i); } else if (ads0->common.bytes[0] & SESCTL_CSEL) { enc->status[i][0] = 0; enc->status[i][1] = ads0->bytes[0] & 0x02; enc->status[i][2] = ads0->bytes[1] & (0x80 | SESCTL_RQSID); enc->status[i][3] = ads0->bytes[2] & SESCTL_RQSFLT; ahci_em_setleds(dev, i); } } ccb->ccb_h.status = CAM_REQ_CMP; goto out; } /* SEMB RECEIVE DIAGNOSTIC RESULT (7) */ if (ccb->ataio.cmd.lba_low == 0x02 && ccb->ataio.cmd.features == 0x07 && ccb->ataio.cmd.sector_count >= (3 + 3 * enc->channels)) { bzero(buf, ccb->ataio.dxfer_len); page->hdr.page_code = 0x07; scsi_ulto2b(4 + 4 + 12 * enc->channels, page->hdr.length); for (i = 0; i < enc->channels; i++) { elmd = (struct ses_elm_desc_hdr *)&buf[8 + 4 + 12 * i]; scsi_ulto2b(8, elmd->length); snprintf((char *)(elmd + 1), 9, "SLOT %03d", i); } ccb->ccb_h.status = CAM_REQ_CMP; goto out; } ccb->ccb_h.status = CAM_REQ_INVALID; out: xpt_done(ccb); } static void ahci_em_begin_transaction(device_t dev, union ccb *ccb) { struct ahci_enclosure *enc; struct ata_res *res; enc = device_get_softc(dev); res = &ccb->ataio.res; bzero(res, sizeof(*res)); if ((ccb->ataio.cmd.flags & CAM_ATAIO_CONTROL) && (ccb->ataio.cmd.control & ATA_A_RESET)) { res->lba_high = 0xc3; res->lba_mid = 0x3c; ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); return; } if (enc->capsem & AHCI_EM_LED) { ahci_em_emulate_ses_on_led(dev, ccb); return; } else device_printf(dev, "Unsupported enclosure interface\n"); ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); } static void ahciemaction(struct cam_sim *sim, union ccb *ccb) { device_t dev, parent; struct ahci_enclosure *enc; CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("ahciemaction func_code=%x\n", ccb->ccb_h.func_code)); enc = cam_sim_softc(sim); dev = enc->dev; switch (ccb->ccb_h.func_code) { case XPT_ATA_IO: /* Execute the requested I/O operation */ if (ahci_check_ids(ccb)) return; ahci_em_begin_transaction(dev, ccb); return; case XPT_RESET_BUS: /* Reset the specified bus */ case XPT_RESET_DEV: /* Bus Device Reset the specified device */ ahci_em_reset(dev); ccb->ccb_h.status = CAM_REQ_CMP; break; case XPT_PATH_INQ: /* Path routing inquiry */ { struct ccb_pathinq *cpi = &ccb->cpi; parent = device_get_parent(dev); cpi->version_num = 1; /* XXX??? */ cpi->hba_inquiry = PI_SDTR_ABLE; cpi->target_sprt = 0; cpi->hba_misc = PIM_SEQSCAN; cpi->hba_eng_cnt = 0; cpi->max_target = 0; cpi->max_lun = 0; cpi->initiator_id = 0; cpi->bus_id = cam_sim_bus(sim); cpi->base_transfer_speed = 150000; strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "AHCI", HBA_IDLEN); strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); cpi->unit_number = cam_sim_unit(sim); cpi->transport = XPORT_SATA; cpi->transport_version = XPORT_VERSION_UNSPECIFIED; cpi->protocol = PROTO_ATA; cpi->protocol_version = PROTO_VERSION_UNSPECIFIED; cpi->maxio = MAXPHYS; cpi->hba_vendor = pci_get_vendor(parent); cpi->hba_device = pci_get_device(parent); cpi->hba_subvendor = pci_get_subvendor(parent); cpi->hba_subdevice = pci_get_subdevice(parent); cpi->ccb_h.status = CAM_REQ_CMP; break; } default: ccb->ccb_h.status = CAM_REQ_INVALID; break; } xpt_done(ccb); } static void ahciempoll(struct cam_sim *sim) { } Index: projects/clang350-import/sys =================================================================== --- projects/clang350-import/sys (revision 276347) +++ projects/clang350-import/sys (revision 276348) Property changes on: projects/clang350-import/sys ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/sys:r276343-276346 Index: projects/clang350-import =================================================================== --- projects/clang350-import (revision 276347) +++ projects/clang350-import (revision 276348) Property changes on: projects/clang350-import ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r276343-276346