/*	$NetBSD: aml_evalobj.c,v 1.3 2021/12/10 11:09:52 msaitoh Exp $	*/

/*-
 * Copyright (c) 1999 Takanori Watanabe
 * Copyright (c) 1999, 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	Id: aml_evalobj.c,v 1.27 2000/08/16 18:14:53 iwasaki Exp 
 *	$FreeBSD: src/usr.sbin/acpi/amldb/aml/aml_evalobj.c,v 1.4 2000/11/09 06:24:45 iwasaki Exp $
 */
#include <sys/cdefs.h>
__RCSID("$NetBSD: aml_evalobj.c,v 1.3 2021/12/10 11:09:52 msaitoh Exp $");

#include <sys/param.h>

#include <acpi_common.h>
#include <aml/aml_amlmem.h>
#include <aml/aml_common.h>
#include <aml/aml_env.h>
#include <aml/aml_evalobj.h>
#include <aml/aml_name.h>
#include <aml/aml_obj.h>
#include <aml/aml_parse.h>
#include <aml/aml_region.h>
#include <aml/aml_status.h>
#include <aml/aml_store.h>

#ifndef _KERNEL
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/mman.h>

#include <assert.h>
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "debug.h"
#else /* _KERNEL */
#include <sys/systm.h>
#endif /* !_KERNEL */

static union aml_object	*aml_eval_fieldobject(struct aml_environ *env,
					      struct aml_name *name);

static union aml_object *
aml_eval_fieldobject(struct aml_environ *env, struct aml_name *name)
{
	int	num;
	struct	aml_name *oname,*wname;
	struct	aml_field *field;
	struct	aml_opregion *or;
	union	aml_object tobj;

	num = 0;
	/* CANNOT OCCUR! */
	if (name == NULL || name->property == NULL ||
	    name->property->type != aml_t_field) {
		printf("????\n");
		env->stat = aml_stat_panic;
		return (NULL);
	}
	field = &name->property->field;
	oname = env->curname;
	if (field->bitlen > 32) {
		env->tempobject.type = aml_t_regfield;
	} else {
		env->tempobject.type = aml_t_num;
	}
	env->curname = name;
	if (field->f.ftype == f_t_field) {
		wname = aml_search_name(env, field->f.fld.regname);
		if (wname == NULL || wname->property == NULL ||
		    wname->property->type != aml_t_opregion) {
			AML_DEBUGPRINT("Inappropreate Type\n");
			env->stat = aml_stat_panic;
			env->curname = oname;
			return (NULL);
		}
		or = &wname->property->opregion;
		if (env->tempobject.type == aml_t_regfield) {
			env->tempobject.regfield.space = or->space;
			env->tempobject.regfield.flags = field->flags;
			env->tempobject.regfield.offset = or->offset;
			env->tempobject.regfield.bitoffset = field->bitoffset;
			env->tempobject.regfield.bitlen = field->bitlen;
		} else {
			env->tempobject.type = aml_t_num;
			env->tempobject.num.number = aml_region_read(env,
			    or->space, field->flags, or->offset,
			    field->bitoffset, field->bitlen);
			AML_DEBUGPRINT("[read(%d, 0x%x)->0x%x]",
			    or->space, or->offset + field->bitoffset / 8,
			    env->tempobject.num.number);
		}
	} else if (field->f.ftype == f_t_index) {
		wname = aml_search_name(env, field->f.ifld.indexname);
		tobj.type = aml_t_num;
		tobj.num.number = field->bitoffset / 8;/* AccessType Boundary */
		aml_store_to_name(env, &tobj, wname);
		wname = aml_search_name(env, field->f.ifld.dataname);
		num = aml_objtonum(env, aml_eval_name(env, wname));
		env->tempobject.type = aml_t_num;
		env->tempobject.num.number = (num >> (field->bitoffset & 7)) &
		    ((1 << field->bitlen) - 1);
	}
	env->curname = oname;
	return (&env->tempobject);
}

