Skip to contentSkip to conversation

powerpc: add ps3 platform repository support

PATCH In review Geoff Levand 6 participants 13 messages Nov 22, 2006 – Dec 2, 2006

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

PATCH 8/14 powerpc: add ps3 platform repository support

From: Geoff Levand <[email protected]>

Adds support for the PS3 repository.

---
 arch/powerpc/platforms/ps3/repository.c |  840 ++++++++++++++++++++++++++++++++
 include/asm-powerpc/ps3.h               |  139 +++++
 2 files changed, 979 insertions(+)
arch/powerpc/platforms/ps3/repository.c new file +840 -0
Index: cell--common--6/arch/powerpc/platforms/ps3/repository.c
===================================================================
@@ -0,0 +1,840 @@
+/*
+ *  PS3 repository routines.
+ *
+ *  Copyright (C) 2006 Sony Computer Entertainment Inc.
+ *  Copyright 2006 Sony Corp.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <asm/ps3.h>
+#include <asm/lv1call.h>
+
+enum ps3_vendor_id {
+	PS3_VENDOR_ID_NONE = 0,
+	PS3_VENDOR_ID_SONY = 0x8000000000000000UL,
+};
+
+enum ps3_lpar_id {
+	PS3_LPAR_ID_CURRENT = 0,
+	PS3_LPAR_ID_PME = 1,
+};
+
+#define dump_field(_a, _b) _dump_field(_a, _b, __func__, __LINE__)
+static void _dump_field(const char *hdr, u64 n, const char* func, int line)
+{
+#if defined(DEBUG)
+	char s[16];
+	const char *const in = (const char *)&n;
+	unsigned int i;
+
+	for (i = 0; i < 8; i++)
+		s[i] = (in[i] <= 126 && in[i] >= 32) ? in[i] : '.';
+	s[i] = 0;
+
+	pr_debug("%s:%d: %s%016lx : %s\n", func, line, hdr, n, s);
+#endif
+}
+
+#define dump_node_name(_a, _b, _c, _d, _e) \
+	_dump_node_name(_a, _b, _c, _d, _e, __func__, __LINE__)
+static void _dump_node_name (unsigned int lpar_id, u64 n1, u64 n2, u64 n3,
+	u64 n4, const char* func, int line)
+{
+	pr_debug("%s:%d: lpar: %u\n", func, line, lpar_id);
+	_dump_field("n1: ", n1, func, line);
+	_dump_field("n2: ", n2, func, line);
+	_dump_field("n3: ", n3, func, line);
+	_dump_field("n4: ", n4, func, line);
+}
+
+#define dump_node(_a, _b, _c, _d, _e, _f, _g) \
+	_dump_node(_a, _b, _c, _d, _e, _f, _g, __func__, __LINE__)
+static void _dump_node(unsigned int lpar_id, u64 n1, u64 n2, u64 n3, u64 n4,
+	u64 v1, u64 v2, const char* func, int line)
+{
+	pr_debug("%s:%d: lpar: %u\n", func, line, lpar_id);
+	_dump_field("n1: ", n1, func, line);
+	_dump_field("n2: ", n2, func, line);
+	_dump_field("n3: ", n3, func, line);
+	_dump_field("n4: ", n4, func, line);
+	pr_debug("%s:%d: v1: %016lx\n", func, line, v1);
+	pr_debug("%s:%d: v2: %016lx\n", func, line, v2);
+}
+
+/**
+ * make_first_field - Make the first field of a repository node name.
+ * @text: Text portion of the field.
+ * @index: Numeric index portion of the field.  Use zero for 'don't care'.
+ *
+ * This routine sets the vendor id to zero (non-vendor specific).
+ * Returns field value.
+ */
+
+static u64 make_first_field(const char *text, u64 index)
+{
+	u64 n;
+
+	strncpy((char *)&n, text, 8);
+	return PS3_VENDOR_ID_NONE + (n >> 32) + index;
+}
+
+/**
+ * make_field - Make subsequent fields of a repository node name.
+ * @text: Text portion of the field.  Use "" for 'don't care'.
+ * @index: Numeric index portion of the field.  Use zero for 'don't care'.
+ *
+ * Returns field value.
+ */
+
+static u64 make_field(const char *text, u64 index)
+{
+	u64 n;
+
+	strncpy((char *)&n, text, 8);
+	return n + index;
+}
+
+/**
+ * read_node - Read a repository node from raw fields.
+ * @n1: First field of node name.
+ * @n2: Second field of node name.  Use zero for 'don't care'.
+ * @n3: Third field of node name.  Use zero for 'don't care'.
+ * @n4: Fourth field of node name.  Use zero for 'don't care'.
+ * @v1: First repository value (high word).
+ * @v2: Second repository value (low word).  Optional parameter, use zero
+ *      for 'don't care'.
+ */
+
+static int read_node(unsigned int lpar_id, u64 n1, u64 n2, u64 n3, u64 n4,
+	u64 *_v1, u64 *_v2)
+{
+	int result;
+	u64 v1;
+	u64 v2;
+
+	if (lpar_id == PS3_LPAR_ID_CURRENT) {
+		u64 id;
+		lv1_get_logical_partition_id(&id);
+		lpar_id = id;
+	}
+
+	result = lv1_get_repository_node_value(lpar_id, n1, n2, n3, n4, &v1,
+		&v2);
+
+	if (result) {
+		pr_debug("%s:%d: lv1_get_repository_node_value failed: %s\n",
+			__func__, __LINE__, ps3_result(result));
+		dump_node_name(lpar_id, n1, n2, n3, n4);
+		return result;
+	}
+
+	dump_node(lpar_id, n1, n2, n3, n4, v1, v2);
+
+	if (_v1)
+		*_v1 = v1;
+	if (_v2)
+		*_v2 = v2;
+
+	if (v1 && !_v1)
+		pr_debug("%s:%d: warning: discarding non-zero v1: %016lx\n",
+			__func__, __LINE__, v1);
+	if (v2 && !_v2)
+		pr_debug("%s:%d: warning: discarding non-zero v2: %016lx\n",
+			__func__, __LINE__, v2);
+
+	return result;
+}
+
+int ps3_repository_read_bus_str(unsigned int bus_index, const char *bus_str,
+	u64 *value)
+{
+	return read_node(PS3_LPAR_ID_PME,
+		make_first_field("bus", bus_index),
+		make_field(bus_str, 0),
+		0, 0,
+		value, 0);
+}
+
+int ps3_repository_read_bus_id(unsigned int bus_index, unsigned int *bus_id)
+{
+	int result;
+	u64 v1;
+	u64 v2; /* unused */
+
+	result = read_node(PS3_LPAR_ID_PME,
+		make_first_field("bus", bus_index),
+		make_field("id", 0),
+		0, 0,
+		&v1, &v2);
+	*bus_id = v1;
+	return result;
+}
+
+int ps3_repository_read_bus_type(unsigned int bus_index,
+	enum ps3_bus_type *bus_type)
+{
+	int result;
+	u64 v1;
+
+	result = read_node(PS3_LPAR_ID_PME,
+		make_first_field("bus", bus_index),
+		make_field("type", 0),
+		0, 0,
+		&v1, 0);
+	*bus_type = v1;
+	return result;
+}
+
+int ps3_repository_read_bus_num_dev(unsigned int bus_index,
+	unsigned int *num_dev)
+{
+	int result;
+	u64 v1;
+
+	result = read_node(PS3_LPAR_ID_PME,
+		make_first_field("bus", bus_index),
+		make_field("num_dev", 0),
+		0, 0,
+		&v1, 0);
+	*num_dev = v1;
+	return result;
+}
+
+int ps3_repository_read_dev_str(unsigned int bus_index,
+	unsigned int dev_index, const char *dev_str, u64 *value)
+{
+	return read_node(PS3_LPAR_ID_PME,
+		make_first_field("bus", bus_index),
+		make_field("dev", dev_index),
+		make_field(dev_str, 0),
+		0,
+		value, 0);
+}
+
+int ps3_repository_read_dev_id(unsigned int bus_index, unsigned int dev_index,
+	unsigned int *dev_id)
+{
+	int result;
+	u64 v1;
+
+	result = read_node(PS3_LPAR_ID_PME,
+		make_first_field("bus", bus_index),
+		make_field("dev", dev_index),
+		make_field("id", 0),
+		0,
+		&v1, 0);
+	*dev_id = v1;
+	return result;
+}
+
+int ps3_repository_read_dev_type(unsigned int bus_index,
+	unsigned int dev_index, enum ps3_dev_type *dev_type)
+{
+	int result;
+	u64 v1;
+
+	result = read_node(PS3_LPAR_ID_PME,
+		make_first_field("bus", bus_index),
+		make_field("dev", dev_index),
+		make_field("type", 0),
+		0,
+		&v1, 0);
+	*dev_type = v1;
+	return result;
+}
+
+int ps3_repository_read_dev_intr(unsigned int bus_index,
+	unsigned int dev_index, unsigned int intr_index,
+	unsigned int *intr_type, unsigned int* interrupt_id)
+{
+	int result;
+	u64 v1;
+	u64 v2;
+
+	result = read_node(PS3_LPAR_ID_PME,
+		make_first_field("bus", bus_index),
+		make_field("dev", dev_index),
+		make_field("intr", intr_index),
+		0,
+		&v1, &v2);
+	*intr_type = v1;
+	*interrupt_id = v2;
+	return result;
+}
+
+int ps3_repository_read_dev_reg_type(unsigned int bus_index,
+	unsigned int dev_index, unsigned int reg_index, unsigned int *reg_type)
+{
+	int result;
+	u64 v1;
+
+	result = read_node(PS3_LPAR_ID_PME,
+		make_first_field("bus", bus_index),
+		make_field("dev", dev_index),
+		make_field("reg", reg_index),
+		make_field("type", 0),
+		&v1, 0);
+	*reg_type = v1;
+	return result;
+}
+
+int ps3_repository_read_dev_reg_addr(unsigned int bus_index,
+	unsigned int dev_index, unsigned int reg_index, u64 *bus_addr, u64 *len)
+{
+	return read_node(PS3_LPAR_ID_PME,
+		make_first_field("bus", bus_index),
+		make_field("dev", dev_index),
+		make_field("reg", reg_index),
+		make_field("data", 0),
+		bus_addr, len);
+}
+
+int ps3_repository_read_dev_reg(unsigned int bus_index,
+	unsigned int dev_index, unsigned int reg_index, unsigned int *reg_type,
+	u64 *bus_addr, u64 *len)
+{
+	int result = ps3_repository_read_dev_reg_type(bus_index, dev_index,
+		reg_index, reg_type);
+	return result ? result
+		: ps3_repository_read_dev_reg_addr(bus_index, dev_index,
+		reg_index, bus_addr, len);
+}
+
+#if defined(DEBUG)
+int ps3_repository_dump_resource_info(unsigned int bus_index,
+	unsigned int dev_index)
+{
+	int result = 0;
+	unsigned int res_index;
+
+	pr_debug(" -> %s:%d: (%u:%u)\n", __func__, __LINE__,
+		bus_index, dev_index);
+
+	for (res_index = 0; res_index < 10; res_index++) {
+		enum ps3_interrupt_type intr_type;
+		unsigned int interrupt_id;
+
+		result = ps3_repository_read_dev_intr(bus_index, dev_index,
+			res_index, &intr_type, &interrupt_id);
+
+		if (result) {
+			if (result !=  LV1_NO_ENTRY)
+				pr_debug("%s:%d ps3_repository_read_dev_intr"
+					" (%u:%u) failed\n", __func__, __LINE__,
+					bus_index, dev_index);
+			break;
+		}
+
+		pr_debug("%s:%d (%u:%u) intr_type %u, interrupt_id %u\n",
+			__func__, __LINE__, bus_index, dev_index, intr_type,
+			interrupt_id);
+	}
+
+	for (res_index = 0; res_index < 10; res_index++) {
+		enum ps3_region_type reg_type;
+		u64 bus_addr;
+		u64 len;
+
+		result = ps3_repository_read_dev_reg(bus_index, dev_index,
+			res_index, &reg_type, &bus_addr, &len);
+
+		if (result) {
+			if (result !=  LV1_NO_ENTRY)
+				pr_debug("%s:%d ps3_repository_read_dev_reg"
+					" (%u:%u) failed\n", __func__, __LINE__,
+					bus_index, dev_index);
+			break;
+		}
+
+		pr_debug("%s:%d (%u:%u) reg_type %u, bus_addr %lxh, len %lxh\n",
+			__func__, __LINE__, bus_index, dev_index, reg_type,
+			bus_addr, len);
+	}
+
+	pr_debug(" <- %s:%d\n", __func__, __LINE__);
+	return result;
+}
+
+static int dump_device_info(unsigned int bus_index, unsigned int num_dev)
+{
+	int result = 0;
+	unsigned int dev_index;
+
+	pr_debug(" -> %s:%d: bus_%u\n", __func__, __LINE__, bus_index);
+
+	for (dev_index = 0; dev_index < num_dev; dev_index++) {
+		enum ps3_dev_type dev_type;
+		unsigned int dev_id;
+
+		result = ps3_repository_read_dev_type(bus_index, dev_index,
+			&dev_type);
+
+		if (result) {
+			pr_debug("%s:%d ps3_repository_read_dev_type"
+				" (%u:%u) failed\n", __func__, __LINE__,
+				bus_index, dev_index);
+			break;
+		}
+
+		result = ps3_repository_read_dev_id(bus_index, dev_index,
+			&dev_id);
+
+		if (result) {
+			pr_debug("%s:%d ps3_repository_read_dev_id"
+				" (%u:%u) failed\n", __func__, __LINE__,
+				bus_index, dev_index);
+			continue;
+		}
+
+		pr_debug("%s:%d  (%u:%u): dev_type %u, dev_id %u\n", __func__,
+			__LINE__, bus_index, dev_index, dev_type, dev_id);
+
+		ps3_repository_dump_resource_info(bus_index, dev_index);
+	}
+
+	pr_debug(" <- %s:%d\n", __func__, __LINE__);
+	return result;
+}
+
+int ps3_repository_dump_bus_info(void)
+{
+	int result = 0;
+	unsigned int bus_index;
+
+	pr_debug(" -> %s:%d\n", __func__, __LINE__);
+
+	for (bus_index = 0; bus_index < 10; bus_index++) {
+		enum ps3_bus_type bus_type;
+		unsigned int bus_id;
+		unsigned int num_dev;
+
+		result = ps3_repository_read_bus_type(bus_index, &bus_type);
+
+		if (result) {
+			pr_debug("%s:%d read_bus_type(%u) failed\n",
+				__func__, __LINE__, bus_index);
+			break;
+		}
+
+		result = ps3_repository_read_bus_id(bus_index, &bus_id);
+
+		if (result) {
+			pr_debug("%s:%d read_bus_id(%u) failed\n",
+				__func__, __LINE__, bus_index);
+			continue;
+		}
+
+		if (bus_index != bus_id)
+			pr_debug("%s:%d bus_index != bus_id\n",
+				__func__, __LINE__);
+
+		result = ps3_repository_read_bus_num_dev(bus_index, &num_dev);
+
+		if (result) {
+			pr_debug("%s:%d read_bus_num_dev(%u) failed\n",
+				__func__, __LINE__, bus_index);
+			continue;
+		}
+
+		pr_debug("%s:%d bus_%u: bus_type %u, bus_id %u, num_dev %u\n",
+			__func__, __LINE__, bus_index, bus_type, bus_id,
+			num_dev);
+
+		dump_device_info(bus_index, num_dev);
+	}
+
+	pr_debug(" <- %s:%d\n", __func__, __LINE__);
+	return result;
+}
+#endif /* defined(DEBUG) */
+
+static int find_device(unsigned int bus_index, unsigned int num_dev,
+	unsigned int start_dev_index, enum ps3_dev_type dev_type,
+	struct ps3_repository_device *dev)
+{
+	int result = 0;
+	unsigned int dev_index;
+
+	pr_debug("%s:%d: find dev_type %u\n", __func__, __LINE__, dev_type);
+
+	dev->dev_index = UINT_MAX;
+
+	for (dev_index = start_dev_index; dev_index < num_dev; dev_index++) {
+		enum ps3_dev_type x;
+
+		result = ps3_repository_read_dev_type(bus_index, dev_index,
+			&x);
+
+		if (result) {
+			pr_debug("%s:%d read_dev_type failed\n",
+				__func__, __LINE__);
+			return result;
+		}
+
+		if (x == dev_type)
+			break;
+	}
+
+	BUG_ON(dev_index == num_dev);
+
+	pr_debug("%s:%d: found dev_type %u at dev_index %u\n",
+		__func__, __LINE__, dev_type, dev_index);
+
+	result = ps3_repository_read_dev_id(bus_index, dev_index,
+		&dev->did.dev_id);
+
+	if (result) {
+		pr_debug("%s:%d read_dev_id failed\n",
+			__func__, __LINE__);
+		return result;
+	}
+
+	dev->dev_index = dev_index;
+
+	pr_debug("%s:%d found: dev_id %u\n", __func__, __LINE__,
+		dev->did.dev_id);
+
+	return result;
+}
+
+int ps3_repository_find_device (enum ps3_bus_type bus_type,
+	enum ps3_dev_type dev_type,
+	const struct ps3_repository_device *start_dev,
+	struct ps3_repository_device *dev)
+{
+	int result = 0;
+	unsigned int bus_index;
+	unsigned int num_dev;
+
+	pr_debug("%s:%d: find bus_type %u, dev_type %u\n", __func__, __LINE__,
+		bus_type, dev_type);
+
+	dev->bus_index = UINT_MAX;
+
+	for (bus_index = start_dev ? start_dev->bus_index : 0; bus_index < 10;
+		bus_index++) {
+		enum ps3_bus_type x;
+
+		result = ps3_repository_read_bus_type(bus_index, &x);
+
+		if (result) {
+			pr_debug("%s:%d read_bus_type failed\n",
+				__func__, __LINE__);
+			return result;
+		}
+		if (x == bus_type)
+			break;
+	}
+
+	BUG_ON(bus_index == 10);
+
+	pr_debug("%s:%d: found bus_type %u at bus_index %u\n",
+		__func__, __LINE__, bus_type, bus_index);
+
+	result = ps3_repository_read_bus_num_dev(bus_index, &num_dev);
+
+	if (result) {
+		pr_debug("%s:%d read_bus_num_dev failed\n",
+			__func__, __LINE__);
+		return result;
+	}
+
+	result = find_device(bus_index, num_dev, start_dev
+		? start_dev->dev_index + 1 : 0, dev_type, dev);
+
+	if (result) {
+		pr_debug("%s:%d get_did failed\n", __func__, __LINE__);
+		return result;
+	}
+
+	result = ps3_repository_read_bus_id(bus_index, &dev->did.bus_id);
+
+	if (result) {
+		pr_debug("%s:%d read_bus_id failed\n",
+			__func__, __LINE__);
+		return result;
+	}
+
+	dev->bus_index = bus_index;
+
+	pr_debug("%s:%d found: bus_id %u, dev_id %u\n",
+		__func__, __LINE__, dev->did.bus_id, dev->did.dev_id);
+
+	return result;
+}
+
+int ps3_repository_find_interrupt(const struct ps3_repository_device *dev,
+	enum ps3_interrupt_type intr_type, unsigned int *interrupt_id)
+{
+	int result = 0;
+	unsigned int res_index;
+
+	pr_debug("%s:%d: find intr_type %u\n", __func__, __LINE__, intr_type);
+
+	*interrupt_id = UINT_MAX;
+
+	for (res_index = 0; res_index < 10; res_index++) {
+		enum ps3_interrupt_type t;
+		unsigned int id;
+
+		result = ps3_repository_read_dev_intr(dev->bus_index,
+			dev->dev_index, res_index, &t, &id);
+
+		if (result) {
+			pr_debug("%s:%d read_dev_intr failed\n",
+				__func__, __LINE__);
+			return result;
+		}
+
+		if (t == intr_type) {
+			*interrupt_id = id;
+			break;
+		}
+	}
+
+	BUG_ON(res_index == 10);
+
+	pr_debug("%s:%d: found intr_type %u at res_index %u\n",
+		__func__, __LINE__, intr_type, res_index);
+
+	return result;
+}
+
+int ps3_repository_find_region(const struct ps3_repository_device *dev,
+	enum ps3_region_type reg_type, u64 *bus_addr, u64 *len)
+{
+	int result = 0;
+	unsigned int res_index;
+
+	pr_debug("%s:%d: find reg_type %u\n", __func__, __LINE__, reg_type);
+
+	*bus_addr = *len = 0;
+
+	for (res_index = 0; res_index < 10; res_index++) {
+		enum ps3_region_type t;
+		u64 a;
+		u64 l;
+
+		result = ps3_repository_read_dev_reg(dev->bus_index,
+			dev->dev_index, res_index, &t, &a, &l);
+
+		if (result) {
+			pr_debug("%s:%d read_dev_reg failed\n",
+				__func__, __LINE__);
+			return result;
+		}
+
+		if (t == reg_type) {
+			*bus_addr = a;
+			*len = l;
+			break;
+		}
+	}
+
+	BUG_ON(res_index == 10);
+
+	pr_debug("%s:%d: found reg_type %u at res_index %u\n",
+		__func__, __LINE__, reg_type, res_index);
+
+	return result;
+}
+
+int ps3_repository_read_rm_size(unsigned int ppe_id, u64 *rm_size)
+{
+	return read_node(PS3_LPAR_ID_CURRENT,
+		make_first_field("bi", 0),
+		make_field("pu", 0),
+		ppe_id,
+		make_field("rm_size", 0),
+		rm_size, 0);
+}
+
+int ps3_repository_read_region_total(u64 *region_total)
+{
+	return read_node(PS3_LPAR_ID_CURRENT,
+		make_first_field("bi", 0),
+		make_field("rgntotal", 0),
+		0, 0,
+		region_total, 0);
+}
+
+/**
+ * ps3_repository_read_mm_info - Read mm info for single pu system.
+ * @rm_base: Real mode memory base address.
+ * @rm_size: Real mode memory size.
+ * @region_total: Maximum memory region size.
+ */
+
+int ps3_repository_read_mm_info(u64 *rm_base, u64 *rm_size, u64 *region_total)
+{
+	int result;
+	u64 ppe_id;
+
+	lv1_get_logical_ppe_id(&ppe_id);
+	*rm_base = 0;
+	result = ps3_repository_read_rm_size(ppe_id, rm_size);
+	return result ? result
+		: ps3_repository_read_region_total(region_total);
+}
+
+/**
+ * ps3_repository_read_num_spu_reserved - Number of physical spus reserved.
+ * @num_spu: Number of physical spus.
+ */
+
+int ps3_repository_read_num_spu_reserved(unsigned int *num_spu_reserved)
+{
+	int result;
+	u64 v1;
+
+	result = read_node(PS3_LPAR_ID_CURRENT,
+		make_first_field("bi", 0),
+		make_field("spun", 0),
+		0, 0,
+		&v1, 0);
+	*num_spu_reserved = v1;
+	return result;
+}
+
+/**
+ * ps3_repository_read_num_spu_resource_id - Number of spu resource reservations.
+ * @num_resource_id: Number of spu resource ids.
+ */
+
+int ps3_repository_read_num_spu_resource_id(unsigned int *num_resource_id)
+{
+	int result;
+	u64 v1;
+
+	result = read_node(PS3_LPAR_ID_CURRENT,
+		make_first_field("bi", 0),
+		make_field("spursvn", 0),
+		0, 0,
+		&v1, 0);
+	*num_resource_id = v1;
+	return result;
+}
+
+/**
+ * ps3_repository_read_spu_resource_id - spu resource reservation id value.
+ * @res_index: Resource reservation index.
+ * @resource_type: Resource reservation type.
+ * @resource_id: Resource reservation id.
+ */
+
+int ps3_repository_read_spu_resource_id(unsigned int res_index,
+	enum ps3_spu_resource_type* resource_type, unsigned int *resource_id)
+{
+	int result;
+	u64 v1;
+	u64 v2;
+
+	result = read_node(PS3_LPAR_ID_CURRENT,
+		make_first_field("bi", 0),
+		make_field("spursv", 0),
+		res_index,
+		0,
+		&v1, &v2);
+	*resource_type = v1;
+	*resource_id = v2;
+	return result;
+}
+
+int ps3_repository_read_boot_dat_address(u64 *address)
+{
+	return read_node(PS3_LPAR_ID_CURRENT,
+		make_first_field("bi", 0),
+		make_field("boot_dat", 0),
+		make_field("address", 0),
+		0,
+		address, 0);
+}
+
+int ps3_repository_read_boot_dat_size(unsigned int *size)
+{
+	int result;
+	u64 v1;
+
+	result = read_node(PS3_LPAR_ID_CURRENT,
+		make_first_field("bi", 0),
+		make_field("boot_dat", 0),
+		make_field("size", 0),
+		0,
+		&v1, 0);
+	*size = v1;
+	return result;
+}
+
+/**
+  * ps3_repository_read_boot_dat_info - Get address and size of cell_ext_os_area.
+  * address: lpar address of cell_ext_os_area
+  * @size: size of cell_ext_os_area
+  */
+
+int ps3_repository_read_boot_dat_info(u64 *lpar_addr, unsigned int *size)
+{
+	int result;
+
+	*size = 0;
+	result = ps3_repository_read_boot_dat_address(lpar_addr);
+	return result ? result
+		: ps3_repository_read_boot_dat_size(size);
+}
+
+int ps3_repository_read_num_be(unsigned int *num_be)
+{
+	int result;
+	u64 v1;
+
+	result = read_node(PS3_LPAR_ID_PME,
+		make_first_field("ben", 0),
+		0,
+		0,
+		0,
+		&v1, 0);
+	*num_be = v1;
+	return result;
+}
+
+int ps3_repository_read_be_node_id(unsigned int be_index, u64 *node_id)
+{
+	return read_node(PS3_LPAR_ID_PME,
+		make_first_field("be", be_index),
+		0,
+		0,
+		0,
+		node_id, 0);
+}
+
+int ps3_repository_read_tb_freq(u64 node_id, u64 *tb_freq)
+{
+	return read_node(PS3_LPAR_ID_PME,
+		make_first_field("be", 0),
+		node_id,
+		make_field("clock", 0),
+		0,
+		tb_freq, 0);
+}
+
+int ps3_repository_read_be_tb_freq(unsigned int be_index, u64 *tb_freq)
+{
+	int result;
+	u64 node_id;
+
+	*tb_freq = 0;
+	result = ps3_repository_read_be_node_id(0, &node_id);
+	return result ? result
+		: ps3_repository_read_tb_freq(node_id, tb_freq);
+}
include/asm-powerpc/ps3.h +139 -0

