/**
 * acquire_cpu
 *
 * Acquire a new cpu for this partition from the hypervisor.
 * If drc_index is zero, look up an available index to use.
 *
 * @param drc_index
 * @returns pointer to cpu_info on success, NULL otherwise
 */
static int
acquire_cpu(struct dr_node *cpu, struct dr_info *dr_info)
{
	struct of_node *of_nodes;
	int rc;

	rc = acquire_drc(cpu->drc_index);
	if (rc) {
		say(DEBUG, "Could not acquire drc resources for %s\n",
		    cpu->name);
		return rc;
	}

	of_nodes = configure_connector(cpu->drc_index);
	if (of_nodes == NULL) {
		say(ERROR, "Call to configure_connector failed for %s\n",
		    cpu->name);
		release_drc(cpu->drc_index, CPU_DEV);
		return -1;
	}

	rc = add_device_tree_nodes(CPU_OFDT_BASE, of_nodes);
	free_of_node(of_nodes);
	if (rc) {
		say(ERROR, "Failure to add device tree nodes for %s\n",
		    cpu->name);
		release_drc(cpu->drc_index, CPU_DEV);
		return rc;
	}

	update_cpu_node(cpu, NULL, dr_info);
	refresh_cache_info(dr_info);

	return 0;
}
/**
 * acquire_hp_resource
 *
 * @param drc
 * @param of_path
 * @returns 0 on success, !0 otherwise
 */
static int
acquire_hp_resource(struct dr_connector *drc, char *of_path)
{
	struct of_node *new_nodes;
	int progress = ACQUIRE_HP_START;
	int state;
	int rc;

	state = dr_entity_sense(drc->index);
	if (state == PRESENT || state == NEED_POWER || state == PWR_ONLY) {
		rc = set_power(drc->powerdomain, POWER_ON);
		if (rc) {
			say(ERROR, "set power failed for 0x%x\n",
			    drc->powerdomain);
			return progress;
		}

		progress = ACQUIRE_HP_SPL;
		if (state == PWR_ONLY)
			state = dr_entity_sense(drc->index);
	}

	if (state == PRESENT || state == NEED_POWER) {
		rc = rtas_set_indicator(ISOLATION_STATE, drc->index,
				UNISOLATE);
		if (rc) {
			say(ERROR, "set ind failed for 0x%x\n", drc->index);
			return progress;
		}

		progress = ACQUIRE_HP_UNISO;
		if (state == NEED_POWER)
			state = dr_entity_sense(drc->index);
	}

	if (state < 0) {
		say(ERROR, "invalid state %d\n", state);
		return progress;
	}

	if (state == PRESENT) {
		new_nodes = configure_connector(drc->index);
		if (new_nodes == NULL)
			return progress;

		progress = ACQUIRE_HP_CFGCONN;

		rc = add_device_tree_nodes(of_path, new_nodes);
		if (rc) {
			say(ERROR, "add nodes failed for 0x%x\n", drc->index);
			return progress;
		}
	}

	return 0;
}
示例#3
0
/**
 * acquire_phb
 *
 */
static int acquire_phb(char *drc_name, struct dr_node **phb)
{
    struct dr_connector drc;
    struct of_node *of_nodes;
    char path[DR_PATH_MAX];
    int rc;

    rc = get_drc_by_name(drc_name, &drc, path, OFDT_BASE);
    if (rc) {
        say(ERROR, "Could not find drc index for %s, unable to add the"
            "PHB.\n", drc_name);
        return rc;
    }

    rc = acquire_drc(drc.index);
    if (rc)
        return rc;

    of_nodes = configure_connector(drc.index);
    if (of_nodes == NULL) {
        release_drc(drc.index, PHB_DEV);
        return -1;
    }

    rc = add_device_tree_nodes(path, of_nodes);
    free_of_node(of_nodes);
    if (rc) {
        say(ERROR, "add_device_tree_nodes failed at %s\n", path);
        release_drc(drc.index, PHB_DEV);
        return -1;
    }

    /* Now that the node has been added to the device-tree, retrieve it.
     * This also acts as a sanity check that everything up to this
     * point has succeeded.
     */
    *phb = get_node_by_name(drc_name, PHB_NODES);
    if (*phb == NULL) {
        say(ERROR, "Could not get find \"%s\"\n", drc_name);
        /* or should we call release_drc? but need device type */
        release_drc(drc.index, PHB_DEV);
        return -1;
    }

    return 0;
}
示例#4
0
/**
 * add_new_node
 *
 * @param phandle
 * @param drcindex
 */
