/** * get_dlpar_nodes * * @param list_type * @returns pointer to node list on success, NULL otherwise */ struct dr_node * get_dlpar_nodes(uint32_t node_types) { struct dr_connector *drc_list = NULL; struct dr_node *node_list = NULL; struct dirent *de; DIR *d; char path[1024]; say(DEBUG, "Getting node types 0x%08x\n", node_types); d = opendir(OFDT_BASE); if (d == NULL) { say(ERROR, "failed to open %s\n%s\n", OFDT_BASE, strerror(errno)); return NULL; } while ((de = readdir(d)) != NULL) { if ((de->d_type != DT_DIR) || is_dot_dir(de->d_name)) continue; memset(path, 0, 1024); sprintf(path, "%s/%s", OFDT_BASE, de->d_name); if ((! strcmp(de->d_name, "vdevice")) && (node_types & VIO_NODES)) add_pci_vio_node(path, VIO_DEV, &node_list); else if (! strncmp(de->d_name, "pci@", 4)) { if (node_types & PCI_NODES) add_pci_vio_node(path, PCI_DLPAR_DEV, &node_list); else if (node_types & PHB_NODES) { if (drc_list == NULL) drc_list = get_drc_info(OFDT_BASE); add_phb_node(path, drc_list, &node_list); } } else if ((! strncmp(de->d_name, "lhea@", 5)) && (node_types & HEA_NODES)) { if (drc_list == NULL) drc_list = get_drc_info(OFDT_BASE); add_hea_node(path, drc_list, &node_list); } } closedir(d); if (node_list != NULL) { add_linux_devices(NULL, node_list); if (node_types & PHB_NODES) update_phb_ic_info(node_list); } return node_list; }
/** * acquire_hp_children * * @param slot_of_path * @param n_acquired * @returns 0 on success, !0 otherwise */ int acquire_hp_children(char *slot_of_path, int *n_acquired) { struct dr_connector *drc_list, *drc; int rc; int failure = 0, count = 0; drc_list = get_drc_info(slot_of_path); if (drc_list == NULL) { /* No hotplug-capable children */ return 0; } for (drc = drc_list; drc != NULL; drc = drc->next) { if (is_hp_type(drc->type)) { rc = acquire_hp_resource(drc, slot_of_path); if (rc) { say(ERROR, "failed to acquire %s\n", drc->name); failure = 1; } count++; } } *n_acquired = count; return failure; }
/** * add_pci_vio_node * @bried Add a PCI or virtual device node * * @param path ofdt path to this node * @param dev_type type of device * @param node_list pointer to list to add node to * @returns 0 on success, !0 otherwise */ static int add_pci_vio_node(const char *path, int dev_type, struct dr_node **node_list) { struct dr_connector *drc_list; struct dr_connector *drc; struct dr_node *node; int child_dev_type = 0; int rc = -1; drc_list = get_drc_info(path); if (drc_list == NULL) return -1; for (drc = drc_list; drc != NULL; drc = drc->next) { switch (dev_type) { case PCI_HP_DEV: if (! is_hp_type(drc->type)) continue; child_dev_type = dev_type; break; case PCI_DLPAR_DEV: case VIO_DEV: if (! is_logical_type(drc->type)) continue; child_dev_type = dev_type; break; case PHB_DEV: if (is_logical_type(drc->type)) child_dev_type = PCI_DLPAR_DEV; else child_dev_type = PCI_HP_DEV; break; } node = alloc_dr_node(drc, child_dev_type, path); if (node == NULL) { say(ERROR, "Could not allocate pci/vio node\n"); return -1; } if (child_dev_type == PCI_HP_DEV) node->is_owned = 1; rc = init_node(node); if (rc) { free(node); return rc; } node->next = *node_list; *node_list = node; } return rc; }
/** * get_drc_by_name * @brief Retrieve a dr_connector with the specified drc_name * * This routine searches the drc lists for a dr_connector with the * specified name starting at the specified directory. If a dr_connector * is found the root_dir that the dr_connector was found in is also * filled out. * * @param drc_name name of the dr_connector to search for * @param drc pointer to a drc to point to the found dr_connector * @param root_dir pointer to buf to fill in with root directory * @param start_dir, directory to start searching * @returns 0 on success (drc and root_dir filled in), !0 on failure */ int get_drc_by_name(char *drc_name, struct dr_connector *drc, char *root_dir, char *start_dir) { struct dr_connector *drc_list = NULL; struct dr_connector *drc_entry; struct dirent *de; DIR *d; int rc = -1; memset(drc, 0, sizeof(*drc)); /* Try to get the drc in this directory */ drc_list = get_drc_info(start_dir); if (drc_list == NULL) return -1; drc_entry = search_drc_list(drc_list, NULL, DRC_NAME, drc_name); if (drc_entry != NULL) { memcpy(drc, drc_entry, sizeof(*drc)); sprintf(root_dir, "%s", start_dir); return 0; } /* If we didn't find it here, check the subdirs */ d = opendir(start_dir); if (d == NULL) return -1; while ((de = readdir(d)) != NULL) { char dir_path[DR_PATH_MAX]; if ((de->d_type != DT_DIR) || is_dot_dir(de->d_name)) continue; sprintf(dir_path, "%s/%s", start_dir, de->d_name); rc = get_drc_by_name(drc_name, drc, root_dir, dir_path); if (rc == 0) break; } closedir(d); return rc; }
int drslot_chrp_phb(struct options *opts) { int rc = -1; if (! phb_dlpar_capable()) { say(ERROR, "DLPAR PHB operations are not supported on" "this kernel."); return rc; } if (!opts->usr_drc_name) { struct dr_connector *drc_list = get_drc_info(OFDT_BASE); opts->usr_drc_name = drc_index_to_name(opts->usr_drc_index, drc_list); if (!opts->usr_drc_name) { say(ERROR, "Could not locate DRC name for DRC index: 0x%x", opts->usr_drc_index); return -1; } } switch(opts->action) { case ADD: rc = add_phb(opts); break; case REMOVE: rc = remove_phb(opts); break; case QUERY: rc = query_phb(opts); break; } return rc; }
/** * add_child_node * * Create information about the Open Firmware node and * add that information to the appropriate per-node list of * Open Firmware nodes. Also create the corresponding information * for any PCI device. * * NOTES: * 1) does not need to be concerned about one or more Open * Firmware nodes having the used-by-rtas property present. * One of the RTAS services used during removing a PCI adapter * must take the appropriate action (most likely eliminate RTAS * usage) in this case. * 2) Open Firmware node RPA physical location code: * * [Un.m-]Pn[-Pm]-In[[/Zn]-An] * * where * Un.m is for the enclosure for multi-enclosue systems * Pn is for the planar * In is for the slot * /Zn is for the connector on the adapter card * An is for the device connected to the adapter * * note * There may be multiple levels of planars [Pm]. * * RECOVERY OPERATION: * 1) This function does not add the Open Firmware node if the user * mode process has exceeded all available malloc space. This * should not happen based on the rather small total amount of * memory allocation required. The node is marked as skip. * 2) This function does not add the device information if there * is a problem initializing device. The node is marked as skip. * * @param parent * @param child_path */ static void add_child_node(struct dr_node *parent, char *child_path) { struct dr_connector *drc_list, *drc; char loc_code[DR_BUF_SZ]; char *slash; struct dr_node *child; uint my_drc_index; int rc; assert(parent != NULL); assert(strlen(child_path) != 0); /* Make sure that the Open Firmware node is not added twice * in case the ibm,my-drc-index property is put in all nodes * for the adapter instead of just the ones at the connector. */ if (parent->children != NULL) { struct dr_node *tmp; for (tmp = parent->children; tmp; tmp = tmp->next) { if (! strcmp(tmp->ofdt_path, child_path)) return; } } /* Create the Open Firmware node's information and insert that * information into the node's list based on the node's RPA * physical location code. Ignore the OF node if the node * does not have an RPA physical location code because that is * a firmware error. */ rc = get_property(child_path, "ibm,loc-code", &loc_code, DR_BUF_SZ); if (rc) return; /* Skip the Open Firmware node if it is a device node. Determine that * the node is for a device by looking for a hyphen after the last * slash (...-In/Z1-An). */ slash = strrchr(loc_code, '/'); if (slash != NULL) { char *hyphen; hyphen = strchr(slash, '-'); if (hyphen != NULL) return; *slash = '\0'; } if (parent->dev_type == PCI_HP_DEV) { /* hotplug */ /* Squadrons don't have "/" in devices' (scsi, * ethernet, tokenring ...) loc-code strings. * * Skip the Open Firmware node if the node's RPA * physical location code does not match the node's * location code. Ignore the connector information, * i.e. information after last slash if no hyphen * follows. */ if ((strcmp(parent->drc_name, loc_code) != 0) && (slash != NULL)) { parent->skip = 1; return; } } /* Restore the slash because the full RPA location code * is saved for each OF node so that the connector * information can be used to sort the OF node list for * each node. */ if (slash != NULL) *slash = '/'; if (get_my_drc_index(child_path, &my_drc_index)) return; /* need the drc-info in the dir above */ slash = strrchr(child_path, '/'); *slash = '\0'; drc_list = get_drc_info(child_path); *slash = '/'; for (drc = drc_list; drc != NULL; drc = drc->next) { if (drc->index == my_drc_index) break; } /* Allocate space for the Open Firmware node information. */ child = alloc_dr_node(drc, parent->dev_type, child_path); if (child == NULL) { parent->skip = 1; return; } if ((! strcmp(parent->drc_type, "SLOT")) && (parent->dev_type == PCI_DLPAR_DEV)) snprintf(child->ofdt_dname, DR_STR_MAX, "%s", parent->ofdt_dname); else get_property(child_path, "name", child->ofdt_dname, sizeof(child->ofdt_dname)); switch (parent->dev_type) { case PCI_HP_DEV: case PCI_DLPAR_DEV: { get_ofdt_uint_property(child_path, "vendor-id", &child->pci_vendor_id); get_ofdt_uint_property(child_path, "device-id", &child->pci_device_id); get_ofdt_uint_property(child_path, "class_code", &child->pci_class_code); break; } case HEA_DEV: { child->dev_type = HEA_PORT_DEV; get_ofdt_uint_property(child_path, "ibm,hea-port-no", &child->hea_port_no); get_ofdt_uint_property(child_path, "ibm,hea-port-tenure", &child->hea_port_tenure); break; } } child->next = parent->children; parent->children = child; }
/** * init_cpu_info * @brief Initialize cpu information * * @returns pointer to cpu_info on success, NULL otherwise */ static int init_cpu_info(struct dr_info *dr_info) { struct dr_connector *drc_list, *drc; struct dr_node *cpu, *cpu_list = NULL; DIR *d; struct dirent *de; int rc = 0; drc_list = get_drc_info(CPU_OFDT_BASE); if (drc_list == NULL) { say(ERROR, "Could not get drc information for %s\n", CPU_OFDT_BASE); return -1; } /* For cpu dlpar, we need a list of all possible cpus on the system */ for (drc = drc_list; drc; drc = drc->next) { cpu = alloc_dr_node(drc, CPU_DEV, NULL); if (cpu == NULL) { say(ERROR, "Could not allocate CPU node structure: " "%s\n", strerror(errno)); free_node(cpu_list); return -1; } cpu->next = cpu_list; cpu_list = cpu; } d = opendir(CPU_OFDT_BASE); if (d == NULL) { say(ERROR, "Could not open %s: %s\n", CPU_OFDT_BASE, strerror(errno)); free_node(cpu_list); return -1; } while ((de = readdir(d)) != NULL) { char path[DR_PATH_MAX]; if ((de->d_type != DT_DIR) || is_dot_dir(de->d_name)) continue; if (! strncmp(de->d_name, "PowerPC", 7)) { uint32_t my_drc_index; memset(path, 0, 1024); sprintf(path, "%s/%s", CPU_OFDT_BASE, de->d_name); rc = get_my_drc_index(path, &my_drc_index); if (rc) { say(ERROR, "Could not retrieve drc index for " "%s\n", path); break; } for (cpu = cpu_list; cpu; cpu = cpu->next) { if (cpu->drc_index == my_drc_index) break; } if (cpu == NULL) { say(ERROR, "Could not find cpu with drc index " "%x\n", my_drc_index); rc = -1; break; } rc = update_cpu_node(cpu, path, dr_info); if (rc) break; say(DEBUG, "Found cpu %s\n", cpu->name); } } closedir(d); if (rc) free_node(cpu_list); else dr_info->all_cpus = cpu_list; return rc; }
/** * get_lmbs * @brief Build a list of all possible lmbs for the system * * @param sort LMB_NORMAL_SORT or LMB_REVERSE_SORT to control sort order * * @return list of lmbs, NULL on failure */ struct lmb_list_head * get_lmbs(unsigned int sort) { struct lmb_list_head *lmb_list = NULL; struct dr_node *lmb = NULL; struct stat sbuf; char buf[DR_STR_MAX]; int rc = 0; lmb_list = zalloc(sizeof(*lmb_list)); if (lmb_list == NULL) { say(DEBUG, "Could not allocate LMB list head\n"); return NULL; } lmb_list->sort = sort; rc = get_str_attribute("/sys/devices/system/memory", "/block_size_bytes", &buf, DR_STR_MAX); if (rc) { say(DEBUG, "Could not determine block size bytes for memory.\n"); free_lmbs(lmb_list); return NULL; } block_sz_bytes = strtoul(buf, NULL, 16); /* We also need to know which lmbs are already allocated to * the system and their corresponding memory sections as defined * by sysfs. Walk the device tree and update the appropriate * lmb entries (and their memory sections) as we find their device * tree entries. */ if (stat(DYNAMIC_RECONFIG_MEM, &sbuf)) { struct dr_connector *drc_list, *drc; drc_list = get_drc_info(OFDT_BASE); if (drc_list == NULL) { report_unknown_error(__FILE__, __LINE__); rc = -1; } else { /* For memory dlpar, we need a list of all * posiible memory nodes for the system, initalize * those here. */ for (drc = drc_list; drc; drc = drc->next) { if (strncmp(drc->name, "LMB", 3)) continue; lmb = lmb_list_add(drc->index, lmb_list); if (!lmb) { say(ERROR, "Failed to add LMB (%x)\n", drc->index); rc = -1; break; } lmb_list->lmbs_found++; } } say(INFO, "Maximum of %d LMBs\n", lmb_list->lmbs_found); rc = get_mem_node_lmbs(lmb_list); } else { /* A small hack to here to allow memory add to work in * certain kernels. Due to a bug in the kernel (see comment * in acquire_lmb()) we need to get lmb info from both places. * For a good kernel, the get_mem_node_lmbs routine will not * update the lmb_list. */ rc = get_dynamic_reconfig_lmbs(lmb_list); if (! rc) rc = get_mem_node_lmbs(lmb_list); } if (rc) { free_lmbs(lmb_list); lmb_list = NULL; } else if (sort == LMB_RANDOM_SORT) { shuffle_lmbs(lmb_list); } return lmb_list; }