143 lines not on this page · raw message

#1 lore raw

cast truncates bits from constant value (8000000000000000 becomes 0)

arch/powerpc/platforms/ps3/repository.c line 27

Quoted 4 lines · Geoff Levand
arch/powerpc/platforms/ps3/repository.c new file +4 -0
+enum ps3_vendor_id {
+	PS3_VENDOR_ID_NONE = 0,
+	PS3_VENDOR_ID_SONY = 0x8000000000000000UL,
+};

I've just ran `make C=1' (PPC in 64-bit mode, and sparse is called with -m64), and noticed that sparse (cloned from
git://git.kernel.org/pub/scm/devel/sparse/sparse.git a few minutes ago) complains about the second value with:

| warning: cast truncates bits from constant value (8000000000000000 becomes 0)

Section 6.7.2.2.4 of C99 says:

| Each enumerated type shall be compatible with char, a signed integer type, or
| an unsigned integer type. The choice of type is implementation-defined, but
| shall be capable of representing the values of all the members of the
| enumeration.

The code snippet

| u64 x = PS3_VENDOR_ID_SONY;
| printk("PS3_VENDOR_ID_SONY = %lu\n", x);

does print the expected (i.e. non-zero) result.

Hence this looks like a bug in sparse.

Gr{oetje,eeting}s,

Geert

--
Geert Uytterhoeven -- Sony Network and Software Technology Center Europe (NSCE) [email protected] ------- The Corporate Village, Da Vincilaan 7-D1 Voice +32-2-7008453 Fax +32-2-7008622 ---------------- B-1935 Zaventem, Belgium

#2 lore raw

Re: cast truncates bits from constant value (8000000000000000 becomes 0)

Quoted 11 lines · Geert Uytterhoeven

arch/powerpc/platforms/ps3/repository.c line 27

Quoted 4 lines · Geoff Levand
arch/powerpc/platforms/ps3/repository.c new file +4 -0
+enum ps3_vendor_id {
+	PS3_VENDOR_ID_NONE = 0,
+	PS3_VENDOR_ID_SONY = 0x8000000000000000UL,
+};

I've just ran `make C=1' (PPC in 64-bit mode, and sparse is called with -m64), and noticed that sparse (cloned from
git://git.kernel.org/pub/scm/devel/sparse/sparse.git a few minutes ago) complains about the second value with:

Quoted 8 lines

| warning: cast truncates bits from constant value (8000000000000000 becomes 0)

Section 6.7.2.2.4 of C99 says:

| Each enumerated type shall be compatible with char, a signed integer type, or
| an unsigned integer type. The choice of type is implementation-defined, but
| shall be capable of representing the values of all the members of the
| enumeration.

FWIW, that code is *not* valid C99; note that all enumeration members must fit the range of int (see 6.7.2.2.2). What you quote speaks about the objects of type enum <something>, which is not the same as type of enum members. In C99 it's int, plain and simple. Value outside of the range of int => undefined behaviour. Note that with

	enum foo {A, B} x;

you might very well have sizeof(A) != sizeof(x).

IOW, you are using a gccism in an area where gcc is bloody inconsistent in the set of bugs it shows in different versions.

Generally safe way is to split the anonymous huge enum members into single-element enums and pray that gcc will at least stay consistent in handling of those.

#3 lore raw

Re: cast truncates bits from constant value (8000000000000000 becomes 0)

Quoted 16 lines · Al Viro
Quoted 15 lines · Geert Uytterhoeven

arch/powerpc/platforms/ps3/repository.c line 27

Quoted 4 lines · Geoff Levand
arch/powerpc/platforms/ps3/repository.c new file +4 -0
+enum ps3_vendor_id {
+	PS3_VENDOR_ID_NONE = 0,
+	PS3_VENDOR_ID_SONY = 0x8000000000000000UL,
+};

I've just ran `make C=1' (PPC in 64-bit mode, and sparse is called with -m64), and noticed that sparse (cloned from
git://git.kernel.org/pub/scm/devel/sparse/sparse.git a few minutes ago) complains about the second value with:

| warning: cast truncates bits from constant value (8000000000000000 becomes 0)

Section 6.7.2.2.4 of C99 says:

Quoted 7 lines
Quoted 4 lines
| Each enumerated type shall be compatible with char, a signed integer type, or
| an unsigned integer type. The choice of type is implementation-defined, but
| shall be capable of representing the values of all the members of the
| enumeration.

FWIW, that code is *not* valid C99; note that all enumeration members must fit the range of int (see 6.7.2.2.2). What you quote speaks about the

You're right. I missed that one.

Quoted 2 lines

IOW, you are using a gccism in an area where gcc is bloody inconsistent in the set of bugs it shows in different versions.

Hmmm...

Quoted 3 lines

Generally safe way is to split the anonymous huge enum members into single-element enums and pray that gcc will at least stay consistent in handling of those.

Or fall back to #defines, which is what we were trying to avoid in the first place (i.e. group related values together in enums)...

Gr{oetje,eeting}s,

Geert

--
Geert Uytterhoeven -- Sony Network and Software Technology Center Europe (NSCE) [email protected] ------- The Corporate Village, Da Vincilaan 7-D1 Voice +32-2-7008453 Fax +32-2-7008622 ---------------- B-1935 Zaventem, Belgium

#4 lore raw

Re: cast truncates bits from constant value (8000000000000000 becomes 0)

Quoted 30 lines · Geert Uytterhoeven
Quoted 16 lines · Al Viro
Quoted 15 lines · Geert Uytterhoeven

arch/powerpc/platforms/ps3/repository.c line 27

Quoted 4 lines · Geoff Levand
arch/powerpc/platforms/ps3/repository.c new file +4 -0
+enum ps3_vendor_id {
+	PS3_VENDOR_ID_NONE = 0,
+	PS3_VENDOR_ID_SONY = 0x8000000000000000UL,
+};

I've just ran `make C=1' (PPC in 64-bit mode, and sparse is called with -m64), and noticed that sparse (cloned from
git://git.kernel.org/pub/scm/devel/sparse/sparse.git a few minutes ago) complains about the second value with:

| warning: cast truncates bits from constant value (8000000000000000 becomes 0)

Section 6.7.2.2.4 of C99 says:

Quoted 7 lines
Quoted 4 lines
| Each enumerated type shall be compatible with char, a signed integer type, or
| an unsigned integer type. The choice of type is implementation-defined, but
| shall be capable of representing the values of all the members of the
| enumeration.

FWIW, that code is *not* valid C99; note that all enumeration members must fit the range of int (see 6.7.2.2.2). What you quote speaks about the

You're right. I missed that one.

Quoted 2 lines

IOW, you are using a gccism in an area where gcc is bloody inconsistent in the set of bugs it shows in different versions.

Quoted 8 lines

Hmmm...

Quoted 3 lines

Generally safe way is to split the anonymous huge enum members into single-element enums and pray that gcc will at least stay consistent in handling of those.

Or fall back to #defines, which is what we were trying to avoid in the first place (i.e. group related values together in enums)...

The enum achieves very little in this case IMHO, it's just a more verbose way of #defining - and it doesn't even have reliable semantics.

cheers

Signature · 8 lines
Michael Ellerman
OzLabs, IBM Australia Development Lab

wwweb: http://michael.ellerman.id.au
phone: +61 2 6212 1183 (tie line 70 21183)

We do not inherit the earth from our ancestors,
we borrow it from our children. - S.M.A.R.T Person
#5 lore raw

Re: cast truncates bits from constant value (8000000000000000 becomes 0)

In reply to Geert Uytterhoeven
Quoted 21 lines · Geert Uytterhoeven

arch/powerpc/platforms/ps3/repository.c line 27

Quoted 4 lines · Geoff Levand
arch/powerpc/platforms/ps3/repository.c new file +4 -0
+enum ps3_vendor_id {
+	PS3_VENDOR_ID_NONE = 0,
+	PS3_VENDOR_ID_SONY = 0x8000000000000000UL,
+};

I've just ran `make C=1' (PPC in 64-bit mode, and sparse is called with -m64), and noticed that sparse (cloned from
git://git.kernel.org/pub/scm/devel/sparse/sparse.git a few minutes ago) complains about the second value with:

| warning: cast truncates bits from constant value (8000000000000000 becomes 0)

Section 6.7.2.2.4 of C99 says:

| Each enumerated type shall be compatible with char, a signed integer type, or
| an unsigned integer type. The choice of type is implementation-defined, but
| shall be capable of representing the values of all the members of the
| enumeration.
Quoted 8 lines

The code snippet

| u64 x = PS3_VENDOR_ID_SONY;
| printk("PS3_VENDOR_ID_SONY = %lu\n", x);

does print the expected (i.e. non-zero) result.

Hence this looks like a bug in sparse.

It's really a bug in gcc, but it's documented, so it's a "feature".

Gcc allows large enums, but does so in such a strange manner that it's totally hopeless to catch problems. Also, putting a value that is larger than "unsigned int" into an enum is really setting yourself up for bugs and not even guaranteed to work for standard C, so sparse takes a dim view of it and just says that enums are limited in size to "int" or "unsigned int".

You can either ignore that warning or just use a #define.

Linus

#6 lore raw

Re: cast truncates bits from constant value (8000000000000000 becomes 0)

Quoted 34 lines · Linus Torvalds
Quoted 21 lines · Geert Uytterhoeven

arch/powerpc/platforms/ps3/repository.c line 27

Quoted 4 lines · Geoff Levand
arch/powerpc/platforms/ps3/repository.c new file +4 -0
+enum ps3_vendor_id {
+	PS3_VENDOR_ID_NONE = 0,
+	PS3_VENDOR_ID_SONY = 0x8000000000000000UL,
+};

I've just ran `make C=1' (PPC in 64-bit mode, and sparse is called with -m64), and noticed that sparse (cloned from
git://git.kernel.org/pub/scm/devel/sparse/sparse.git a few minutes ago) complains about the second value with:

| warning: cast truncates bits from constant value (8000000000000000 becomes 0)

Section 6.7.2.2.4 of C99 says:

| Each enumerated type shall be compatible with char, a signed integer type, or
| an unsigned integer type. The choice of type is implementation-defined, but
| shall be capable of representing the values of all the members of the
| enumeration.
Quoted 8 lines

The code snippet

| u64 x = PS3_VENDOR_ID_SONY;
| printk("PS3_VENDOR_ID_SONY = %lu\n", x);

does print the expected (i.e. non-zero) result.

Hence this looks like a bug in sparse.

It's really a bug in gcc, but it's documented, so it's a "feature".

Quoted 8 lines

Gcc allows large enums, but does so in such a strange manner that it's totally hopeless to catch problems. Also, putting a value that is larger than "unsigned int" into an enum is really setting yourself up for bugs and not even guaranteed to work for standard C, so sparse takes a dim view of it and just says that enums are limited in size to "int" or "unsigned int".

You can either ignore that warning or just use a #define.

One of the gcc maintainers (Andrew Pinski) told me to set it up that way, so I figured it was safe.

-Geoff

#7 lore raw

Re: cast truncates bits from constant value (8000000000000000 becomes 0)

Quoted 3 lines · Geoff Levand

One of the gcc maintainers (Andrew Pinski) told me to set it up that way, so I figured it was safe.

It _is_ safe, as long as you stick to gcc, and as long as you don't mix signs in your values, for example.

But we actually want to let people compile even the kernel with other compilers than gcc. Most of the time, that means that we strongly encourage those compiler people to support all the gcc extensions (let's face it, standardization is good, and open standards work better than closed ones, and gcc is the most widely spread open and portable compiler BY FAR, so it would be stupid to _not_ do that).

But at times, some of the gcc extensions aren't necessarily that well defined or thought out, or simply not worth it. The extended type system for enums in gcc is just basically messy, and it doesn't really offer you anything important.

Compared with inline assembly, for example, inline assembly may be even messier and more complicated, but inline assembly definitely offers you something very important. The strange enum type extensions? What do they really offer above and beyond the standard C preprocessor #define's? Apart from some (very limited) name scoping advantages, there really isn't anything that it really helps with.

And as Al already mentioned, some of the extensions aren't even compatible within different versions of gcc itself. Admittedly they are all fairly special, but basically the "type" of different individual enum members simply isn't well-defined in all cases, and will even depend on thigns like the order they were declared in, if I recall correctly.

Messy, messy.

Linus

#8 lore raw

Re: cast truncates bits from constant value (8000000000000000 becomes 0)

Linus,

Quoted 6 lines
Quoted 2 lines

One of the gcc maintainers (Andrew Pinski) told me to set it up that way, so I figured it was safe.

It _is_ safe, as long as you stick to gcc, and as long as you don't mix signs in your values, for example.

But we actually want to let people compile even the kernel with other compilers than gcc. Most of the time, that means that we strongly encourage those compiler people to support all the gcc extensions (let's face it, standardization is good, and open standards work better than closed ones, and gcc is the most widely spread open and portable compiler BY FAR, so it would be stupid to _not_ do that).

The C++ Standard explicitly supports enumeration constants not having type int. See sentence 858 of
http://www.coding-guidelines.com/cbook/cbook1_0b.pdf
for a discussion of the issues (plus some other sentences).

C compiler vendors, at least those who extend the language, are more likely to want to follow the C++ rules (which are documented) than the gcc rules (which are poorly documented).

The C++ way of doing things is also likely to be followed by vendors whose C compiler is enabled by a command line switch on their C++ compiler (eg, at least one vendor based in Seattle).

Signature · 3 lines
Derek M. Jones                              tel: +44 (0) 1252 520 667
Knowledge Software Ltd                      mailto:[email protected]
Applications Standards Conformance Testing    http://www.knosof.co.uk
#9 lore raw

Re: cast truncates bits from constant value (8000000000000000 becomes 0)

Quoted 5 lines · Derek M Jones

The C++ Standard explicitly supports enumeration constants not having type int. See sentence 858 of
http://www.coding-guidelines.com/cbook/cbook1_0b.pdf
for a discussion of the issues (plus some other sentences).

Quoted 7 lines

C compiler vendors, at least those who extend the language, are more likely to want to follow the C++ rules (which are documented) than the gcc rules (which are poorly documented).

The C++ way of doing things is also likely to be followed by vendors whose C compiler is enabled by a command line switch on their C++ compiler (eg, at least one vendor based in Seattle).

"C++ way of doing things" is hardly an endorsement. _IF_ we are changing the way enum works, might as well do it sanely and have the type of enumeration constant same as that of expression initializing it (with usual implicit initializers). But yes, that explicitly changes semantics - enum { A = 0L, B, C }; will have A, B and C long, not int. Direct contradiction with C90/C99...

#10 lore raw

Re: cast truncates bits from constant value (8000000000000000 becomes 0)

Quoted 7 lines · Al Viro

"C++ way of doing things" is hardly an endorsement. _IF_ we are changing the way enum works, might as well do it sanely and have the type of enumeration constant same as that of expression initializing it (with usual implicit initializers). But yes, that explicitly changes semantics - enum { A = 0L, B, C }; will have A, B and C long, not int. Direct contradiction with C90/C99...

Well, the C++ definition at least means that enumerated names have the same types _after_ the enumaration is closed (ie the example I sent out will at least give the same value for the sizeof() of everything).

So the C++ definition is a lot saner than what gcc does now.

On the other hand, I do agree that "keeping the enumerated type the same as the initializer expression type" is _also_ a sane situation. And it's better than the C++ situation in the sense that at least the sizes are _consistent_ (which C++ is not - the size AT DEFINITION time is potentially different from the size AFTER the definition).

So in the C++ world, you have the odd situation that

	enum strange {
		one = (char) 1,
		other = sizeof(one),
	};

we will actually end up with

	other != sizeof(one)

AFTER the declaration (because when "other" got its value, "one" had a type of "char", but _after_ the declaration, "one" will have a type of "size_t" or "int" or whatever, because the final type of "one" depends on the type of the enumerator.

So the C++ definition is really odd, but it's better than what gcc does, because at least all the values end up having a common type at the end.

The version where all the values _keep_ their types all the time, and you can force them to be whatever you want (by just making sure the initializer expression has the right type) is the most flexible one and at least doesn't have inconsistencies like the above example, but it's neither what gcc nor the C++ standard (or any older C standard, for that matter) actually says.

Anyway, because of all this, enum types are a mess. sparse warns if you can't fit it in an "int", which is the traditional and fairly dependable old C behaviour, and basically says "extended integer types for enums are flaky as hell - warn about them!"

Linus

#11 lore raw

Re: cast truncates bits from constant value (8000000000000000 becomes 0)

In reply to Linus Torvalds
Quoted 5 lines · Linus Torvalds

But at times, some of the gcc extensions aren't necessarily that well defined or thought out, or simply not worth it. The extended type system for enums in gcc is just basically messy, and it doesn't really offer you anything important.

Btw, try this stupid program, to see just how _strange_ gcc enums are.. A sizeof of the enum is not the same as the size of the individual entries.

Notice also how the size of the enum entry is _not_ tied to the type of the expression it had, but literally to its _value_. The size of "one" ends up being 4, even though it was initialized with a "1ll" value.

So with gcc-enums, you CANNOT get a sane type result.

In contrast, if you want sane types, you could easily do

	#define one (1ull)
	#define other (0x10000ull)
	#define strange (0x100000000ull)

and they'd all have the same type (and having the same type means that they act the same in expressions - you get the same expression type in mixing these values, _unlike_ the insane gcc enum cases)

Linus

---

enum hello {
	one = 1ll,
	other = 0x10000,
	bigval = 0x1000000000000ll,
};

int main(int argc, char **argv)

{
	printf("%zu %zu %zu %zu\n",
		sizeof(enum hello),
		sizeof(one),
		sizeof(other),
		sizeof(bigval));
	return 0;
}
#12 lore raw

Re: cast truncates bits from constant value (8000000000000000 becomes 0)

Quoted 2 lines · Linus Torvalds

Btw, try this stupid program, to see just how _strange_ gcc enums are.. A sizeof of the enum is not the same as the size of the individual entries.

Yes, interesting. I think your comment regarding compatibility with other compilers is the convincing one.

Another way is to change the encoding of the value such that it fits into the range of an enum. For this particular case, the values are actually for a field in the high 3 bits, so I can just do the shift when the value is used.

-Geoff