static void
add_new_node(unsigned int phandle, unsigned int drcindex)
{
    char *path;
    int rtas_rc;
    struct of_node *new_nodes;/* nodes returned from configure_connector */

    new_nodes = configure_connector(drcindex);

    path = find_phandle(phandle);
    if (path == NULL) {
        say(DEBUG, "Cannot find pnahdle %x\n", phandle);
        return;
    }

    rtas_rc = add_device_tree_nodes(path, new_nodes);
    if (rtas_rc)
        say(DEBUG, "add_device_tree_nodes failed at %s\n", path);
}
示例#5
0
/**
 * add_work
 *
 * Prepares a PCI hot plug slot for adding an adapter, then
 * configures the adapter and any PCI adapters below into the
 * kernel's and /proc's Open Firmware device tree.
 *
 * If there are any errors from the RTAS routines,
 * the slot is powered off, isolated, and the LED is turned off. No
 * configuration is completed.
 * If the OF tree cannot be updated, the slot is powered
 * off, isolated, and the LED is turned off.
 *
 * @param opts
 * @param slot
 * @returns 0 on success, !0 on failure
 */
static int
add_work(struct options *opts, struct dr_node *node)
{
	int pow_state;	/* Tells us if power was turned on when	 */
	int iso_state;	/* Tells us isolation state after 	 */
	int rc;
	struct of_node *new_nodes;/* nodes returned from configure_connector */

	/* if we're continuing, set LED_ON and see if a card is really there. */
	if (process_led(node, LED_ON))
		return -1;

	say(DEBUG, "is calling card_present\n");
	rc = card_present(opts, node, &pow_state, &iso_state);
	if (!rc) {
		say(ERROR, "No PCI card was detected in the specified "
		    "PCI slot.\n");
		rtas_set_indicator(ISOLATION_STATE, node->drc_index, ISOLATE);
		set_power(node->drc_power, POWER_OFF);
		return -1;
	}

	if (!pow_state) {
		say(DEBUG, "is calling set_power(POWER_ON index 0x%x, "
		    "power_domain 0x%x\n", node->drc_index, node->drc_power);

		rc = set_power(node->drc_power, POWER_ON);
		if (rc) {
			if (rc == HW_ERROR)
				say(ERROR, "%s", hw_error);
			else if (rc == SPEED_ERROR)
				say(ERROR, "%s", speed_error);
			else
				say(ERROR, "%s", sw_error);

			rtas_set_indicator(ISOLATION_STATE, node->drc_index,
					   ISOLATE);
			set_power(node->drc_power, POWER_OFF);
			return -1;
		}
	}

	if (!iso_state) {
		say(DEBUG, "calling rtas_set_indicator(UNISOLATE index 0x%x)\n",
		    node->drc_index);

		rc = rtas_set_indicator(ISOLATION_STATE, node->drc_index,
					UNISOLATE);
		if (rc) {
			if (rc == HW_ERROR)
				say(ERROR, "%s", hw_error);
			else
				say(ERROR, "%s", sw_error);

			rtas_set_indicator(ISOLATION_STATE, node->drc_index,
					   ISOLATE);
			set_power(node->drc_power, POWER_OFF);
			return -1;
		}
	}

	/* Now go get all the new nodes for this adapter. If
	 * the return status requires a message, print it out
	 * and exit, otherwise, add the nodes to the OF tree.
	 */
	new_nodes = configure_connector(node->drc_index);
	if (new_nodes == NULL) {
		rtas_set_indicator(ISOLATION_STATE, node->drc_index, ISOLATE);
		set_power(node->drc_power, POWER_OFF);
		return -1;
	}

	say(DEBUG, "Adding %s to %s\n", new_nodes->name, node->ofdt_path);
	rc = add_device_tree_nodes(node->ofdt_path, new_nodes);
	if (rc) {
		say(DEBUG, "add_device_tree_nodes failed at %s\n",
		    node->ofdt_path);
		say(ERROR, "%s", sw_error);
		rtas_set_indicator(ISOLATION_STATE, node->drc_index, ISOLATE);
		set_power(node->drc_power, POWER_OFF);
		return -1;
	}

	return 0;
}
/**
 * release_cpu
 *
 * Release a cpu back to the hypervisor, using the cpu's drc-index.
 * The cpu must have already been offlined.  We remove the cpu from the
 * device tree only when the isolate and set_indicator have succeeded.
 * The caller must not use the value given for the cpu parameter after
 * this function has returned.
 *
 * @param cpu
 * @returns 0 on success, !0 otherwise
 */
