int ocxl_config_check_afu_index(struct pci_dev *dev, struct ocxl_fn_config *fn, int afu_idx) { u32 val; int rc, templ_major, templ_minor, len; pci_write_config_byte(dev, fn->dvsec_afu_info_pos + OCXL_DVSEC_AFU_INFO_AFU_IDX, afu_idx); rc = read_afu_info(dev, fn, OCXL_DVSEC_TEMPL_VERSION, &val); if (rc) return rc; /* AFU index map can have holes */ if (!val) return 0; templ_major = EXTRACT_BITS(val, 8, 15); templ_minor = EXTRACT_BITS(val, 0, 7); dev_dbg(&dev->dev, "AFU descriptor template version %d.%d\n", templ_major, templ_minor); len = EXTRACT_BITS(val, 16, 31); if (len != OCXL_TEMPL_LEN) { dev_warn(&dev->dev, "Unexpected template length in AFU information (%#x)\n", len); } return 1; }
static int read_dvsec_function(struct pci_dev *dev, struct ocxl_fn_config *fn) { int pos, afu_present; u32 val; pos = find_dvsec(dev, OCXL_DVSEC_FUNC_ID); if (!pos) { dev_err(&dev->dev, "Can't find function DVSEC\n"); return -ENODEV; } fn->dvsec_function_pos = pos; pci_read_config_dword(dev, pos + OCXL_DVSEC_FUNC_OFF_INDEX, &val); afu_present = EXTRACT_BIT(val, 31); if (!afu_present) { fn->max_afu_index = -1; dev_dbg(&dev->dev, "Function doesn't define any AFU\n"); goto out; } fn->max_afu_index = EXTRACT_BITS(val, 24, 29); out: dev_dbg(&dev->dev, "Function DVSEC:\n"); dev_dbg(&dev->dev, " Max AFU index = %d\n", fn->max_afu_index); return 0; }
static int read_afu_mmio(struct pci_dev *dev, struct ocxl_fn_config *fn, struct ocxl_afu_config *afu) { int rc; u32 val; /* * Global MMIO */ rc = read_afu_info(dev, fn, OCXL_DVSEC_TEMPL_MMIO_GLOBAL, &val); if (rc) return rc; afu->global_mmio_bar = EXTRACT_BITS(val, 0, 2); afu->global_mmio_offset = EXTRACT_BITS(val, 16, 31) << 16; rc = read_afu_info(dev, fn, OCXL_DVSEC_TEMPL_MMIO_GLOBAL + 4, &val); if (rc) return rc; afu->global_mmio_offset += (u64) val << 32; rc = read_afu_info(dev, fn, OCXL_DVSEC_TEMPL_MMIO_GLOBAL_SZ, &val); if (rc) return rc; afu->global_mmio_size = val; /* * Per-process MMIO */ rc = read_afu_info(dev, fn, OCXL_DVSEC_TEMPL_MMIO_PP, &val); if (rc) return rc; afu->pp_mmio_bar = EXTRACT_BITS(val, 0, 2); afu->pp_mmio_offset = EXTRACT_BITS(val, 16, 31) << 16; rc = read_afu_info(dev, fn, OCXL_DVSEC_TEMPL_MMIO_PP + 4, &val); if (rc) return rc; afu->pp_mmio_offset += (u64) val << 32; rc = read_afu_info(dev, fn, OCXL_DVSEC_TEMPL_MMIO_PP_SZ, &val); if (rc) return rc; afu->pp_mmio_stride = val; return 0; }
static int read_afu_control(struct pci_dev *dev, struct ocxl_afu_config *afu) { int pos; u8 val8; u16 val16; pos = find_dvsec_afu_ctrl(dev, afu->idx); if (!pos) { dev_err(&dev->dev, "Can't find AFU control DVSEC for AFU %d\n", afu->idx); return -ENODEV; } afu->dvsec_afu_control_pos = pos; pci_read_config_byte(dev, pos + OCXL_DVSEC_AFU_CTRL_PASID_SUP, &val8); afu->pasid_supported_log = EXTRACT_BITS(val8, 0, 4); pci_read_config_word(dev, pos + OCXL_DVSEC_AFU_CTRL_ACTAG_SUP, &val16); afu->actag_supported = EXTRACT_BITS(val16, 0, 11); return 0; }
static void read_pasid(struct pci_dev *dev, struct ocxl_fn_config *fn) { u16 val; int pos; pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_PASID); if (!pos) { /* * PASID capability is not mandatory, but there * shouldn't be any AFU */ dev_dbg(&dev->dev, "Function doesn't require any PASID\n"); fn->max_pasid_log = -1; goto out; } pci_read_config_word(dev, pos + PCI_PASID_CAP, &val); fn->max_pasid_log = EXTRACT_BITS(val, 8, 12); out: dev_dbg(&dev->dev, "PASID capability:\n"); dev_dbg(&dev->dev, " Max PASID log = %d\n", fn->max_pasid_log); }
int ocxl_config_read_afu(struct pci_dev *dev, struct ocxl_fn_config *fn, struct ocxl_afu_config *afu, u8 afu_idx) { int rc; u32 val32; /* * First, we need to write the AFU idx for the AFU we want to * access. */ WARN_ON((afu_idx & OCXL_DVSEC_AFU_IDX_MASK) != afu_idx); afu->idx = afu_idx; pci_write_config_byte(dev, fn->dvsec_afu_info_pos + OCXL_DVSEC_AFU_INFO_AFU_IDX, afu->idx); rc = read_afu_name(dev, fn, afu); if (rc) return rc; rc = read_afu_info(dev, fn, OCXL_DVSEC_TEMPL_AFU_VERSION, &val32); if (rc) return rc; afu->version_major = EXTRACT_BITS(val32, 24, 31); afu->version_minor = EXTRACT_BITS(val32, 16, 23); afu->afuc_type = EXTRACT_BITS(val32, 14, 15); afu->afum_type = EXTRACT_BITS(val32, 12, 13); afu->profile = EXTRACT_BITS(val32, 0, 7); rc = read_afu_mmio(dev, fn, afu); if (rc) return rc; rc = read_afu_info(dev, fn, OCXL_DVSEC_TEMPL_MEM_SZ, &val32); if (rc) return rc; afu->log_mem_size = EXTRACT_BITS(val32, 0, 7); rc = read_afu_control(dev, afu); if (rc) return rc; dev_dbg(&dev->dev, "AFU configuration:\n"); dev_dbg(&dev->dev, " name = %s\n", afu->name); dev_dbg(&dev->dev, " version = %d.%d\n", afu->version_major, afu->version_minor); dev_dbg(&dev->dev, " global mmio bar = %hhu\n", afu->global_mmio_bar); dev_dbg(&dev->dev, " global mmio offset = %#llx\n", afu->global_mmio_offset); dev_dbg(&dev->dev, " global mmio size = %#x\n", afu->global_mmio_size); dev_dbg(&dev->dev, " pp mmio bar = %hhu\n", afu->pp_mmio_bar); dev_dbg(&dev->dev, " pp mmio offset = %#llx\n", afu->pp_mmio_offset); dev_dbg(&dev->dev, " pp mmio stride = %#x\n", afu->pp_mmio_stride); dev_dbg(&dev->dev, " mem size (log) = %hhu\n", afu->log_mem_size); dev_dbg(&dev->dev, " pasid supported (log) = %u\n", afu->pasid_supported_log); dev_dbg(&dev->dev, " actag supported = %u\n", afu->actag_supported); rc = validate_afu(dev, afu); return rc; }
/** * Initialize a new decoded CFE entry using the provided encoded CFE data. Any resources held by a successfully * initialized instance must be freed via plcrash_async_cfe_entry_free(); * * @param entry The entry instance to initialize. * @param cpu_type The target architecture of the CFE data, encoded as a Mach-O CPU type. Interpreting CFE data is * architecture-specific, and Apple has not defined encodings for all supported architectures. * @param encoding The CFE entry data, in the hosts' native byte order. * * @internal * This code supports sparse register lists for the EBP_FRAME and RBP_FRAME modes. It's unclear as to whether these * actually ever occur in the wild, but they are supported by Apple's unwinddump tool. */ apigee_plcrash_error_t apigee_plcrash_async_cfe_entry_init (apigee_plcrash_async_cfe_entry_t *entry, cpu_type_t cpu_type, uint32_t encoding) { apigee_plcrash_error_t ret; /* Target-neutral initialization */ entry->cpu_type = cpu_type; entry->stack_adjust = 0; /* Perform target-specific decoding */ if (cpu_type == CPU_TYPE_X86) { uint32_t mode = encoding & UNWIND_X86_MODE_MASK; switch (mode) { case UNWIND_X86_MODE_EBP_FRAME: { entry->type = APIGEE_PLCRASH_ASYNC_CFE_ENTRY_TYPE_FRAME_PTR; /* Extract the register frame offset */ entry->stack_offset = -(EXTRACT_BITS(encoding, UNWIND_X86_EBP_FRAME_OFFSET) * sizeof(uint32_t)); /* Extract the register values. They're stored as a bitfield of of 3 bit values. We support * sparse entries, but terminate the loop if no further entries remain. */ uint32_t regs = EXTRACT_BITS(encoding, UNWIND_X86_EBP_FRAME_REGISTERS); entry->register_count = 0; for (uint32_t i = 0; i < PLCRASH_ASYNC_CFE_SAVED_REGISTER_MAX; i++) { /* Check for completion */ uint32_t remaining = regs >> (3 * i); if (remaining == 0) break; /* Map to the correct PLCrashReporter register name */ uint32_t reg = remaining & 0x7; ret = apigee_plcrash_async_map_register_name(reg, &entry->register_list[i], cpu_type); if (ret != APIGEE_PLCRASH_ESUCCESS) { PLCF_DEBUG("Failed to map register value of %" PRIx32, reg); return ret; } /* Update the register count */ entry->register_count++; } return APIGEE_PLCRASH_ESUCCESS; } case UNWIND_X86_MODE_STACK_IMMD: case UNWIND_X86_MODE_STACK_IND: { /* These two types are identical except for the interpretation of the stack offset and adjustment values */ if (mode == UNWIND_X86_MODE_STACK_IMMD) { entry->type = APIGEE_PLCRASH_ASYNC_CFE_ENTRY_TYPE_FRAMELESS_IMMD; entry->stack_offset = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_SIZE) * sizeof(uint32_t); } else { entry->type = APIGEE_PLCRASH_ASYNC_CFE_ENTRY_TYPE_FRAMELESS_INDIRECT; entry->stack_offset = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_SIZE); entry->stack_adjust = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_ADJUST) * sizeof(uint32_t); } /* Extract the register values */ entry->register_count = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_COUNT); uint32_t encoded_regs = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION); uint32_t decoded_regs[PLCRASH_ASYNC_CFE_SAVED_REGISTER_MAX]; apigee_plcrash_async_cfe_register_decode(encoded_regs, entry->register_count, decoded_regs); /* Map to the correct PLCrashReporter register names */ for (uint32_t i = 0; i < entry->register_count; i++) { ret = apigee_plcrash_async_map_register_name(decoded_regs[i], &entry->register_list[i], cpu_type); if (ret != APIGEE_PLCRASH_ESUCCESS) { PLCF_DEBUG("Failed to map register value of %" PRIx32, entry->register_list[i]); return ret; } } return APIGEE_PLCRASH_ESUCCESS; } case UNWIND_X86_MODE_DWARF: entry->type = APIGEE_PLCRASH_ASYNC_CFE_ENTRY_TYPE_DWARF; /* Extract the register frame offset */ entry->stack_offset = EXTRACT_BITS(encoding, UNWIND_X86_DWARF_SECTION_OFFSET); entry->register_count = 0; return APIGEE_PLCRASH_ESUCCESS; case 0: /* Handle a NULL encoding. This interpretation is derived from Apple's actual implementation; the correct interpretation of * a 0x0 value is not defined in what documentation exists. */ entry->type = APIGEE_PLCRASH_ASYNC_CFE_ENTRY_TYPE_NONE; entry->stack_offset = 0; entry->register_count = 0; return APIGEE_PLCRASH_ESUCCESS; default: PLCF_DEBUG("Unexpected entry mode of %" PRIx32, mode); return APIGEE_PLCRASH_ENOTSUP; } // Unreachable __builtin_trap(); return APIGEE_PLCRASH_EINTERNAL; } else if (cpu_type == CPU_TYPE_X86_64) {