mdesc.c 20.3 KB
Newer Older
1 2
/* mdesc.c: Sun4V machine description handling.
 *
3
 * Copyright (C) 2007, 2008 David S. Miller <davem@davemloft.net>
4 5 6
 */
#include <linux/kernel.h>
#include <linux/types.h>
Y
Yinghai Lu 已提交
7
#include <linux/memblock.h>
8
#include <linux/log2.h>
9 10
#include <linux/list.h>
#include <linux/slab.h>
11
#include <linux/mm.h>
12
#include <linux/miscdevice.h>
13
#include <linux/bootmem.h>
14

15
#include <asm/cpudata.h>
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
#include <asm/hypervisor.h>
#include <asm/mdesc.h>
#include <asm/prom.h>
#include <asm/oplib.h>
#include <asm/smp.h>

/* Unlike the OBP device tree, the machine description is a full-on
 * DAG.  An arbitrary number of ARCs are possible from one
 * node to other nodes and thus we can't use the OBP device_node
 * data structure to represent these nodes inside of the kernel.
 *
 * Actually, it isn't even a DAG, because there are back pointers
 * which create cycles in the graph.
 *
 * mdesc_hdr and mdesc_elem describe the layout of the data structure
 * we get from the Hypervisor.
 */
struct mdesc_hdr {
	u32	version; /* Transport version */
	u32	node_sz; /* node block size */
	u32	name_sz; /* name block size */
	u32	data_sz; /* data block size */
38
} __attribute__((aligned(16)));
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61

struct mdesc_elem {
	u8	tag;
#define MD_LIST_END	0x00
#define MD_NODE		0x4e
#define MD_NODE_END	0x45
#define MD_NOOP		0x20
#define MD_PROP_ARC	0x61
#define MD_PROP_VAL	0x76
#define MD_PROP_STR	0x73
#define MD_PROP_DATA	0x64
	u8	name_len;
	u16	resv;
	u32	name_offset;
	union {
		struct {
			u32	data_len;
			u32	data_offset;
		} data;
		u64	val;
	} d;
};

62 63 64 65
struct mdesc_mem_ops {
	struct mdesc_handle *(*alloc)(unsigned int mdesc_size);
	void (*free)(struct mdesc_handle *handle);
};
66

67 68 69 70 71 72 73 74
struct mdesc_handle {
	struct list_head	list;
	struct mdesc_mem_ops	*mops;
	void			*self_base;
	atomic_t		refcnt;
	unsigned int		handle_size;
	struct mdesc_hdr	mdesc;
};
75

76 77 78
static void mdesc_handle_init(struct mdesc_handle *hp,
			      unsigned int handle_size,
			      void *base)
79
{
80 81 82 83 84 85 86
	BUG_ON(((unsigned long)&hp->mdesc) & (16UL - 1));

	memset(hp, 0, handle_size);
	INIT_LIST_HEAD(&hp->list);
	hp->self_base = base;
	atomic_set(&hp->refcnt, 1);
	hp->handle_size = handle_size;
87 88
}

Y
Yinghai Lu 已提交
89
static struct mdesc_handle * __init mdesc_memblock_alloc(unsigned int mdesc_size)
90
{
91
	unsigned int handle_size, alloc_size;
92 93
	struct mdesc_handle *hp;
	unsigned long paddr;
94

95 96 97 98
	handle_size = (sizeof(struct mdesc_handle) -
		       sizeof(struct mdesc_hdr) +
		       mdesc_size);
	alloc_size = PAGE_ALIGN(handle_size);
99

Y
Yinghai Lu 已提交
100
	paddr = memblock_alloc(alloc_size, PAGE_SIZE);
101

102 103 104 105 106
	hp = NULL;
	if (paddr) {
		hp = __va(paddr);
		mdesc_handle_init(hp, handle_size, hp);
	}
107
	return hp;
108 109
}