union aml_object *
aml_eval_objref(struct aml_environ *env, union aml_object *obj)
{
	int	offset;
	union	aml_object num1;
	union	aml_object *ref, *ret;

	ret = obj;
	if (obj->objref.deref == 1) {
		num1.type = aml_t_num;
		offset = obj->objref.offset;
		ref = obj->objref.ref;
		if (ref == NULL) {
			goto out;
		}
		switch (ref->type) {
		case aml_t_package:
			if (ref->package.elements > offset) {
				ret = ref->package.objects[offset];
			} else {
				num1.num.number = 0;
				env->tempobject = num1;
				ret = &env->tempobject;
			}
			break;
		case aml_t_buffer:
			if (ref->buffer.size > offset) {
				num1.num.number = ref->buffer.data[offset] & 0xff;
			} else {
				num1.num.number = 0;
			}
			env->tempobject = num1;
			ret = &env->tempobject;
			break;
		default:
			break;
		}
	}
	if (obj->objref.alias == 1) {
		ret = aml_eval_name(env, obj->objref.nameref);
		goto out;
	}
out:
	return (ret);
}

/*
 * Eval named object.
 */
union aml_object *
aml_eval_name(struct aml_environ *env, struct aml_name *aname)
{
	int	argnum, i;
	int	num;
	struct	aml_name *tmp;
	struct	aml_environ *copy;
	struct	aml_local_stack *stack;
	union	aml_object *obj, *ret;
	union	aml_object *src;

	ret = NULL;
	if (aname == NULL || aname->property == NULL) {
		return (NULL);
	}
	if (env->stat == aml_stat_panic) {
		return (NULL);
	}
	copy = memman_alloc(aml_memman, memid_aml_environ);
	if (copy == NULL) {
		return (NULL);
	}
	ret = aname->property;
	i = 0;
reevaluate:
	if (i > 10) {
		env->stat = aml_stat_panic;
		printf("TOO MANY LOOP\n");
		ret = NULL;
		goto out;
	}
	switch (aname->property->type) {
	case aml_t_namestr:
		tmp = aname;
		aname = aml_search_name(env, aname->property->nstr.dp);
		if (aname == NULL) {
			aname = tmp;
		}
		i++;
		goto reevaluate;
	case aml_t_objref:
		ret = aml_eval_objref(env, aname->property);
		goto out;
	case aml_t_num:
	case aml_t_string:
	case aml_t_buffer:
	case aml_t_package:
	case aml_t_debug:
		ret = aname->property;
		goto out;
	case aml_t_field:
		aml_free_objectcontent(&env->tempobject);
		ret = aml_eval_fieldobject(env, aname);
		goto out;
	case aml_t_method:
		aml_free_objectcontent(&env->tempobject);
		argnum = aname->property->meth.argnum & 7;
		*copy = *env;
		copy->curname = aname;
		copy->dp = aname->property->meth.from;
		copy->end = aname->property->meth.to;
		copy->stat = aml_stat_none;
		stack = aml_local_stack_create();
		AML_DEBUGPRINT("(");
		for (i = 0; i < argnum; i++) {
			aml_local_stack_getArgX(stack, i)->property =
			    aml_copy_object(env,
				aml_eval_name(env,
				    aml_parse_termobj(env, 0)));
			if (i < argnum - 1)
				AML_DEBUGPRINT(", ");
		}
		AML_DEBUGPRINT(")\n");
		aml_local_stack_push(stack);
		if (env->stat == aml_stat_step) {
			AML_DEBUGGER(env, copy);
		}
		tmp = aml_execute_method(copy);
		obj = aml_eval_name(env, tmp);
		if (copy->stat == aml_stat_panic) {
			AML_DEBUGPRINT("PANIC OCCURRED IN METHOD");
			env->stat = aml_stat_panic;
			ret = NULL;
			aml_local_stack_delete(aml_local_stack_pop());
			goto out;
		}
		if (aml_debug) {
			aml_showobject(obj);
		}

		if (tmp)
			tmp->property = NULL;
		aml_local_stack_delete(aml_local_stack_pop());
		if (obj) {
			aml_create_local_object()->property = obj;
			ret = obj;
		} else {
			env->tempobject.type = aml_t_num;
			env->tempobject.num.number = 0;
		}

		goto out;
	case aml_t_bufferfield:
		aml_free_objectcontent(&env->tempobject);
		if (aname->property->bfld.bitlen > 32) {
			ret = aname->property;
		} else {
			src = aname->property;
			num = aml_bufferfield_read(src->bfld.origin,
			    src->bfld.bitoffset, src->bfld.bitlen);
			env->tempobject.type = aml_t_num;
			env->tempobject.num.number = num;
			ret = &env->tempobject;
		}
		goto out;
	default:
		AML_DEBUGPRINT("I eval the object that I should not eval, %s%d",
		    aname->name, aname->property->type);
		AML_SYSABORT();
		ret = NULL;
		goto out;
	}
out:
	memman_free(aml_memman, memid_aml_environ, copy);
	return (ret);
}