int
release_cpu(struct dr_node *cpu, struct dr_info *dr_info)
{
	int release_file;
	int rc;

	release_file = open(CPU_RELEASE_FILE, O_WRONLY);
	if (release_file > 0) {
		/* DLPAR can be done in kernel */
		char *path = cpu->ofdt_path + strlen(OFDT_BASE);
		int write_len = strlen(path);

		say(DEBUG, "Releasing cpu \"%s\"\n", path);
		rc = write(release_file, path, write_len);
		if (rc != write_len)
			say(ERROR, "Release failed! rc = %d\n", rc);
		else
			/* set rc to success */
			rc = 0;

		close(release_file);
	} else {
		/* Must do DLPAR from user-space */
		rc = offline_cpu(cpu);
		if (rc) {
			say(ERROR, "Could not offline cpu %s\n", cpu->drc_name);
			return rc;
		}

		rc = release_drc(cpu->drc_index, CPU_DEV);
		if (rc) {
			say(ERROR, "Could not release drc resources for %s\n",
			    cpu->name);
			return rc;
		}

		rc = remove_device_tree_nodes(cpu->ofdt_path);
		if (rc) {
			struct of_node *of_nodes;

			say(ERROR, "Could not remove device tree nodes %s\n",
			    cpu->name);
		
			of_nodes = configure_connector(cpu->drc_index);
			if (of_nodes == NULL) {
				say(ERROR, "Call to configure_connector failed "
				    "for %s. The device tree\nmay contain "
				    "invalid data for this cpu and a "
				    "re-activation of the partition is "
				    "needed to correct it.\n", cpu->name);
			} else {
				rc = add_device_tree_nodes(CPU_OFDT_BASE,
							   of_nodes);
				free_of_node(of_nodes);
			}

			acquire_drc(cpu->drc_index);
			return rc;
		}

		release_caches(cpu, dr_info);
	}

	return rc;
}
示例#7
0
/**
 * add_device_tree_lmb
 * @brief Update the device tree for the lmb being added..
 *
 * @param lmb lmb to acquire
 * @param lmb_list list of all lmbs
 * @returns 0 on success, !0 otherwise
 */
static int
add_device_tree_lmb(struct dr_node *lmb, struct lmb_list_head *lmb_list)
{
        int rc;

	lmb->lmb_of_node = configure_connector(lmb->drc_index);
	if (lmb->lmb_of_node == NULL) {
		release_drc(lmb->drc_index, MEM_DEV);
		return -1;
	}
	
	if (lmb_list->drconf_buf) {
		errno = 0;
		rc = update_drconf_node(lmb, lmb_list, ADD);
		if (errno == ENODEV) {
			/* Due to bug in pre 2.6.27 kernels, updating the
			 * property in the device tree fails when the phandle
			 * is processed as a signed int instead of unsigned
			 * In this case we provide this little hack to allow
			 * memory add to work on these kernels.
			 */
			say(DEBUG, "Assuming older kernel, trying to add "
			    "node\n");

			sprintf(lmb->ofdt_path, "%s/%s", OFDT_BASE,
				lmb->lmb_of_node->name);
			rc = add_device_tree_nodes(OFDT_BASE, lmb->lmb_of_node);
		} else {
			sprintf(lmb->ofdt_path, "%s/%s", OFDT_BASE,
				"/ibm,dynamic-reconfiguration-memory");
		}
	} else {
		/* Add the new nodes to the device tree */
		sprintf(lmb->ofdt_path, "%s/%s", OFDT_BASE,
			lmb->lmb_of_node->name);
		rc = add_device_tree_nodes(OFDT_BASE, lmb->lmb_of_node);
	}

	if (rc)
		return rc;

	if (! lmb_list->drconf_buf) {
		/* Find the physical address for this lmb.  This is only
		 * needed for non-drconf memory.  The address for drconf
		 * lmbs is correctly initialized when building the lmb list
		 */
		char *tmp = strrchr(lmb->ofdt_path, '@');
		if (tmp == NULL) {
			say(DEBUG, "Could not determine physical address for "
			    "%s\n", lmb->ofdt_path);
			remove_device_tree_nodes(lmb->ofdt_path);
			return -1;
		}

		lmb->lmb_address = strtoull(tmp + 1, NULL, 16);

		rc = get_lmb_size(lmb);
		if (rc) {
			remove_device_tree_nodes(lmb->ofdt_path);
			return rc;
		}
	}

	rc = get_mem_scns(lmb);
	if (rc) 
		remove_device_tree_lmb(lmb, lmb_list);

        return rc;
}