110
static void __init mdesc_memblock_free(struct mdesc_handle *hp)
111
{
112 113
	unsigned int alloc_size;
	unsigned long start;
114

115 116
	BUG_ON(atomic_read(&hp->refcnt) != 0);
	BUG_ON(!list_empty(&hp->list));
117

118 119 120
	alloc_size = PAGE_ALIGN(hp->handle_size);
	start = __pa(hp);
	free_bootmem_late(start, alloc_size);
121 122
}

Y
Yinghai Lu 已提交
123 124 125
static struct mdesc_mem_ops memblock_mdesc_ops = {
	.alloc = mdesc_memblock_alloc,
	.free  = mdesc_memblock_free,
126 127 128
};

static struct mdesc_handle *mdesc_kmalloc(unsigned int mdesc_size)
129
{
130 131
	unsigned int handle_size;
	void *base;
132

133 134 135 136
	handle_size = (sizeof(struct mdesc_handle) -
		       sizeof(struct mdesc_hdr) +
		       mdesc_size);

137
	base = kmalloc(handle_size + 15, GFP_KERNEL | __GFP_NOFAIL);
138 139 140 141 142 143 144 145 146 147
	if (base) {
		struct mdesc_handle *hp;
		unsigned long addr;

		addr = (unsigned long)base;
		addr = (addr + 15UL) & ~15UL;
		hp = (struct mdesc_handle *) addr;

		mdesc_handle_init(hp, handle_size, base);
		return hp;
148
	}
149 150

	return NULL;
151 152
}

153
static void mdesc_kfree(struct mdesc_handle *hp)
154
{
155 156 157 158
	BUG_ON(atomic_read(&hp->refcnt) != 0);
	BUG_ON(!list_empty(&hp->list));

	kfree(hp->self_base);
159 160
}

161 162 163 164 165 166 167
static struct mdesc_mem_ops kmalloc_mdesc_memops = {
	.alloc = mdesc_kmalloc,
	.free  = mdesc_kfree,
};

static struct mdesc_handle *mdesc_alloc(unsigned int mdesc_size,
					struct mdesc_mem_ops *mops)
168
{
169
	struct mdesc_handle *hp = mops->alloc(mdesc_size);
170

171 172
	if (hp)
		hp->mops = mops;
173

174 175
	return hp;
}
176

177
static void mdesc_free(struct mdesc_handle *hp)
178
{
179 180
	hp->mops->free(hp);
}
181

182 183 184
static struct mdesc_handle *cur_mdesc;
static LIST_HEAD(mdesc_zombie_list);
static DEFINE_SPINLOCK(mdesc_lock);
185

186 187 188 189
struct mdesc_handle *mdesc_grab(void)
{
	struct mdesc_handle *hp;
	unsigned long flags;
190

191 192 193 194 195
	spin_lock_irqsave(&mdesc_lock, flags);
	hp = cur_mdesc;
	if (hp)
		atomic_inc(&hp->refcnt);
	spin_unlock_irqrestore(&mdesc_lock, flags);
196

197
	return hp;
198
}
199
EXPORT_SYMBOL(mdesc_grab);
200

201
void mdesc_release(struct mdesc_handle *hp)
202
{
203
	unsigned long flags;
204

205 206 207 208
	spin_lock_irqsave(&mdesc_lock, flags);
	if (atomic_dec_and_test(&hp->refcnt)) {
		list_del_init(&hp->list);
		hp->mops->free(hp);
209
	}
210
	spin_unlock_irqrestore(&mdesc_lock, flags);
211
}
212
EXPORT_SYMBOL(mdesc_release);
213

214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
static DEFINE_MUTEX(mdesc_mutex);
static struct mdesc_notifier_client *client_list;

void mdesc_register_notifier(struct mdesc_notifier_client *client)
{
	u64 node;

	mutex_lock(&mdesc_mutex);
	client->next = client_list;
	client_list = client;

	mdesc_for_each_node_by_name(cur_mdesc, node, client->node_name)
		client->add(cur_mdesc, node);

	mutex_unlock(&mdesc_mutex);
}

