text: /* Copyright 2013-2014 IBM Corp.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   * 	http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
   * implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  #include <stdint.h>
  #include <stdlib.h>
  #include <stdio.h>
  
  #include <lock.h>
  #include <device.h>
  #include <compiler.h>
  #include <hostservices.h>
  #include <mem_region.h>
  #include <xscom.h>
  #include <fsp.h>
  #include <chip.h>
  #include <console.h>
  #include <mem-map.h>
  #include <timebase.h>
  
  #define HOSTBOOT_RUNTIME_INTERFACE_VERSION 1
  
  struct host_interfaces {
  	/** Interface version. */
  	uint64_t interface_version;
  
  	/** Put a string to the console. */
  	void (*puts)(const char*);
  	/** Critical failure in runtime execution. */
  	void (*assert)(void);
  
  	/** OPTIONAL. Hint to environment that the page may be executed. */
  	int (*set_page_execute)(void*);
  
  	/** malloc */
  	void *(*malloc)(size_t);
  	/** free */
  	void (*free)(void*);
  	/** realloc */
  	void *(*realloc)(void*, size_t);
  
  	/** sendErrorLog
  	 * @param[in] plid Platform Log identifier
  	 * @param[in] data size in bytes
  	 * @param[in] pointer to data
  	 * @return 0 on success else error code
  	 */
  	int (*send_error_log)(uint32_t,uint32_t,void *);
  
  	/** Scan communication read
  	 * @param[in] chip_id (based on devtree defn)
  	 * @param[in] address
  	 * @param[in] pointer to 8-byte data buffer
  	 * @return 0 on success else return code
  	 */
  	int (*scom_read)(uint64_t, uint64_t, void*);
  
  	/** Scan communication write
  	 * @param[in] chip_id (based on devtree defn)
  	 * @param[in] address
  	 * @param[in] pointer to 8-byte data buffer
  	 * @return 0 on success else return code
  	 */
  	int (*scom_write)(uint64_t, uint64_t, const void *);
  
  	/** lid_load
  	 *  Load a LID from PNOR, FSP, etc.
  	 *
  	 *  @param[in] LID number.
  	 *  @param[out] Allocated buffer for LID.
  	 *  @param[out] Size of LID (in bytes).
  	 *
  	 *  @return 0 on success, else RC.
  	 */
  	int (*lid_load)(uint32_t lid, void **buf, size_t *len);
  
  	/** lid_unload
  	 *  Release memory from previously loaded LID.
  	 *
  	 *  @param[in] Allocated buffer for LID to release.
  	 *
  	 *  @return 0 on success, else RC.
  	 */
  	int (*lid_unload)(void *buf);
  
  	/** Get the address of a reserved memory region by its devtree name.
  	 *
  	 *  @param[in] Devtree name (ex. "ibm,hbrt-vpd-image")
  	 *  @return physical address of region (or NULL).
  	 **/
  	uint64_t (*get_reserved_mem)(const char*);
  
  	/**
  	 * @brief  Force a core to be awake, or clear the force
  	 * @param[in] i_core  Core to wake up (pid)
  	 * @param[in] i_mode  0=force awake
  	 *                1=clear force
  	 *                2=clear all previous forces
  	 * @return rc  non-zero on error
  	 */
  	int (*wakeup)( uint32_t i_core, uint32_t i_mode );
  
  	/**
  	 * @brief Delay/sleep for at least the time given
  	 * @param[in] seconds
  	 * @param[in] nano seconds
  	 */
  	void (*nanosleep)(uint64_t i_seconds, uint64_t i_nano_seconds);
  
  	// Reserve some space for future growth.
  	void (*reserved[32])(void);
  };
  
  struct runtime_interfaces {
  	/** Interface version. */
  	uint64_t interface_version;
  
  	/** Execute CxxTests that may be contained in the image.
  	 *
  	 * @param[in] - Pointer to CxxTestStats structure for results reporting.
  	 */
  	void (*cxxtestExecute)(void *);
  	/** Get a list of lids numbers of the lids known to HostBoot
  	 *
  	 * @param[out] o_num - the number of lids in the list
  	 * @return a pointer to the list
  	 */
  	const uint32_t * (*get_lid_list)(size_t * o_num);
  
  	/** Load OCC Image and common data into mainstore, also setup OCC BARSs
  	 *
  	 * @param[in] i_homer_addr_phys - The physical mainstore address of the
  	 *                                start of the HOMER image
  	 * @param[in] i_homer_addr_va - Virtual memory address of the HOMER image
  	 * @param[in] i_common_addr_phys - The physical mainstore address of the
  	 *                                 OCC common area.
  	 * @param[in] i_common_addr_va - Virtual memory address of the common area
  	 * @param[in] i_chip - The HW chip id (XSCOM chip ID)
  	 * @return 0 on success else return code
  	 */
  	int(*loadOCC)(uint64_t i_homer_addr_phys,
  			uint64_t i_homer_addr_va,
  			uint64_t i_common_addr_phys,
  			uint64_t i_common_addr_va,
  			uint64_t i_chip);
  
  	/** Start OCC on all chips, by module
  	 *
  	 *  @param[in] i_chip - Array of functional HW chip ids
  	 *  @Note The caller must include a complete modules worth of chips
  	 *  @param[in] i_num_chips - Number of chips in the array
  	 *  @return 0 on success else return code
  	 */
  	int (*startOCCs)(uint64_t* i_chip,
  			size_t i_num_chips);
  
  	/** Stop OCC hold OCCs in reset
  	 *
  	 *  @param[in] i_chip - Array of functional HW chip ids
  	 *  @Note The caller must include a complete modules worth of chips
  	 *  @param[in] i_num_chips - Number of chips in the array
  	 *  @return 0 on success else return code
  	 */
  	int (*stopOCCs)(uint64_t* i_chip,
  			size_t i_num_chips);
  
  	/* Reserve some space for future growth. */
  	void (*reserved[32])(void);
  };
  
  static struct runtime_interfaces *hservice_runtime;
  
  static char *hbrt_con_buf = (char *)HBRT_CON_START;
  static size_t hbrt_con_pos;
  static bool hbrt_con_wrapped;
  
  #define HBRT_CON_IN_LEN		0
  #define HBRT_CON_OUT_LEN	(HBRT_CON_LEN - HBRT_CON_IN_LEN)
  
  static struct memcons hbrt_memcons __section(".data.memcons") = {
  	.magic		= MEMCONS_MAGIC,
  	.obuf_phys	= HBRT_CON_START,
  	.ibuf_phys	= HBRT_CON_START + HBRT_CON_OUT_LEN,
  	.obuf_size	= HBRT_CON_OUT_LEN,
  	.ibuf_size	= HBRT_CON_IN_LEN,
  };
  
  static void hservice_putc(char c)
  {
  	uint32_t opos;
  
  	hbrt_con_buf[hbrt_con_pos++] = c;
  	if (hbrt_con_pos >= HBRT_CON_OUT_LEN) {
  		hbrt_con_pos = 0;
  		hbrt_con_wrapped = true;
  	}
  
  	/*
  	 * We must always re-generate memcons.out_pos because
  	 * under some circumstances, the console script will
  	 * use a broken putmemproc that does RMW on the full
  	 * 8 bytes containing out_pos and in_prod, thus corrupting
  	 * out_pos
  	 */
  	opos = hbrt_con_pos;
  	if (hbrt_con_wrapped)
  		opos |= MEMCONS_OUT_POS_WRAP;
  	lwsync();
  	hbrt_memcons.out_pos = opos;
  }
  
  static void hservice_puts(const char *str)
  {
  	char c;
  
  	while((c = *(str++)) != 0)
  		hservice_putc(c);
  	hservice_putc(10);
  }
  
  static void hservice_mark(void)
  {
  	hservice_puts("--------------------------------------------------"
  		      "--------------------------------------------------\n");
  }
  
  static void hservice_assert(void)
  {
  	/**
  	 * @fwts-label HBRTassert
  	 * @fwts-advice HBRT triggered assert: you need to debug HBRT
  	 */
  	prlog(PR_EMERG, "HBRT: Assertion from hostservices\n");
  	abort();
  }
  
  static void *hservice_malloc(size_t size)
  {
  	return malloc(size);
  }
  
  static void hservice_free(void *ptr)
  {
  	free(ptr);
  }
  
  
  static void *hservice_realloc(void *ptr, size_t size)
  {
  	return realloc(ptr, size);
  }
  
  struct hbrt_elog_ent {
  	void *buf;
  	unsigned int size;
  	unsigned int plid;
  	struct list_node link;
  };
  static LIST_HEAD(hbrt_elogs);
  static struct lock hbrt_elog_lock = LOCK_UNLOCKED;
  static bool hbrt_elog_sending;
  static void hservice_start_elog_send(void);
  
  static void hservice_elog_write_complete(struct fsp_msg *msg)
  {
  	struct hbrt_elog_ent *ent = msg->user_data;
  
  	lock(&hbrt_elog_lock);
  	prlog(PR_DEBUG, "HBRT: Completed send of PLID 0x%08x\n", ent->plid);
  	hbrt_elog_sending = false;
  	fsp_tce_unmap(PSI_DMA_HBRT_LOG_WRITE_BUF,
  		      PSI_DMA_HBRT_LOG_WRITE_BUF_SZ);
  	free(ent->buf);
  	free(ent);
  	fsp_freemsg(msg);
  	hservice_start_elog_send();
  	unlock(&hbrt_elog_lock);
  }
  
  static void hservice_start_elog_send(void)
  {
  	struct fsp_msg *msg;
  	struct hbrt_elog_ent *ent;
  
   again:
  	if (list_empty(&hbrt_elogs))
  		return;
  	ent = list_pop(&hbrt_elogs, struct hbrt_elog_ent, link);
  
  	hbrt_elog_sending = true;
  
  	prlog(PR_DEBUG, "HBRT: Starting send of PLID 0x%08x\n", ent->plid);
  
  	fsp_tce_map(PSI_DMA_HBRT_LOG_WRITE_BUF, ent->buf,
  		    PSI_DMA_HBRT_LOG_WRITE_BUF_SZ);
  
  	msg = fsp_mkmsg(FSP_CMD_WRITE_SP_DATA, 6, FSP_DATASET_HBRT_BLOB,
  			0, 0, 0, PSI_DMA_HBRT_LOG_WRITE_BUF,
  			ent->size);
  
  	if (!msg) {
  		prerror("HBRT: Failed to create error msg log to FSP\n");
  		goto error;
  	}
  	msg->user_data = ent;
  	if (!fsp_queue_msg(msg, hservice_elog_write_complete))
  		return;
  	prerror("FSP: Error queueing elog update\n");
   error:
  	if (msg)
  		fsp_freemsg(msg);
  	fsp_tce_unmap(PSI_DMA_HBRT_LOG_WRITE_BUF,
  		      PSI_DMA_HBRT_LOG_WRITE_BUF_SZ);
  	free(ent->buf);
  	free(ent);
  	hbrt_elog_sending = false;
  	goto again;
  }
  
  static int hservice_send_error_log(uint32_t plid, uint32_t dsize, void *data)
  {
  	struct hbrt_elog_ent *ent;
  	void *abuf;
  
  	prlog(PR_ERR, "HBRT: Error log generated with plid 0x%08x\n", plid);
  
  	/* We only know how to send error logs to FSP */
  	if (!fsp_present()) {
  		prerror("HBRT: Warning, error log from HBRT discarded !\n");
  		return OPAL_UNSUPPORTED;
  	}
  	if (dsize > PSI_DMA_HBRT_LOG_WRITE_BUF_SZ) {
  		prerror("HBRT: Warning, error log from HBRT too big (%d) !\n",
  			dsize);
  		dsize = PSI_DMA_HBRT_LOG_WRITE_BUF_SZ;
  	}
  
  	lock(&hbrt_elog_lock);
  
  	/* Create and populate a tracking structure */
  	ent = zalloc(sizeof(struct hbrt_elog_ent));
  	if (!ent) {
  		unlock(&hbrt_elog_lock);
  		return OPAL_NO_MEM;
  	}
  
  	/* Grab a 4k aligned page */
  	abuf = memalign(0x1000, PSI_DMA_HBRT_LOG_WRITE_BUF_SZ);
  	if (!abuf) {
  		free(ent);
  		unlock(&hbrt_elog_lock);
  		return OPAL_NO_MEM;
  	}
  	memset(abuf, 0, PSI_DMA_HBRT_LOG_WRITE_BUF_SZ);
  	memcpy(abuf, data, dsize);
  	ent->buf = abuf;
  	ent->size = dsize;
  	ent->plid = plid;
  	list_add_tail(&hbrt_elogs, &ent->link);
  	if (!hbrt_elog_sending)
  		hservice_start_elog_send();
  	unlock(&hbrt_elog_lock);
  
  	return 0;
  }
  
  static int hservice_scom_read(uint64_t chip_id, uint64_t addr, void *buf)
  {
  	return xscom_read(chip_id, addr, buf);
  }
  
  static int hservice_scom_write(uint64_t chip_id, uint64_t addr,
  			       const void *buf)
  {
  	uint64_t val;
  
  	memcpy(&val, buf, sizeof(val));
  	return xscom_write(chip_id, addr, val);
  }
  
  struct hbrt_lid {
  	void *load_addr;
  	size_t len;
  	uint32_t id;
  	struct list_node link;
  };
  static LIST_HEAD(hbrt_lid_list);
  
  static bool hbrt_lid_preload_complete = false;
  
  bool hservices_lid_preload_complete(void)
  {
  	return hbrt_lid_preload_complete;
  }
  
  /* TODO: Few of the following routines can be generalized */
  static int __hservice_lid_load(uint32_t lid, void **buf, size_t *len)
  {
  	int rc;
  
  	/* Adjust LID side first or we get a cache mismatch */
  	lid = fsp_adjust_lid_side(lid);
  
  	/*
  	 * Allocate a new buffer and load the LID into it
  	 * XXX: We currently use the same size for each HBRT lid.
  	 */
  	*buf = malloc(HBRT_LOAD_LID_SIZE);
  	*len = HBRT_LOAD_LID_SIZE;
  	rc = fsp_preload_lid(lid, *buf, len);
  	rc = fsp_wait_lid_loaded(lid);
  	if (rc != 0)
  		/* Take advantage of realloc corner case here. */
  		*len = 0;
  	*buf = realloc(*buf, *len);
  
  	prlog(PR_DEBUG, "HBRT: LID 0x%08x successfully loaded, len=0x%lx\n",
  			lid, (unsigned long)len);
  
  	return rc;
  }
  
  static int __hservice_lid_preload(const uint32_t lid)
  {
  	struct hbrt_lid *hlid;
  	void *buf;
  	size_t len;
  	int rc;
  
  	hlid = zalloc(sizeof(struct hbrt_lid));
  	if (!hlid) {
  		prerror("HBRT: Could not allocate struct hbrt_lid\n");
  		return OPAL_NO_MEM;
  	}
  
  	rc = __hservice_lid_load(lid, &buf, &len);
  	if (rc) {
  		free(hlid);
  		return rc;
  	}
  
  	hlid->load_addr = buf;
  	hlid->len = len;
  	hlid->id = lid;
  	list_add_tail(&hbrt_lid_list, &hlid->link);
  
  	return 0;
  }
  
  /* Find and preload all lids needed by hostservices */
  void hservices_lid_preload(void)
  {
  	const uint32_t *lid_list = NULL;
  	size_t num_lids;
  	int i;
  
  	if (!hservice_runtime)
  		return;
  
  	lid_list = (const uint32_t *)hservice_runtime->get_lid_list(&num_lids);
  	if (!lid_list) {
  		prerror("HBRT: get_lid_list() returned NULL\n");
  		return;
  	}
  
  	prlog(PR_INFO, "HBRT: %d lids to load\n", (int)num_lids);
  
  	/* Currently HBRT needs only one (OCC) lid */
  	for (i = 0; i < num_lids; i++)
  		__hservice_lid_preload(lid_list[i]);
  
  	hbrt_lid_preload_complete = true;
  	occ_poke_load_queue();
  }
  
  static int hservice_lid_load(uint32_t lid, void **buf, size_t *len)
  {
  	struct hbrt_lid *hlid;
  
  	prlog(PR_INFO, "HBRT: Lid load request for 0x%08x\n", lid);
  
  	if (list_empty(&hbrt_lid_list))	{ /* Should not happen */
  		/**
  		 * @fwts-label HBRTlidLoadFail
  		 * @fwts-advice Firmware should have aborted boot
  		 */
  		prlog(PR_CRIT, "HBRT: LID Load failed\n");
  		abort();
  	}
  
  	list_for_each(&hbrt_lid_list, hlid, link) {
  		if (hlid->id == lid) {
  			*buf = hlid->load_addr;
  			*len = hlid->len;
  			prlog(PR_DEBUG, "HBRT: LID Serviced from cache,"
  			      " %x, len=0x%lx\n", hlid->id, hlid->len);
  			return 0;
  		}
  	}
  	return -ENOENT;
  }
  
  static int hservice_lid_unload(void *buf __unused)
  {
  	/* We do nothing as the LID is held in cache */
  	return 0;
  }
  
  static uint64_t hservice_get_reserved_mem(const char *name)
  {
  	struct mem_region *region;
  	uint64_t ret;
  
  	/* We assume it doesn't change after we've unlocked it, but
  	 * lock ensures list is safe to walk. */
  	lock(&mem_region_lock);
  	region = find_mem_region(name);
  	ret = region ? region->start : 0;
  	unlock(&mem_region_lock);
  
  	if (!ret)
  		prlog(PR_WARNING, "HBRT: Mem region '%s' not found !\n", name);
  
  	return ret;
  }
  
  static void hservice_nanosleep(uint64_t i_seconds, uint64_t i_nano_seconds)
  {
  	struct timespec ts;
  
  	ts.tv_sec = i_seconds;
  	ts.tv_nsec = i_nano_seconds;
  	nanosleep_nopoll(&ts, NULL);
  }
  
  static int hservice_set_special_wakeup(struct cpu_thread *cpu)
  {
  	uint64_t val, core_id, poll_target, stamp;
  	int rc;
  
  	/*
  	 * Note: HWP checks for checkstops, but I assume we don't need to
  	 * as we wouldn't be running if one was present
  	 */
  
  	/* Grab core ID once */
  	core_id = pir_to_core_id(cpu->pir);
  
  	/*
  	 * The original HWp reads the XSCOM first but ignores the result
  	 * and error, let's do the same until I know for sure that is
  	 * not necessary
  	 */
  	xscom_read(cpu->chip_id,
  		   XSCOM_ADDR_P8_EX_SLAVE(core_id, EX_PM_SPECIAL_WAKEUP_PHYP),
  		   &val);
  
  	/* Then we write special wakeup */
  	rc = xscom_write(cpu->chip_id,
  			 XSCOM_ADDR_P8_EX_SLAVE(core_id,
  						EX_PM_SPECIAL_WAKEUP_PHYP),
  			 PPC_BIT(0));
  	if (rc) {
  		prerror("HBRT: XSCOM error %d asserting special"
  			" wakeup on 0x%x\n", rc, cpu->pir);
  		return rc;
  	}
  
  	/*
  	 * HWP uses the history for Perf register here, dunno why it uses
  	 * that one instead of the pHyp one, maybe to avoid clobbering it...
  	 *
  	 * In any case, it does that to check for run/nap vs.sleep/winkle/other
  	 * to decide whether to poll on checkstop or not. Since we don't deal
  	 * with checkstop conditions here, we ignore that part.
  	 */
  
  	/*
  	 * Now poll for completion of special wakeup. The HWP is nasty here,
  	 * it will poll at 5ms intervals for up to 200ms. This is not quite
  	 * acceptable for us at runtime, at least not until we have the
  	 * ability to "context switch" HBRT. In practice, because we don't
  	 * winkle, it will never take that long, so we increase the polling
  	 * frequency to 1us per poll. However we do have to keep the same
  	 * timeout.
  	 *
  	 * We don't use time_wait_ms() either for now as we don't want to
  	 * poll the FSP here.
  	 */
  	stamp = mftb();
  	poll_target = stamp + msecs_to_tb(200);
  	val = 0;
  	while (!(val & EX_PM_GP0_SPECIAL_WAKEUP_DONE)) {
  		/* Wait 1 us */
  		hservice_nanosleep(0, 1000);
  
  		/* Read PM state */
  		rc = xscom_read(cpu->chip_id,
  				XSCOM_ADDR_P8_EX_SLAVE(core_id, EX_PM_GP0),
  				&val);
  		if (rc) {
  			prerror("HBRT: XSCOM error %d reading PM state on"
  				" 0x%x\n", rc, cpu->pir);
  			return rc;
  		}
  		/* Check timeout */
  		if (mftb() > poll_target)
  			break;
  	}
  
  	/* Success ? */
  	if (val & EX_PM_GP0_SPECIAL_WAKEUP_DONE) {
  		uint64_t now = mftb();
  		prlog(PR_TRACE, "HBRT: Special wakeup complete after %ld us\n",
  		      tb_to_usecs(now - stamp));
  		return 0;
  	}
  
  	/*
  	 * We timed out ...
  	 *
  	 * HWP has a complex workaround for HW255321 which affects
  	 * Murano DD1 and Venice DD1. Ignore that for now
  	 *
  	 * Instead we just dump some XSCOMs for error logging
  	 */
  	prerror("HBRT: Timeout on special wakeup of 0x%0x\n", cpu->pir);
  	prerror("HBRT:      PM0 = 0x%016llx\n", val);
  	val = -1;
  	xscom_read(cpu->chip_id,
  		   XSCOM_ADDR_P8_EX_SLAVE(core_id, EX_PM_SPECIAL_WAKEUP_PHYP),
  		   &val);
  	prerror("HBRT: SPC_WKUP = 0x%016llx\n", val);
  	val = -1;
  	xscom_read(cpu->chip_id,
  		   XSCOM_ADDR_P8_EX_SLAVE(core_id,
  					  EX_PM_IDLE_STATE_HISTORY_PHYP),
  		   &val);
  	prerror("HBRT:  HISTORY = 0x%016llx\n", val);
  
  	return OPAL_HARDWARE;
  }
  
  static int hservice_clr_special_wakeup(struct cpu_thread *cpu)
  {
  	uint64_t val, core_id;
  	int rc;
  
  	/*
  	 * Note: HWP checks for checkstops, but I assume we don't need to
  	 * as we wouldn't be running if one was present
  	 */
  
  	/* Grab core ID once */
  	core_id = pir_to_core_id(cpu->pir);
  
  	/*
  	 * The original HWp reads the XSCOM first but ignores the result
  	 * and error, let's do the same until I know for sure that is
  	 * not necessary
  	 */
  	xscom_read(cpu->chip_id,
  		   XSCOM_ADDR_P8_EX_SLAVE(core_id, EX_PM_SPECIAL_WAKEUP_PHYP),
  		   &val);
  
  	/* Then we write special wakeup */
  	rc = xscom_write(cpu->chip_id,
  			 XSCOM_ADDR_P8_EX_SLAVE(core_id,
  						EX_PM_SPECIAL_WAKEUP_PHYP), 0);
  	if (rc) {
  		prerror("HBRT: XSCOM error %d deasserting"
  			" special wakeup on 0x%x\n", rc, cpu->pir);
  		return rc;
  	}
  
  	/*
  	 * The original HWp reads the XSCOM again with the comment
  	 * "This puts an inherent delay in the propagation of the reset
  	 * transition"
  	 */
  	xscom_read(cpu->chip_id,
  		   XSCOM_ADDR_P8_EX_SLAVE(core_id, EX_PM_SPECIAL_WAKEUP_PHYP),
  		   &val);
  
  	return 0;
  }
  
  static int hservice_wakeup(uint32_t i_core, uint32_t i_mode)
  {
  	struct cpu_thread *cpu;
  	int rc = OPAL_SUCCESS;
  
  	/*
  	 * Mask out the top nibble of i_core since it may contain
  	 * 0x4 (which we use for XSCOM targeting)
  	 */
  	i_core &= 0x0fffffff;
  
  	/* What do we need to do ? */
  	switch(i_mode) {
  	case 0: /* Assert special wakeup */
  		/* XXX Assume P8 */
  		cpu = find_cpu_by_pir(i_core << 3);
  		if (!cpu)
  			return OPAL_PARAMETER;
  		prlog(PR_DEBUG, "HBRT: Special wakeup assert for core 0x%x,"
  		      " count=%d\n", i_core, cpu->hbrt_spec_wakeup);
  		if (cpu->hbrt_spec_wakeup == 0)
  			rc = hservice_set_special_wakeup(cpu);
  		if (rc == 0)
  			cpu->hbrt_spec_wakeup++;
  		return rc;
  	case 1: /* Deassert special wakeup */
  		/* XXX Assume P8 */
  		cpu = find_cpu_by_pir(i_core << 3);
  		if (!cpu)
  			return OPAL_PARAMETER;
  		prlog(PR_DEBUG, "HBRT: Special wakeup release for core"
  		      " 0x%x, count=%d\n", i_core, cpu->hbrt_spec_wakeup);
  		if (cpu->hbrt_spec_wakeup == 0) {
  			prerror("HBRT: Special wakeup clear"
  				" on core 0x%x with count=0\n",
  				i_core);
  			return OPAL_WRONG_STATE;
  		}
  		/* What to do with count on errors ? */
  		cpu->hbrt_spec_wakeup--;
  		if (cpu->hbrt_spec_wakeup == 0)
  			rc = hservice_clr_special_wakeup(cpu);
  		return rc;
  	case 2: /* Clear all special wakeups */
  		prlog(PR_DEBUG, "HBRT: Special wakeup release for all cores\n");
  		for_each_cpu(cpu) {
  			if (cpu->hbrt_spec_wakeup) {
  				cpu->hbrt_spec_wakeup = 0;
  				/* What to do on errors ? */
  				hservice_clr_special_wakeup(cpu);
  			}
  		}
  		return OPAL_SUCCESS;
  	default:
  		return OPAL_PARAMETER;
  	}
  }
  
  static struct host_interfaces hinterface = {
  	.interface_version = HOSTBOOT_RUNTIME_INTERFACE_VERSION,
  	.puts = hservice_puts,
  	.assert = hservice_assert,
  	.malloc = hservice_malloc,
  	.free = hservice_free,
  	.realloc = hservice_realloc,
  	.send_error_log = hservice_send_error_log,
  	.scom_read = hservice_scom_read,
  	.scom_write = hservice_scom_write,
  	.lid_load = hservice_lid_load,
  	.lid_unload = hservice_lid_unload,
  	.get_reserved_mem = hservice_get_reserved_mem,
  	.wakeup = hservice_wakeup,
  	.nanosleep = hservice_nanosleep,
  };
  
  int host_services_occ_load(void)
  {
  	struct proc_chip *chip;
  	int rc = 0;
  
  	prlog(PR_DEBUG, "HBRT: OCC Load requested\n");
  
  	if (!(hservice_runtime && hservice_runtime->loadOCC)) {
  		prerror("HBRT: No hservice_runtime->loadOCC\n");
  		return -ENOENT;
  	}
  
  	for_each_chip(chip) {
  
  		prlog(PR_DEBUG, "HBRT: Calling loadOCC() homer"
  		      " %016llx, occ_common_area %016llx, chip %04x\n",
  		      chip->homer_base,
  		      chip->occ_common_base,
  		      chip->id);
  
  		rc = hservice_runtime->loadOCC(chip->homer_base,
  						chip->homer_base,
  						chip->occ_common_base,
  						chip->occ_common_base,
  						chip->id);
  
  		hservice_mark();
  		prlog(PR_DEBUG, "HBRT: -> rc = %d\n", rc);
  	}
  	return rc;
  }
  
  int host_services_occ_start(void)
  {
  	struct proc_chip *chip;
  	int i, rc = 0, nr_chips=0;
  	uint64_t chipids[MAX_CHIPS];
  
  	prlog(PR_INFO, "HBRT: OCC Start requested\n");
  
  	if (!(hservice_runtime && hservice_runtime->startOCCs)) {
  		prerror("HBRT: No hservice_runtime->startOCCs\n");
  		return -ENOENT;
  	}
  
  	for_each_chip(chip) {
  		chipids[nr_chips++] = chip->id;
  	}
  
  	for (i = 0; i < nr_chips; i++)
  		prlog(PR_TRACE, "HBRT: Calling startOCC() for %04llx\n",
  		      chipids[i]);
  
  	/* Lets start all OCC */
  	rc = hservice_runtime->startOCCs(chipids, nr_chips);
  	hservice_mark();
  	prlog(PR_DEBUG, "HBRT: startOCCs() rc  = %d\n", rc);
  	return rc;
  }
  
  int host_services_occ_stop(void)
  {
  	int i, rc = 0, nr_slaves = 0, nr_masters = 0;
  	uint64_t *master_chipids = NULL, *slave_chipids = NULL;
  
  	prlog(PR_INFO, "HBRT: OCC Stop requested\n");
  
  	if (!(hservice_runtime && hservice_runtime->stopOCCs)) {
  		prerror("HBRT: No hservice_runtime->stopOCCs\n");
  		return -ENOENT;
  	}
  
  	rc = find_master_and_slave_occ(&master_chipids, &slave_chipids,
  				       &nr_masters, &nr_slaves);
  	if (rc)
  		goto out;
  
  	for (i = 0; i < nr_slaves; i++)
  		prlog(PR_TRACE, "HBRT: Calling stopOCC() for %04llx ",
  		      slave_chipids[i]);
  
  	if (!nr_slaves)
  		goto master;
  
  	/* Lets STOP all the slave OCC */
  	rc = hservice_runtime->stopOCCs(slave_chipids, nr_slaves);
  	prlog(PR_DEBUG, "HBRT: stopOCCs() slave rc  = %d\n", rc);
  
  master:
  	for (i = 0; i < nr_masters; i++)
  		prlog(PR_TRACE, "HBRT: Calling stopOCC() for %04llx ",
  		      master_chipids[i]);
  
  	/* Lets STOP all the master OCC */
  	rc = hservice_runtime->stopOCCs(master_chipids, nr_masters);
  
  	hservice_mark();
  	prlog(PR_DEBUG, "HBRT: stopOCCs() master rc  = %d\n", rc);
  
  out:
  	free(master_chipids);
  	free(slave_chipids);
  	return rc;
  }
  
  void host_services_occ_base_setup(void)
  {
  	struct proc_chip *chip;
  	uint64_t occ_common;
  
  	chip = next_chip(NULL); /* Frist chip */
  	occ_common = (uint64_t) local_alloc(chip->id, OCC_COMMON_SIZE, OCC_COMMON_SIZE);
  
  	for_each_chip(chip) {
  		chip->occ_common_base = occ_common;
  		chip->occ_common_size = OCC_COMMON_SIZE;
  
  		chip->homer_base = (uint64_t) local_alloc(chip->id, HOMER_IMAGE_SIZE,
  							HOMER_IMAGE_SIZE);
  		chip->homer_size = HOMER_IMAGE_SIZE;
  		memset((void *)chip->homer_base, 0, chip->homer_size);
  
  		prlog(PR_DEBUG, "HBRT: Chip %d HOMER base %016llx : %08llx\n",
  		      chip->id, chip->homer_base, chip->homer_size);
  		prlog(PR_DEBUG, "HBRT: OCC common base %016llx : %08llx\n",
  		      chip->occ_common_base, chip->occ_common_size);
  	}
  }
  
  bool hservices_init(void)
  {
  	void *code = NULL;
  	struct runtime_interfaces *(*hbrt_init)(struct host_interfaces *);
  
  	struct function_descriptor {
  		void *addr;
  		void *toc;
  	} fdesc;
  
  	code = (void *)hservice_get_reserved_mem("ibm,hbrt-code-image");
  	if (!code) {
  		prerror("HBRT: No ibm,hbrt-code-image found.\n");
  		return false;
  	}
  
  	if (memcmp(code, "HBRTVERS", 8) != 0) {
  		prerror("HBRT: Bad eyecatcher for ibm,hbrt-code-image!\n");
  		return false;
  	}
  
  	prlog(PR_INFO, "HBRT: Found HostBoot Runtime version %llu\n",
  	      ((u64 *)code)[1]);
  
  	/* We enter at 0x100 into the image. */
  	fdesc.addr = code + 0x100;
  	/* It doesn't care about TOC */
  	fdesc.toc = NULL;
  
  	hbrt_init = (void *)&fdesc;
  
  	hservice_runtime = hbrt_init(&hinterface);
  	hservice_mark();
  	if (!hservice_runtime) {
  		prerror("HBRT: Host services init failed\n");
  		return false;
  	}
  
  	prlog(PR_INFO, "HBRT: Interface version %llu\n",
  	      hservice_runtime->interface_version);
  
  	return true;
  }		
  