/*
 * Eval named object but env variable is not required and return
 * status of evaluation (success is zero).  This function is assumed
 * to be called by aml_apply_foreach_found_objects().
 * Note that no arguments are passed if object is a method.
 */

int
aml_eval_name_simple(struct aml_name *name, va_list ap)
{
	struct	aml_environ *env;
	union	aml_object *ret;

	if (name == NULL || name->property == NULL) {
		return (1);
	}

	env = memman_alloc(aml_memman, memid_aml_environ);
	if (env == NULL) {
		return (1);
	}
	bzero(env, sizeof(struct aml_environ));

	aml_local_stack_push(aml_local_stack_create());

	AML_DEBUGPRINT("Evaluating ");
	aml_print_curname(name);
	ret = aml_eval_name(env, name);
	if (name->property->type != aml_t_method) {
		AML_DEBUGPRINT("\n");
		if (aml_debug) {
			aml_showobject(ret);
		}
	}

	aml_local_stack_delete(aml_local_stack_pop());

	memman_free(aml_memman, memid_aml_environ, env);
	return (0);
}

int
aml_objtonum(struct aml_environ *env, union aml_object *obj)
{

	if (obj != NULL && obj->type == aml_t_num) {
		return (obj->num.number);
	} else {
		env->stat = aml_stat_panic;
		return (-1);
	}
}

struct aml_name *
aml_execute_method(struct aml_environ *env)
{
	struct	aml_name *name;
	struct	aml_name_group *newgrp;

	newgrp = aml_new_name_group((void *)AML_NAME_GROUP_IN_METHOD);

	AML_DEBUGPRINT("[");
	aml_print_curname(env->curname);
	AML_DEBUGPRINT(" START]\n");

	name = aml_parse_objectlist(env, 0);
	AML_DEBUGPRINT("[");
	aml_print_curname(env->curname);
	AML_DEBUGPRINT(" END]\n");

	aml_delete_name_group(newgrp);
	return (name);
}

union aml_object *
aml_invoke_method(struct aml_name *name, int argc, union aml_object *argv)
{
	int	i;
	struct	aml_name *tmp;
	struct	aml_environ *env;
	struct	aml_local_stack *stack;
	union	aml_object *retval;
	union	aml_object *obj;

	retval = NULL;
	env = memman_alloc(aml_memman, memid_aml_environ);
	if (env == NULL) {
		return (NULL);
	}
	bzero(env, sizeof(struct aml_environ));

	if (name != NULL && name->property != NULL &&
	    name->property->type == aml_t_method) {
		env->curname = name;
		env->dp = name->property->meth.from;
		env->end = name->property->meth.to;
		AML_DEBUGGER(env, env);
		stack = aml_local_stack_create();
		for (i = 0; i < argc; i++) {
			aml_local_stack_getArgX(stack, i)->property =
			    aml_alloc_object(argv[i].type, &argv[i]);
		}
		aml_local_stack_push(stack);
		obj = aml_eval_name(env, tmp = aml_execute_method(env));
		if (aml_debug) {
			aml_showtree(name, 0);
		}

		if (tmp)
			tmp->property = NULL;
		aml_local_stack_delete(aml_local_stack_pop());
		if (obj) {
			aml_create_local_object()->property = obj;
			retval = obj;
		}
	}
	memman_free(aml_memman, memid_aml_environ, env);
	return (retval);
}

union aml_object *
aml_invoke_method_by_name(char *method, int argc, union aml_object *argv)
{
	struct	aml_name *name;

	name = aml_find_from_namespace(aml_get_rootname(), method);
	if (name == NULL) {
		return (NULL);
	}

	return (aml_invoke_method(name, argc, argv));
}