231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
static const u64 *parent_cfg_handle(struct mdesc_handle *hp, u64 node)
{
	const u64 *id;
	u64 a;

	id = NULL;
	mdesc_for_each_arc(a, hp, node, MDESC_ARC_TYPE_BACK) {
		u64 target;

		target = mdesc_arc_target(hp, a);
		id = mdesc_get_property(hp, target,
					"cfg-handle", NULL);
		if (id)
			break;
	}

	return id;
}

250 251 252 253 254 255 256 257 258
/* Run 'func' on nodes which are in A but not in B.  */
static void invoke_on_missing(const char *name,
			      struct mdesc_handle *a,
			      struct mdesc_handle *b,
			      void (*func)(struct mdesc_handle *, u64))
{
	u64 node;

	mdesc_for_each_node_by_name(a, node, name) {
259 260 261
		int found = 0, is_vdc_port = 0;
		const char *name_prop;
		const u64 *id;
262 263
		u64 fnode;

264 265 266 267 268 269 270 271 272 273 274 275 276
		name_prop = mdesc_get_property(a, node, "name", NULL);
		if (name_prop && !strcmp(name_prop, "vdc-port")) {
			is_vdc_port = 1;
			id = parent_cfg_handle(a, node);
		} else
			id = mdesc_get_property(a, node, "id", NULL);

		if (!id) {
			printk(KERN_ERR "MD: Cannot find ID for %s node.\n",
			       (name_prop ? name_prop : name));
			continue;
		}

277
		mdesc_for_each_node_by_name(b, fnode, name) {
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
			const u64 *fid;

			if (is_vdc_port) {
				name_prop = mdesc_get_property(b, fnode,
							       "name", NULL);
				if (!name_prop ||
				    strcmp(name_prop, "vdc-port"))
					continue;
				fid = parent_cfg_handle(b, fnode);
				if (!fid) {
					printk(KERN_ERR "MD: Cannot find ID "
					       "for vdc-port node.\n");
					continue;
				}
			} else
				fid = mdesc_get_property(b, fnode,
							 "id", NULL);
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324

			if (*id == *fid) {
				found = 1;
				break;
			}
		}
		if (!found)
			func(a, node);
	}
}

static void notify_one(struct mdesc_notifier_client *p,
		       struct mdesc_handle *old_hp,
		       struct mdesc_handle *new_hp)
{
	invoke_on_missing(p->node_name, old_hp, new_hp, p->remove);
	invoke_on_missing(p->node_name, new_hp, old_hp, p->add);
}

static void mdesc_notify_clients(struct mdesc_handle *old_hp,
				 struct mdesc_handle *new_hp)
{
	struct mdesc_notifier_client *p = client_list;

	while (p) {
		notify_one(p, old_hp, new_hp);
		p = p->next;
	}
}

325
void mdesc_update(void)
326
{
327 328 329 330
	unsigned long len, real_len, status;
	struct mdesc_handle *hp, *orig_hp;
	unsigned long flags;

331 332
	mutex_lock(&mdesc_mutex);

333 334 335 336 337
	(void) sun4v_mach_desc(0UL, 0UL, &len);

	hp = mdesc_alloc(len, &kmalloc_mdesc_memops);
	if (!hp) {
		printk(KERN_ERR "MD: mdesc alloc fails\n");
338
		goto out;
339 340 341 342 343 344 345 346
	}

	status = sun4v_mach_desc(__pa(&hp->mdesc), len, &real_len);
	if (status != HV_EOK || real_len > len) {
		printk(KERN_ERR "MD: mdesc reread fails with %lu\n",
		       status);
		atomic_dec(&hp->refcnt);
		mdesc_free(hp);
347
		goto out;
348
	}
349

350 351 352
	spin_lock_irqsave(&mdesc_lock, flags);
	orig_hp = cur_mdesc;
	cur_mdesc = hp;
353
	spin_unlock_irqrestore(&mdesc_lock, flags);
354

355 356 357
	mdesc_notify_clients(orig_hp, hp);

	spin_lock_irqsave(&mdesc_lock, flags);
358 359 360 361 362
	if (atomic_dec_and_test(&orig_hp->refcnt))
		mdesc_free(orig_hp);
	else
		list_add(&orig_hp->list, &mdesc_zombie_list);
	spin_unlock_irqrestore(&mdesc_lock, flags);
363 364 365

out:
	mutex_unlock(&mdesc_mutex);
366 367
}

