e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye/*
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye * CDDL HEADER START
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye *
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye * The contents of this file are subject to the terms of the
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye * Common Development and Distribution License (the "License").
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye * You may not use this file except in compliance with the License.
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye *
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye * or http://www.opensolaris.org/os/licensing.
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye * See the License for the specific language governing permissions
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye * and limitations under the License.
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye *
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye * When distributing Covered Code, include this CDDL HEADER in each
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye * If applicable, add the following below this CDDL HEADER, with the
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye * fields enclosed by brackets "[]" replaced with your own identifying
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye * information: Portions Copyright [yyyy] [name of copyright owner]
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye *
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye * CDDL HEADER END
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye */
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye/*
349b53dd4e695e3d833b5380540385145b2d3ae8Stuart Maybee * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye * Use is subject to license terms.
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye */
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye/*
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye * "Polled" MCA events in an i86xpv dom0. A timeout runs in the hypervisor
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye * and checks MCA state. If it observes valid MCA state in a bank and if
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye * it sees that dom0 has registered a handler for the VIRQ_MCA then it
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye * raises that VIRQ to dom0. The interrupt handler performs a
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye * hypercall to retrieve the polled telemetry and then pushes that telemetry
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye * into the MSR interpose hash and calls the generic logout code which
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye * will then find the provided interposed MSR values when it performs
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye * cmi_hdl_rdmsr so logout code works unchanged for native or i86xpv dom0.
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye */
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye#include <sys/types.h>
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye#include <sys/conf.h>
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye#include <sys/x86_archext.h>
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye#include <sys/mca_x86.h>
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye#include <sys/ddi.h>
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye#include <sys/spl.h>
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye#include <sys/sunddi.h>
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye#include <sys/evtchn_impl.h>
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye#include <sys/hypervisor.h>
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye#include "../../i86pc/cpu/generic_cpu/gcpu.h"
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Yeextern int *gcpu_xpv_telem_read(mc_info_t *, int, uint64_t *);
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Yeextern void gcpu_xpv_telem_ack(int, uint64_t);
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Yeextern void gcpu_xpv_mci_process(mc_info_t *, int, cmi_mca_regs_t *, size_t);
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Yeint gcpu_xpv_mch_poll_interval_secs = 10;
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Yeint gcpu_xpv_virq_level = 3;
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Yestatic timeout_id_t gcpu_xpv_mch_poll_timeoutid;
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Yestatic int gcpu_xpv_virq_vect = -1;
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Yestatic mc_info_t gcpu_xpv_polldata;
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Yestatic kmutex_t gcpu_xpv_polldata_lock;
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Yestatic cmi_mca_regs_t *gcpu_xpv_poll_bankregs;
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Yestatic size_t gcpu_xpv_poll_bankregs_sz;
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Yestatic uint32_t gcpu_xpv_intr_unclaimed;
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Yestatic uint32_t gcpu_xpv_mca_hcall_busy;
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Yestatic gcpu_poll_trace_ctl_t gcpu_xpv_poll_trace_ctl;
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye#define GCPU_XPV_ARCH_NREGS 3
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye#define GCPU_XPV_MCH_POLL_REARM ((void *)1)
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye#define GCPU_XPV_MCH_POLL_NO_REARM NULL
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Yestatic uint_t
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Yegcpu_xpv_virq_intr(void)
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye{
349b53dd4e695e3d833b5380540385145b2d3ae8Stuart Maybee int types[] = { XEN_MC_URGENT, XEN_MC_NONURGENT };
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye uint64_t fetch_id;
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye int count = 0;
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye int i;
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye if (gcpu_xpv_virq_vect == -1 || gcpu_xpv_poll_bankregs_sz == 0) {
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye gcpu_xpv_intr_unclaimed++;
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye return (DDI_INTR_UNCLAIMED);
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye }
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye if (!mutex_tryenter(&gcpu_xpv_polldata_lock)) {
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye gcpu_xpv_mca_hcall_busy++;
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye return (DDI_INTR_CLAIMED);
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye }
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye for (i = 0; i < sizeof (types) / sizeof (types[0]); i++) {
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye while (gcpu_xpv_telem_read(&gcpu_xpv_polldata, types[i],
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye &fetch_id)) {
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye gcpu_poll_trace(&gcpu_xpv_poll_trace_ctl,
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye GCPU_MPT_WHAT_XPV_VIRQ,
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye x86_mcinfo_nentries(&gcpu_xpv_polldata));
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye gcpu_xpv_mci_process(&gcpu_xpv_polldata, types[i],
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye gcpu_xpv_poll_bankregs, gcpu_xpv_poll_bankregs_sz);
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye gcpu_xpv_telem_ack(types[i], fetch_id);
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye count++;
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye }
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye }
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye mutex_exit(&gcpu_xpv_polldata_lock);
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye return (DDI_INTR_CLAIMED);
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye}
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Yestatic void
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Yegcpu_xpv_mch_poll(void *arg)
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye{
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye cmi_hdl_t hdl = cmi_hdl_any();
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye if (hdl != NULL) {
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye cmi_mc_logout(hdl, 0, 0);
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye cmi_hdl_rele(hdl);
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye }
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye if (arg == GCPU_XPV_MCH_POLL_REARM &&
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye gcpu_xpv_mch_poll_interval_secs != 0) {
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye gcpu_xpv_mch_poll_timeoutid = timeout(gcpu_xpv_mch_poll,
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye GCPU_XPV_MCH_POLL_REARM,
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye drv_usectohz(gcpu_xpv_mch_poll_interval_secs * MICROSEC));
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye }
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye}
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye/*
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye * gcpu_mca_poll_init is called from gcpu_mca_init for each cpu handle
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye * that we initialize for. It should prepare for polling by allocating
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye * control structures and the like, but must not kick polling off yet.
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye *
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye * Since we initialize all cpus in a serialized loop there is no race
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye * on allocating the bankregs structure, nor in free'ing and enlarging
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye * it if we find the number of MCA banks is not uniform in the system
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye * (unlikely) since polling is only started post mp startup.
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye */
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Yevoid
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Yegcpu_mca_poll_init(cmi_hdl_t hdl)
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye{
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye gcpu_data_t *gcpu = cmi_hdl_getcmidata(hdl);
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye int nbanks = gcpu->gcpu_mca.gcpu_mca_nbanks;
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye size_t sz = nbanks * GCPU_XPV_ARCH_NREGS * sizeof (cmi_mca_regs_t);
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye ASSERT(cmi_hdl_class(hdl) == CMI_HDL_SOLARIS_xVM_MCA);
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye if (gcpu_xpv_poll_bankregs == NULL || sz > gcpu_xpv_poll_bankregs_sz) {
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye if (gcpu_xpv_poll_bankregs != NULL) {
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye kmem_free(gcpu_xpv_poll_bankregs,
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye gcpu_xpv_poll_bankregs_sz);
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye } else {
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye gcpu_poll_trace_init(&gcpu_xpv_poll_trace_ctl);
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye }
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye gcpu_xpv_poll_bankregs_sz = sz;
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye gcpu_xpv_poll_bankregs = kmem_zalloc(sz, KM_SLEEP);
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye }
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye}
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu/* deconfigure gcpu_mca_poll_init() */
a31148363f598def767ac48c5d82e1572e44b935Gerry Liuvoid
a31148363f598def767ac48c5d82e1572e44b935Gerry Liugcpu_mca_poll_fini(cmi_hdl_t hdl)
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu{
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu}
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Yevoid
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Yegcpu_mca_poll_start(cmi_hdl_t hdl)
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye{
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye ASSERT(cmi_hdl_class(hdl) == CMI_HDL_SOLARIS_xVM_MCA);
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye /*
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye * We are on the boot cpu (cpu 0), called at the end of its
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye * multiprocessor startup.
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye */
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye if (gcpu_xpv_poll_bankregs_sz != 0 && gcpu_xpv_virq_vect == -1) {
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye /*
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye * The hypervisor will poll MCA state for us, but it cannot
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye * poll MCH state so we do that via a timeout.
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye */
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye if (gcpu_xpv_mch_poll_interval_secs != 0) {
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye gcpu_xpv_mch_poll_timeoutid =
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye timeout(gcpu_xpv_mch_poll, GCPU_XPV_MCH_POLL_REARM,
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye drv_usectohz(gcpu_xpv_mch_poll_interval_secs *
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye MICROSEC));
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye }
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye /*
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye * Register handler for VIRQ_MCA; once this is in place
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye * the hypervisor will begin to forward polled MCA observations
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye * to us.
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye */
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye gcpu_xpv_virq_vect = ec_bind_virq_to_irq(VIRQ_MCA, 0);
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye (void) add_avintr(NULL, gcpu_xpv_virq_level,
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye (avfunc)gcpu_xpv_virq_intr, "MCA", gcpu_xpv_virq_vect,
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye NULL, NULL, NULL, NULL);
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye }
e4b86885570d77af552e9cf94f142f4d744fb8c8Cheng Sean Ye}