1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright (c) 2000 by Sun Microsystems, Inc.
24 * All rights reserved.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32
33#include <fcode/private.h>
34#include <fcode/log.h>
35
36void
37create_prop(fcode_env_t *env, char *name)
38{
39	push_a_string(env, name);
40	property(env);
41}
42
43void
44create_int_prop(fcode_env_t *env, char *name, int val)
45{
46	PUSH(DS, val);
47	encode_int(env);
48	create_prop(env, name);
49}
50
51void
52create_string_prop(fcode_env_t *env, char *name, char *val)
53{
54	push_a_string(env, val);
55	encode_string(env);
56	create_prop(env, name);
57}
58
59static int
60addr_cmp(void *a, void *b)
61{
62	return ((uchar_t *)a == (uchar_t *)b);
63}
64
65static void *
66add_property_buffer(fcode_env_t *env, int len)
67{
68	void *data = MALLOC(len+1);
69	return (add_resource(&env->propbufs, data, addr_cmp));
70}
71
72static void
73free_property_buffer(fcode_env_t *env, void *buffer)
74{
75	free_resource(&env->propbufs, buffer, addr_cmp);
76	FREE(buffer);
77}
78
79/*
80 * Golden Rule:
81 * DO NOT cache the value of the head of the property list *before*
82 * looking up a property.
83 * This routine is also responsible for purging dead properties
84 * and that *can* affect the head pointer.
85 * you have been warned!
86 */
87prop_t *
88find_property(device_t *d, char *name)
89{
90	prop_t *p = d->properties, *prev;
91	prop_t *found = NULL;
92
93	prev = NULL;
94	while (p && !found) {
95		if (p->name) {
96			if (strcmp(name, p->name) == 0) {
97				found = p;
98			}
99			prev = p;
100			p = p->next;
101		} else {
102			prop_t *dead;
103
104			if (prev)
105				prev->next = p->next;
106			else {
107				/* last prop in chain */
108				d->properties = p->next;
109			}
110			dead = p;
111			p = p->next;
112			FREE(dead->name);
113			FREE(dead->data);
114			FREE(dead);
115		}
116	}
117	return (found);
118}
119
120static prop_t *
121stack_find_property(fcode_env_t *env, device_t *d)
122{
123	char *propname;
124
125	propname = pop_a_string(env, NULL);
126	return (find_property(d, propname));
127}
128
129void
130property(fcode_env_t *env)
131{
132	int datalen;
133	char *propname, *srcptr;
134	prop_t *p;
135	device_t *d;
136
137	CHECK_DEPTH(env, 4, "property");
138	if (MYSELF) {
139		d = MYSELF->device;
140	} else {
141		d = env->current_device;
142		if (!d) {
143			void *buffer;
144
145			two_drop(env);
146			if ((buffer = pop_a_string(env, NULL)) != NULL)
147				free_property_buffer(env, buffer);
148			return;
149		}
150	}
151	propname = pop_a_string(env, NULL);
152	p = find_property(d, propname);
153	if (p == NULL) {
154		p = MALLOC(sizeof (prop_t));
155		p->next = d->properties;
156		d->properties = p;
157		p->name = STRDUP(propname);
158	} else if (p->data)
159		FREE(p->data);	/* release old resources */
160	srcptr = pop_a_string(env, &datalen);
161	p->data = MALLOC(datalen+1);
162	p->size = datalen;
163	memcpy(p->data, srcptr, datalen);
164	p->data[datalen] = 0;
165	if (srcptr)
166		free_property_buffer(env, srcptr);
167}
168
169prop_t *
170lookup_package_property(fcode_env_t *env, char *propname, device_t *d)
171{
172	prop_t *p;
173
174	p = find_property(d, propname);
175	if (p) {
176		return (p);
177	}
178	if (d->vectors.get_package_prop) {
179		static prop_t sp;
180		fstack_t fail, n;
181
182		/* recreate the FORTH environment for the remote call */
183		push_a_string(env, propname);
184		REVERT_PHANDLE(env, n, d);
185		PUSH(DS, n);
186		d->vectors.get_package_prop(env);
187		fail = POP(DS);
188		if (fail)
189			return (NULL);
190		sp.size = POP(DS);
191		sp.data = (uchar_t *)POP(DS);
192		sp.name = propname;
193		sp.next = NULL;
194		return (&sp);
195	}
196	return (NULL);
197}
198
199void
200get_package_property(fcode_env_t *env)
201{
202	prop_t *p;
203	device_t *d;
204	char *propname;
205
206	CHECK_DEPTH(env, 3, "get-package-property");
207	CONVERT_PHANDLE(env, d, POP(DS));
208	propname = pop_a_string(env, NULL);
209	p = lookup_package_property(env, propname, d);
210	if (p) {
211		PUSH(DS, (fstack_t)p->data);
212		PUSH(DS, p->size);
213		PUSH(DS, FALSE);
214	} else
215		PUSH(DS, TRUE);
216}
217
218void
219get_inherited_prop(fcode_env_t *env)
220{
221	instance_t *ih;
222	device_t *dev;
223	prop_t *prop;
224	char *pname;
225	int plen;
226
227	/*
228	 * First, we look thru the in-memory device tree for the property.
229	 * If we don't find it, we call get_inherited_prop, which "knows" it's
230	 * not going to find the property below the attachment point.
231	 */
232
233	CHECK_DEPTH(env, 2, "get-inherited-property");
234	pname = pop_a_string(env, &plen);
235	ih = MYSELF;
236	if (ih) {
237		for (; ih; ih = ih->parent) {
238			dev = ih->device;
239			prop = find_property(dev, pname);
240			if (prop) {
241				PUSH(DS, (fstack_t)prop->data);
242				PUSH(DS, (fstack_t)prop->size);
243				PUSH(DS, FALSE);
244				return;
245			}
246		}
247		if (dev->vectors.get_inherited_prop) {
248			push_a_string(env, pname);
249			dev->vectors.get_inherited_prop(env);
250			return;
251		}
252	}
253	PUSH(DS, TRUE);
254}
255
256void
257delete_property(fcode_env_t *env)
258{
259	CHECK_DEPTH(env, 2, "delete-property");
260	if (MYSELF) {
261		prop_t *p;
262
263		p = stack_find_property(env, MYSELF->device);
264		if (p) {
265			/*
266			 * write the name as NULL; the space will be free'd
267			 * the next time a property lookup passes this node
268			 */
269			p->name = NULL;
270		}
271	} else {
272		two_drop(env);
273	}
274}
275
276void
277get_my_property(fcode_env_t *env)
278{
279	CHECK_DEPTH(env, 2, "get-my-property");
280	PUSH(DS, (fstack_t)MYSELF);
281	ihandle_to_phandle(env);
282	get_package_property(env);
283}
284
285void
286encode_string(fcode_env_t *env)
287{
288	char *str;
289	char *prop;
290	int len;
291
292	CHECK_DEPTH(env, 2, "encode-string");
293	str = pop_a_string(env, &len);
294
295	prop = add_property_buffer(env, len);
296	memcpy(prop, str, len);
297	prop[len] = 0;
298	PUSH(DS, (fstack_t)prop);
299	PUSH(DS, len + 1);
300}
301
302void
303encode_int(fcode_env_t *env)
304{
305	uchar_t *ptr;
306	uint32_t p;
307
308	CHECK_DEPTH(env, 1, "encode-int");
309	p = POP(DS);
310	ptr = add_property_buffer(env, sizeof (uint32_t));
311
312	memcpy(ptr, (char *)&p, sizeof (uint32_t));
313	PUSH(DS, (fstack_t)ptr);
314	PUSH(DS, sizeof (uint32_t));
315}
316
317void
318encode_phys(fcode_env_t *env)
319{
320	uint_t ncells;
321
322	ncells = get_number_of_parent_address_cells(env);
323	CHECK_DEPTH(env, ncells, "encode-phys");
324	encode_int(env);
325	while (--ncells) {
326		rot(env);
327		encode_int(env);
328		encode_plus(env);
329	}
330}
331
332static fstack_t
333get_decoded_int(uchar_t *dp)
334{
335	uint32_t d;
336
337	memcpy((char *)&d, dp, sizeof (uint32_t));
338	return (d);
339}
340
341int
342get_default_intprop(fcode_env_t *env, char *name, device_t *d, int def)
343{
344	prop_t *p;
345
346	if (!d)		/* Kludge for testing */
347		return (def);
348	p = lookup_package_property(env, name, d);
349	if (p == NULL)
350		return (def);
351	return (get_decoded_int(p->data));
352}
353
354int
355get_num_addr_cells(fcode_env_t *env, device_t *d)
356{
357	return (get_default_intprop(env, "#address-cells", d, 2));
358}
359
360int
361get_num_size_cells(fcode_env_t *env, device_t *d)
362{
363	return (get_default_intprop(env, "#size-cells", d, 1));
364}
365
366void
367decode_phys(fcode_env_t *env)
368{
369	char *ptr;
370	int len;
371	int adr_cells;
372	int offset;
373
374	CHECK_DEPTH(env, 2, "decode-phys");
375	ptr = pop_a_string(env, &len);
376
377	adr_cells = get_num_addr_cells(env, env->current_device->parent);
378
379	offset = sizeof (uint32_t) * adr_cells;
380
381	PUSH(DS, (fstack_t)(ptr + offset));
382	PUSH(DS, len + offset);
383
384	while (adr_cells--) {
385		fstack_t d;
386		offset -= sizeof (uint32_t);
387		d = get_decoded_int((uchar_t *)(ptr + offset));
388		PUSH(DS, d);
389	}
390}
391
392/*
393 * 'reg' Fcode 0x116
394 */
395void
396reg_prop(fcode_env_t *env)
397{
398	fstack_t size;
399
400	CHECK_DEPTH(env, 1, "reg");
401	size = POP(DS);
402	encode_phys(env);
403	PUSH(DS, size);
404	encode_int(env);
405	encode_plus(env);
406	create_prop(env, "reg");
407}
408
409void
410encode_bytes(fcode_env_t *env)
411{
412	char *str;
413	char *prop;
414	int len;
415
416	CHECK_DEPTH(env, 2, "encode-bytes");
417	str = pop_a_string(env, &len);
418	prop = add_property_buffer(env, len);
419	memcpy(prop, str, len);
420	prop[len] = 0;
421	PUSH(DS, (fstack_t)prop);
422	PUSH(DS, len);
423}
424
425void
426decode_int(fcode_env_t *env)
427{
428	char *dp;
429	fstack_t d;
430	int len;
431
432	CHECK_DEPTH(env, 2, "decode-int");
433	dp = pop_a_string(env, &len);
434	PUSH(DS, (fstack_t)(dp + sizeof (uint32_t)));
435	PUSH(DS, len - sizeof (uint32_t));
436	d = get_decoded_int((uchar_t *)dp);
437	PUSH(DS, d);
438}
439
440void
441decode_string(fcode_env_t *env)
442{
443	int plen, len;
444	char *dp;
445
446	CHECK_DEPTH(env, 2, "decode-string");
447	dp = pop_a_string(env, &plen);
448	len = strlen(dp) + 1;
449	PUSH(DS, (fstack_t)(dp + len));
450	PUSH(DS, plen - len);
451	PUSH(DS, (fstack_t)dp);
452	PUSH(DS, len - 1);
453}
454
455void
456encode_plus(fcode_env_t *env)
457{
458	int len1, len2;
459	char *src1, *src2;
460	uchar_t *new;
461
462	CHECK_DEPTH(env, 4, "encode+");
463	src1 = pop_a_string(env, &len1);
464	src2 = pop_a_string(env, &len2);
465	new = add_property_buffer(env, len1 + len2);
466	if (src2) {
467		memcpy(new, src2, len2);
468		free_property_buffer(env, src2);
469	}
470	if (src1) {
471		memcpy(new + len2, src1, len1);
472		free_property_buffer(env, src1);
473	}
474	PUSH(DS, (fstack_t)new);
475	PUSH(DS, len1 + len2);
476}
477
478static void
479make_special_property(fcode_env_t *env, char *name)
480{
481	push_a_string(env, name);
482	property(env);
483}
484
485void
486device_name(fcode_env_t *env)
487{
488	CHECK_DEPTH(env, 2, "device-name");
489	encode_string(env);
490	make_special_property(env, "name");
491}
492
493void
494model_prop(fcode_env_t *env)
495{
496	CHECK_DEPTH(env, 2, "model");
497	encode_string(env);
498	make_special_property(env, "model");
499}
500
501void
502device_type(fcode_env_t *env)
503{
504	CHECK_DEPTH(env, 2, "device-type");
505	encode_string(env);
506	make_special_property(env, "device_type");
507}
508
509/*
510 * 'next-property' Fcode implementation.
511 */
512void
513next_property(fcode_env_t *env)
514{
515	device_t *phandle;
516	char *previous;
517	prop_t *p;
518
519	CHECK_DEPTH(env, 3, "next-property");
520	phandle = (device_t *)POP(DS);
521	previous = pop_a_string(env, NULL);
522	p = phandle->properties;
523	if (previous == NULL)
524		p = phandle->properties;
525	else if (p = find_property(phandle, previous))
526		p = p->next;
527
528	for (; p != NULL && p->name == NULL; p = p->next)
529		;
530
531	if (p)
532		push_a_string(env, p->name);
533	else
534		push_a_string(env, "");
535	PUSH(DS, TRUE);
536}
537
538void
539get_property(fcode_env_t *env)
540{
541	if (MYSELF)
542		get_my_property(env);
543	else if (env->current_device) {
544		fstack_t d;
545
546		REVERT_PHANDLE(env, d, env->current_device);
547		PUSH(DS, d);
548		get_package_property(env);
549	} else {
550		two_drop(env);
551		log_message(MSG_WARN, "No device context\n");
552	}
553}
554
555#ifdef DEBUG
556
557static void
558print_indented(char *name)
559{
560	log_message(MSG_INFO, "%-28s", name);
561}
562
563static void
564print_string(fcode_env_t *env, uchar_t *data, int len)
565{
566	while (len > 0) {
567		int nlen = (strlen((char *)data)+1);
568		log_message(MSG_INFO, "%s\n", data);
569		len -= nlen;
570		data += nlen;
571		if (len > 0)
572			print_indented("");
573	}
574}
575
576static void
577print_ints(uchar_t *data, int len, int crlf)
578{
579	uint32_t d;
580
581	while (len--) {
582		d = get_decoded_int(data);
583		log_message(MSG_INFO, "%8.8lx ", d);
584		data += sizeof (uint32_t);
585	}
586	if (crlf)
587		log_message(MSG_INFO, "\n");
588}
589
590static void
591print_integer(fcode_env_t *env, uchar_t *data, int len)
592{
593	print_ints(data, len/sizeof (uint32_t), 1);
594}
595
596static void
597print_bytes(fcode_env_t *env, uchar_t *data, int len)
598{
599	while (len--) {
600		log_message(MSG_INFO, "%2.2x ", *data++);
601	}
602	log_message(MSG_INFO, "\n");
603}
604
605static void
606print_bytes_indented(fcode_env_t *env, uchar_t *data, int len)
607{
608	int nbytes;
609
610	for (; ; ) {
611		nbytes = min(len, 16);
612		print_bytes(env, data, nbytes);
613		len -= nbytes;
614		data += nbytes;
615		if (len == 0)
616			break;
617		print_indented("");
618	}
619}
620
621static void
622print_reg(fcode_env_t *env, uchar_t *data, int len)
623{
624	int pcells, nlen;
625
626	if (env->current_device != NULL &&
627	    env->current_device->parent != NULL) {
628		pcells = get_num_size_cells(env, env->current_device->parent);
629		pcells +=  get_num_addr_cells(env, env->current_device->parent);
630		nlen = pcells*sizeof (uint32_t);
631		while (len > 0) {
632			print_ints(data, pcells, 1);
633			len -= nlen;
634			data += nlen;
635			if (len > 0)
636				print_indented("");
637		}
638	} else
639		print_bytes_indented(env, data, len);
640}
641
642static void
643print_imap(fcode_env_t *env, uchar_t *dp, int len)
644{
645	int n, icells;
646
647	if (env->current_device == NULL) {
648		print_bytes_indented(env, dp, len);
649		return;
650	}
651	n = get_num_addr_cells(env, env->current_device);
652
653	while (len) {
654		int offset;
655		fstack_t data;
656		device_t *node;
657
658		offset = 0;
659		data = get_decoded_int(dp+((n+1)*sizeof (uint32_t)));
660		CONVERT_PHANDLE(env, node, data);
661		offset += (n+2)*sizeof (uint32_t);
662		print_ints(dp, (n+2), 0);
663		icells = get_default_intprop(env, "#interrupt-cells", node, 1);
664		print_ints(dp+offset, icells, 1);
665		offset += icells*sizeof (uint32_t);
666		dp += offset;
667		len -= offset;
668		if (len)
669			print_indented("");
670	}
671}
672
673static void
674print_ranges(fcode_env_t *env, uchar_t *data, int len)
675{
676	int pcells, nlen;
677
678	if (env->current_device != NULL &&
679	    env->current_device->parent != NULL) {
680		pcells = get_num_addr_cells(env, env->current_device);
681		pcells += get_num_addr_cells(env, env->current_device->parent);
682		pcells += get_num_size_cells(env, env->current_device);
683		nlen = pcells*sizeof (uint32_t);
684		while (len > 0) {
685			print_ints(data, pcells, 1);
686			len -= nlen;
687			data += nlen;
688			if (len > 0)
689				print_indented("");
690		}
691	} else
692		print_bytes_indented(env, data, len);
693}
694
695typedef struct MAGIC_PROP {
696	char *name;
697	void (*fn)(fcode_env_t *env, uchar_t *data, int len);
698} magic_prop_t;
699
700static magic_prop_t magic_props[] = {
701	{ "name",		print_string },
702	{ "device_type",	print_string },
703	{ "model",		print_string },
704	{ "reg",		print_reg },
705	{ "assigned-addresses",	print_reg },
706	{ "interrupt-map",	print_imap },
707	{ "#interrupt-cells",	print_integer },
708	{ "interrupt-map-mask",	print_integer },
709	{ "#size-cells",	print_integer },
710	{ "#address-cells",	print_integer },
711	{ "ranges",		print_ranges },
712	{ "device-id",		print_integer },
713	{ "vendor-id",		print_integer },
714	{ "class-code",		print_integer },
715	{ "compatible",		print_string },
716	{ "version",		print_string },
717	{ "manufacturer",	print_string },
718	{ NULL, NULL }
719};
720
721static void
722print_content(fcode_env_t *env, char *prop, uchar_t *data, int len)
723{
724	magic_prop_t *p;
725
726	for (p = magic_props; p->name; p++)
727		if (strcmp(prop, p->name) == 0) {
728			(*p->fn)(env, data, len);
729			return;
730		}
731	print_bytes_indented(env, data, len);
732}
733
734void
735print_property(fcode_env_t *env, prop_t *p, char *prepend)
736{
737	char buf[40];
738	char *name = (p->name ? p->name : "<noname>");
739
740	if (prepend) {
741		sprintf(buf, "%s %s", prepend, name);
742		name = buf;
743	}
744	print_indented(name);
745	if (p->name)
746		print_content(env, p->name, p->data, p->size);
747	else
748		print_bytes_indented(env, p->data, p->size);
749}
750
751void
752dot_properties(fcode_env_t *env)
753{
754	prop_t *p;
755	instance_t *omyself;
756
757	omyself = MYSELF;
758	MYSELF = NULL;
759
760	if (env->current_device) {
761		for (p = env->current_device->properties; p; p = p->next)
762			print_property(env, p, NULL);
763	} else {
764		log_message(MSG_INFO, "No device context\n");
765	}
766	MYSELF = omyself;
767}
768
769#endif
770
771#pragma init(_init)
772
773static void
774_init(void)
775{
776	fcode_env_t *env = initial_env;
777
778	ASSERT(env);
779	NOTICE;
780
781	P1275(0x110, 0,		"property",		property);
782	P1275(0x111, 0,		"encode-int",		encode_int);
783	P1275(0x112, 0,		"encode+",		encode_plus);
784	P1275(0x113, 0,		"encode-phys",		encode_phys);
785	P1275(0x114, 0,		"encode-string",	encode_string);
786	P1275(0x115, 0,		"encode-bytes",		encode_bytes);
787	P1275(0x116, 0,		"reg",			reg_prop);
788	FCODE(0x117, 0,		"intr",			fc_obsolete);
789	FCODE(0x118, 0,		"driver",		fc_historical);
790	P1275(0x119, 0,		"model",		model_prop);
791	P1275(0x11a, 0,		"device-type",		device_type);
792
793	P1275(0x128, 0,		"decode-phys",		decode_phys);
794
795	P1275(0x201, 0,		"device-name",		device_name);
796
797	P1275(0x21a, 0,		"get-my-property",	get_my_property);
798	P1275(0x21b, 0,		"decode-int",		decode_int);
799	P1275(0x21c, 0,		"decode-string",	decode_string);
800	P1275(0x21d, 0,		"get-inherited-property", get_inherited_prop);
801	P1275(0x21e, 0,		"delete-property",	delete_property);
802	P1275(0x21f, 0,		"get-package-property",	get_package_property);
803
804	P1275(0x23d, 0,		"next-property",	next_property);
805
806	FORTH(0,		"get-property",		get_property);
807	FORTH(0,		".properties",		dot_properties);
808}
809