368
static struct mdesc_elem *node_block(struct mdesc_hdr *mdesc)
369 370 371 372
{
	return (struct mdesc_elem *) (mdesc + 1);
}

373
static void *name_block(struct mdesc_hdr *mdesc)
374 375 376 377
{
	return ((void *) node_block(mdesc)) + mdesc->node_sz;
}

378
static void *data_block(struct mdesc_hdr *mdesc)
379 380 381 382
{
	return ((void *) name_block(mdesc)) + mdesc->name_sz;
}

383 384
u64 mdesc_node_by_name(struct mdesc_handle *hp,
		       u64 from_node, const char *name)
385
{
386 387 388 389 390
	struct mdesc_elem *ep = node_block(&hp->mdesc);
	const char *names = name_block(&hp->mdesc);
	u64 last_node = hp->mdesc.node_sz / 16;
	u64 ret;

391 392 393
	if (from_node == MDESC_NODE_NULL) {
		ret = from_node = 0;
	} else if (from_node >= last_node) {
394
		return MDESC_NODE_NULL;
395 396 397
	} else {
		ret = ep[from_node].d.val;
	}
398 399 400 401 402 403 404 405 406 407 408 409 410

	while (ret < last_node) {
		if (ep[ret].tag != MD_NODE)
			return MDESC_NODE_NULL;
		if (!strcmp(names + ep[ret].name_offset, name))
			break;
		ret = ep[ret].d.val;
	}
	if (ret >= last_node)
		ret = MDESC_NODE_NULL;
	return ret;
}
EXPORT_SYMBOL(mdesc_node_by_name);
411

412 413 414 415 416 417 418
const void *mdesc_get_property(struct mdesc_handle *hp, u64 node,
			       const char *name, int *lenp)
{
	const char *names = name_block(&hp->mdesc);
	u64 last_node = hp->mdesc.node_sz / 16;
	void *data = data_block(&hp->mdesc);
	struct mdesc_elem *ep;
419

420 421
	if (node == MDESC_NODE_NULL || node >= last_node)
		return NULL;
422

423 424 425 426 427 428 429 430 431 432
	ep = node_block(&hp->mdesc) + node;
	ep++;
	for (; ep->tag != MD_NODE_END; ep++) {
		void *val = NULL;
		int len = 0;

		switch (ep->tag) {
		case MD_PROP_VAL:
			val = &ep->d.val;
			len = 8;
433 434
			break;

435 436 437 438 439
		case MD_PROP_STR:
		case MD_PROP_DATA:
			val = data + ep->d.data.data_offset;
			len = ep->d.data.data_len;
			break;
440

441
		default:
442 443
			break;
		}
444 445
		if (!val)
			continue;
446

447 448 449 450 451
		if (!strcmp(names + ep->name_offset, name)) {
			if (lenp)
				*lenp = len;
			return val;
		}
452 453
	}

454 455 456
	return NULL;
}
EXPORT_SYMBOL(mdesc_get_property);
457

458 459 460 461 462
u64 mdesc_next_arc(struct mdesc_handle *hp, u64 from, const char *arc_type)
{
	struct mdesc_elem *ep, *base = node_block(&hp->mdesc);
	const char *names = name_block(&hp->mdesc);
	u64 last_node = hp->mdesc.node_sz / 16;
463

464 465
	if (from == MDESC_NODE_NULL || from >= last_node)
		return MDESC_NODE_NULL;
466

467 468 469 470 471 472 473 474 475 476 477
	ep = base + from;

	ep++;
	for (; ep->tag != MD_NODE_END; ep++) {
		if (ep->tag != MD_PROP_ARC)
			continue;

		if (strcmp(names + ep->name_offset, arc_type))
			continue;

		return ep - base;
478
	}
479 480

	return MDESC_NODE_NULL;
481
}
482
EXPORT_SYMBOL(mdesc_next_arc);
483

484
u64 mdesc_arc_target(struct mdesc_handle *hp, u64 arc)
485
{
486 487 488 489 490
	struct mdesc_elem *ep, *base = node_block(&hp->mdesc);

	ep = base + arc;

	return ep->d.val;
491
}
492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509
EXPORT_SYMBOL(mdesc_arc_target);

const char *mdesc_node_name(struct mdesc_handle *hp, u64 node)
{
	struct mdesc_elem *ep, *base = node_block(&hp->mdesc);
	const char *names = name_block(&hp->mdesc);
	u64 last_node = hp->mdesc.node_sz / 16;

	if (node == MDESC_NODE_NULL || node >= last_node)
		return NULL;

	ep = base + node;
	if (ep->tag != MD_NODE)
		return NULL;

	return names + ep->name_offset;
}
EXPORT_SYMBOL(mdesc_node_name);
510

511 512
static u64 max_cpus = 64;

513 514
static void __init report_platform_properties(void)
{
515 516
	struct mdesc_handle *hp = mdesc_grab();
	u64 pn = mdesc_node_by_name(hp, MDESC_NODE_NULL, "platform");
517 518 519
	const char *s;
	const u64 *v;

520
	if (pn == MDESC_NODE_NULL) {
521 522 523 524
		prom_printf("No platform node in machine-description.\n");
		prom_halt();
	}

525
	s = mdesc_get_property(hp, pn, "banner-name", NULL);
526
	printk("PLATFORM: banner-name [%s]\n", s);
527
	s = mdesc_get_property(hp, pn, "name", NULL);
528 529
	printk("PLATFORM: name [%s]\n", s);

530
	v = mdesc_get_property(hp, pn, "hostid", NULL);
531
	if (v)
532
		printk("PLATFORM: hostid [%08llx]\n", *v);
533
	v = mdesc_get_property(hp, pn, "serial#", NULL);
534
	if (v)
535
		printk("PLATFORM: serial# [%08llx]\n", *v);
536
	v = mdesc_get_property(hp, pn, "stick-frequency", NULL);
537
	printk("PLATFORM: stick-frequency [%08llx]\n", *v);
538
	v = mdesc_get_property(hp, pn, "mac-address", NULL);
539
	if (v)
540
		printk("PLATFORM: mac-address [%llx]\n", *v);
541
	v = mdesc_get_property(hp, pn, "watchdog-resolution", NULL);
542
	if (v)
543
		printk("PLATFORM: watchdog-resolution [%llu ms]\n", *v);
544
	v = mdesc_get_property(hp, pn, "watchdog-max-timeout", NULL);
545
	if (v)
546
		printk("PLATFORM: watchdog-max-timeout [%llu ms]\n", *v);
547
	v = mdesc_get_property(hp, pn, "max-cpus", NULL);
548 549 550 551
	if (v) {
		max_cpus = *v;
		printk("PLATFORM: max-cpus [%llu]\n", max_cpus);
	}
552

553 554 555 556 557 558 559 560 561 562 563 564
#ifdef CONFIG_SMP
	{
		int max_cpu, i;

		if (v) {
			max_cpu = *v;
			if (max_cpu > NR_CPUS)
				max_cpu = NR_CPUS;
		} else {
			max_cpu = NR_CPUS;
		}
		for (i = 0; i < max_cpu; i++)
565
			set_cpu_possible(i, true);
566 567 568
	}
#endif

569
	mdesc_release(hp);
570 571
}

572
static void __cpuinit fill_in_one_cache(cpuinfo_sparc *c,
573 574
					struct mdesc_handle *hp,
					u64 mp)
575
{
576 577 578
	const u64 *level = mdesc_get_property(hp, mp, "level", NULL);
	const u64 *size = mdesc_get_property(hp, mp, "size", NULL);
	const u64 *line_size = mdesc_get_property(hp, mp, "line-size", NULL);
579 580 581
	const char *type;
	int type_len;

582
	type = mdesc_get_property(hp, mp, "type", &type_len);
583 584 585

	switch (*level) {
	case 1:
586
		if (of_find_in_proplist(type, "instn", type_len)) {
587 588
			c->icache_size = *size;
			c->icache_line_size = *line_size;
589
		} else if (of_find_in_proplist(type, "data", type_len)) {
590 591 592 593 594 595 596 597 598 599 600 601 602 603 604
			c->dcache_size = *size;
			c->dcache_line_size = *line_size;
		}
		break;

	case 2:
		c->ecache_size = *size;
		c->ecache_line_size = *line_size;
		break;

	default:
		break;
	}

	if (*level == 1) {
605
		u64 a;
606

607 608 609
		mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_FWD) {
			u64 target = mdesc_arc_target(hp, a);
			const char *name = mdesc_node_name(hp, target);
610

611 612
			if (!strcmp(name, "cache"))
				fill_in_one_cache(c, hp, target);
613 614 615 616
		}
	}
}

617
static void __cpuinit mark_core_ids(struct mdesc_handle *hp, u64 mp, int core_id)
618
{
619
	u64 a;
620

621 622 623
	mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_BACK) {
		u64 t = mdesc_arc_target(hp, a);
		const char *name;
624 625
		const u64 *id;

626 627 628
		name = mdesc_node_name(hp, t);
		if (!strcmp(name, "cpu")) {
			id = mdesc_get_property(hp, t, "id", NULL);
629 630 631
			if (*id < NR_CPUS)
				cpu_data(*id).core_id = core_id;
		} else {
632
			u64 j;
633

634 635 636
			mdesc_for_each_arc(j, hp, t, MDESC_ARC_TYPE_BACK) {
				u64 n = mdesc_arc_target(hp, j);
				const char *n_name;
637

638 639
				n_name = mdesc_node_name(hp, n);
				if (strcmp(n_name, "cpu"))
640 641
					continue;

642
				id = mdesc_get_property(hp, n, "id", NULL);
643 644 645 646 647 648 649
				if (*id < NR_CPUS)
					cpu_data(*id).core_id = core_id;
			}
		}
	}
}

650
static void __cpuinit set_core_ids(struct mdesc_handle *hp)
651 652
{
	int idx;
653
	u64 mp;
654 655

	idx = 1;
656 657
	mdesc_for_each_node_by_name(hp, mp, "cache") {
		const u64 *level;
658 659 660
		const char *type;
		int len;

661
		level = mdesc_get_property(hp, mp, "level", NULL);
662 663 664
		if (*level != 1)
			continue;

665
		type = mdesc_get_property(hp, mp, "type", &len);
666
		if (!of_find_in_proplist(type, "instn", len))
667 668
			continue;

669
		mark_core_ids(hp, mp, idx);
670 671 672 673 674

		idx++;
	}
}

675
static void __cpuinit mark_proc_ids(struct mdesc_handle *hp, u64 mp, int proc_id)
676
{
677
	u64 a;
678

679 680 681
	mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_BACK) {
		u64 t = mdesc_arc_target(hp, a);
		const char *name;
682 683
		const u64 *id;

684 685
		name = mdesc_node_name(hp, t);
		if (strcmp(name, "cpu"))
686 687
			continue;

688
		id = mdesc_get_property(hp, t, "id", NULL);
689 690 691 692 693
		if (*id < NR_CPUS)
			cpu_data(*id).proc_id = proc_id;
	}
}

694
static void __cpuinit __set_proc_ids(struct mdesc_handle *hp, const char *exec_unit_name)
695 696
{
	int idx;
697
	u64 mp;
698 699

	idx = 0;
700
	mdesc_for_each_node_by_name(hp, mp, exec_unit_name) {
701 702 703
		const char *type;
		int len;

704
		type = mdesc_get_property(hp, mp, "type", &len);
705 706
		if (!of_find_in_proplist(type, "int", len) &&
		    !of_find_in_proplist(type, "integer", len))
707 708
			continue;

709
		mark_proc_ids(hp, mp, idx);
710 711 712 713 714

		idx++;
	}
}

715
static void __cpuinit set_proc_ids(struct mdesc_handle *hp)
716
{
717 718
	__set_proc_ids(hp, "exec_unit");
	__set_proc_ids(hp, "exec-unit");
719 720
}

721
static void __cpuinit get_one_mondo_bits(const u64 *p, unsigned int *mask,
722
					 unsigned long def, unsigned long max)
723 724 725 726 727 728 729 730 731 732
{
	u64 val;

	if (!p)
		goto use_default;
	val = *p;

	if (!val || val >= 64)
		goto use_default;

733 734 735
	if (val > max)
		val = max;

736 737 738 739 740 741 742
	*mask = ((1U << val) * 64U) - 1U;
	return;

use_default:
	*mask = ((1U << def) * 64U) - 1U;
}

743
static void __cpuinit get_mondo_data(struct mdesc_handle *hp, u64 mp,
744
				     struct trap_per_cpu *tb)
745
{
746
	static int printed;
747 748
	const u64 *val;

749
	val = mdesc_get_property(hp, mp, "q-cpu-mondo-#bits", NULL);
750
	get_one_mondo_bits(val, &tb->cpu_mondo_qmask, 7, ilog2(max_cpus * 2));
751

752
	val = mdesc_get_property(hp, mp, "q-dev-mondo-#bits", NULL);
753
	get_one_mondo_bits(val, &tb->dev_mondo_qmask, 7, 8);
754

755
	val = mdesc_get_property(hp, mp, "q-resumable-#bits", NULL);
756
	get_one_mondo_bits(val, &tb->resum_qmask, 6, 7);
757

758
	val = mdesc_get_property(hp, mp, "q-nonresumable-#bits", NULL);
759 760 761 762 763 764 765 766 767
	get_one_mondo_bits(val, &tb->nonresum_qmask, 2, 2);
	if (!printed++) {
		pr_info("SUN4V: Mondo queue sizes "
			"[cpu(%u) dev(%u) r(%u) nr(%u)]\n",
			tb->cpu_mondo_qmask + 1,
			tb->dev_mondo_qmask + 1,
			tb->resum_qmask + 1,
			tb->nonresum_qmask + 1);
	}
768 769
}

770
static void * __cpuinit mdesc_iterate_over_cpus(void *(*func)(struct mdesc_handle *, u64, int, void *), void *arg, cpumask_t *mask)
771
{
772
	struct mdesc_handle *hp = mdesc_grab();
773
	void *ret = NULL;
774
	u64 mp;
775

776 777
	mdesc_for_each_node_by_name(hp, mp, "cpu") {
		const u64 *id = mdesc_get_property(hp, mp, "id", NULL);
778
		int cpuid = *id;
779 780

#ifdef CONFIG_SMP
781 782 783 784
		if (cpuid >= NR_CPUS) {
			printk(KERN_WARNING "Ignoring CPU %d which is "
			       ">= NR_CPUS (%d)\n",
			       cpuid, NR_CPUS);
785
			continue;
786
		}
787
		if (!cpumask_test_cpu(cpuid, mask))
788 789 790
			continue;
#endif

791 792 793 794 795 796 797 798
		ret = func(hp, mp, cpuid, arg);
		if (ret)
			goto out;
	}
out:
	mdesc_release(hp);
	return ret;
}
799

800 801 802 803 804 805 806 807
static void * __cpuinit record_one_cpu(struct mdesc_handle *hp, u64 mp, int cpuid, void *arg)
{
	ncpus_probed++;
#ifdef CONFIG_SMP
	set_cpu_present(cpuid, true);
#endif
	return NULL;
}
808

809 810 811 812
void __cpuinit mdesc_populate_present_mask(cpumask_t *mask)
{
	if (tlb_type != hypervisor)
		return;
813

814 815 816
	ncpus_probed = 0;
	mdesc_iterate_over_cpus(record_one_cpu, NULL, mask);
}
817

818 819 820 821 822 823
static void * __cpuinit fill_in_one_cpu(struct mdesc_handle *hp, u64 mp, int cpuid, void *arg)
{
	const u64 *cfreq = mdesc_get_property(hp, mp, "clock-frequency", NULL);
	struct trap_per_cpu *tb;
	cpuinfo_sparc *c;
	u64 a;
824

825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848
#ifndef CONFIG_SMP
	/* On uniprocessor we only want the values for the
	 * real physical cpu the kernel booted onto, however
	 * cpu_data() only has one entry at index 0.
	 */
	if (cpuid != real_hard_smp_processor_id())
		return NULL;
	cpuid = 0;
#endif

	c = &cpu_data(cpuid);
	c->clock_tick = *cfreq;

	tb = &trap_block[cpuid];
	get_mondo_data(hp, mp, tb);

	mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_FWD) {
		u64 j, t = mdesc_arc_target(hp, a);
		const char *t_name;

		t_name = mdesc_node_name(hp, t);
		if (!strcmp(t_name, "cache")) {
			fill_in_one_cache(c, hp, t);
			continue;
849 850
		}

851 852 853
		mdesc_for_each_arc(j, hp, t, MDESC_ARC_TYPE_FWD) {
			u64 n = mdesc_arc_target(hp, j);
			const char *n_name;
854

855 856 857 858
			n_name = mdesc_node_name(hp, n);
			if (!strcmp(n_name, "cache"))
				fill_in_one_cache(c, hp, n);
		}
859 860
	}

861 862 863 864 865 866
	c->core_id = 0;
	c->proc_id = -1;

	return NULL;
}

867
void __cpuinit mdesc_fill_in_cpu_data(cpumask_t *mask)
868 869 870
{
	struct mdesc_handle *hp;

871
	mdesc_iterate_over_cpus(fill_in_one_cpu, NULL, mask);
872

873 874 875 876
#ifdef CONFIG_SMP
	sparc64_multi_core = 1;
#endif

877 878
	hp = mdesc_grab();

879 880
	set_core_ids(hp);
	set_proc_ids(hp);
881

882
	mdesc_release(hp);
883 884

	smp_fill_in_sib_core_maps();
885 886
}

887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908
static ssize_t mdesc_read(struct file *file, char __user *buf,
			  size_t len, loff_t *offp)
{
	struct mdesc_handle *hp = mdesc_grab();
	int err;

	if (!hp)
		return -ENODEV;

	err = hp->handle_size;
	if (len < hp->handle_size)
		err = -EMSGSIZE;
	else if (copy_to_user(buf, &hp->mdesc, hp->handle_size))
		err = -EFAULT;
	mdesc_release(hp);

	return err;
}

static const struct file_operations mdesc_fops = {
	.read	= mdesc_read,
	.owner	= THIS_MODULE,
909
	.llseek = noop_llseek,
910 911 912 913 914 915 916 917 918 919 920 921 922 923 924
};

static struct miscdevice mdesc_misc = {
	.minor	= MISC_DYNAMIC_MINOR,
	.name	= "mdesc",
	.fops	= &mdesc_fops,
};

static int __init mdesc_misc_init(void)
{
	return misc_register(&mdesc_misc);
}

__initcall(mdesc_misc_init);

925 926
void __init sun4v_mdesc_init(void)
{
927
	struct mdesc_handle *hp;
928 929 930 931 932 933
	unsigned long len, real_len, status;

	(void) sun4v_mach_desc(0UL, 0UL, &len);

	printk("MDESC: Size is %lu bytes.\n", len);

Y
Yinghai Lu 已提交
934
	hp = mdesc_alloc(len, &memblock_mdesc_ops);
935 936 937 938
	if (hp == NULL) {
		prom_printf("MDESC: alloc of %lu bytes failed.\n", len);
		prom_halt();
	}
939

940
	status = sun4v_mach_desc(__pa(&hp->mdesc), len, &real_len);
941 942 943 944
	if (status != HV_EOK || real_len > len) {
		prom_printf("sun4v_mach_desc fails, err(%lu), "
			    "len(%lu), real_len(%lu)\n",
			    status, len, real_len);
945
		mdesc_free(hp);
946 947 948
		prom_halt();
	}

949
	cur_mdesc = hp;
950 951 952

	report_platform_properties();
}