cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

hp_sdc_mlc.c (9323B)


      1/*
      2 * Access to HP-HIL MLC through HP System Device Controller.
      3 *
      4 * Copyright (c) 2001 Brian S. Julin
      5 * All rights reserved.
      6 *
      7 * Redistribution and use in source and binary forms, with or without
      8 * modification, are permitted provided that the following conditions
      9 * are met:
     10 * 1. Redistributions of source code must retain the above copyright
     11 *    notice, this list of conditions, and the following disclaimer,
     12 *    without modification.
     13 * 2. The name of the author may not be used to endorse or promote products
     14 *    derived from this software without specific prior written permission.
     15 *
     16 * Alternatively, this software may be distributed under the terms of the
     17 * GNU General Public License ("GPL").
     18 *
     19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
     23 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     28 *
     29 * References:
     30 * HP-HIL Technical Reference Manual.  Hewlett Packard Product No. 45918A
     31 * System Device Controller Microprocessor Firmware Theory of Operation
     32 *      for Part Number 1820-4784 Revision B.  Dwg No. A-1820-4784-2
     33 *
     34 */
     35
     36#include <linux/hil_mlc.h>
     37#include <linux/hp_sdc.h>
     38#include <linux/errno.h>
     39#include <linux/kernel.h>
     40#include <linux/module.h>
     41#include <linux/init.h>
     42#include <linux/string.h>
     43#include <linux/semaphore.h>
     44
     45#define PREFIX "HP SDC MLC: "
     46
     47static hil_mlc hp_sdc_mlc;
     48
     49MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>");
     50MODULE_DESCRIPTION("Glue for onboard HIL MLC in HP-PARISC machines");
     51MODULE_LICENSE("Dual BSD/GPL");
     52
     53static struct hp_sdc_mlc_priv_s {
     54	int emtestmode;
     55	hp_sdc_transaction trans;
     56	u8 tseq[16];
     57	int got5x;
     58} hp_sdc_mlc_priv;
     59
     60/************************* Interrupt context ******************************/
     61static void hp_sdc_mlc_isr (int irq, void *dev_id,
     62			    uint8_t status, uint8_t data)
     63{
     64	int idx;
     65	hil_mlc *mlc = &hp_sdc_mlc;
     66
     67	write_lock(&mlc->lock);
     68	if (mlc->icount < 0) {
     69		printk(KERN_WARNING PREFIX "HIL Overflow!\n");
     70		up(&mlc->isem);
     71		goto out;
     72	}
     73	idx = 15 - mlc->icount;
     74	if ((status & HP_SDC_STATUS_IRQMASK) == HP_SDC_STATUS_HILDATA) {
     75		mlc->ipacket[idx] |= data | HIL_ERR_INT;
     76		mlc->icount--;
     77		if (hp_sdc_mlc_priv.got5x || !idx)
     78			goto check;
     79		if ((mlc->ipacket[idx - 1] & HIL_PKT_ADDR_MASK) !=
     80		    (mlc->ipacket[idx] & HIL_PKT_ADDR_MASK)) {
     81			mlc->ipacket[idx] &= ~HIL_PKT_ADDR_MASK;
     82			mlc->ipacket[idx] |= (mlc->ipacket[idx - 1]
     83						& HIL_PKT_ADDR_MASK);
     84		}
     85		goto check;
     86	}
     87	/* We know status is 5X */
     88	if (data & HP_SDC_HIL_ISERR)
     89		goto err;
     90	mlc->ipacket[idx] =
     91		(data & HP_SDC_HIL_R1MASK) << HIL_PKT_ADDR_SHIFT;
     92	hp_sdc_mlc_priv.got5x = 1;
     93	goto out;
     94
     95 check:
     96	hp_sdc_mlc_priv.got5x = 0;
     97	if (mlc->imatch == 0)
     98		goto done;
     99	if ((mlc->imatch == (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_POL))
    100	    && (mlc->ipacket[idx] == (mlc->imatch | idx)))
    101		goto done;
    102	if (mlc->ipacket[idx] == mlc->imatch)
    103		goto done;
    104	goto out;
    105
    106 err:
    107	printk(KERN_DEBUG PREFIX "err code %x\n", data);
    108
    109	switch (data) {
    110	case HP_SDC_HIL_RC_DONE:
    111		printk(KERN_WARNING PREFIX "Bastard SDC reconfigured loop!\n");
    112		break;
    113
    114	case HP_SDC_HIL_ERR:
    115		mlc->ipacket[idx] |= HIL_ERR_INT | HIL_ERR_PERR |
    116					HIL_ERR_FERR | HIL_ERR_FOF;
    117		break;
    118
    119	case HP_SDC_HIL_TO:
    120		mlc->ipacket[idx] |= HIL_ERR_INT | HIL_ERR_LERR;
    121		break;
    122
    123	case HP_SDC_HIL_RC:
    124		printk(KERN_WARNING PREFIX "Bastard SDC decided to reconfigure loop!\n");
    125		break;
    126
    127	default:
    128		printk(KERN_WARNING PREFIX "Unknown HIL Error status (%x)!\n", data);
    129		break;
    130	}
    131
    132	/* No more data will be coming due to an error. */
    133 done:
    134	tasklet_schedule(mlc->tasklet);
    135	up(&mlc->isem);
    136 out:
    137	write_unlock(&mlc->lock);
    138}
    139
    140
    141/******************** Tasklet or userspace context functions ****************/
    142
    143static int hp_sdc_mlc_in(hil_mlc *mlc, suseconds_t timeout)
    144{
    145	struct hp_sdc_mlc_priv_s *priv;
    146	int rc = 2;
    147
    148	priv = mlc->priv;
    149
    150	/* Try to down the semaphore */
    151	if (down_trylock(&mlc->isem)) {
    152		if (priv->emtestmode) {
    153			mlc->ipacket[0] =
    154				HIL_ERR_INT | (mlc->opacket &
    155					       (HIL_PKT_CMD |
    156						HIL_PKT_ADDR_MASK |
    157						HIL_PKT_DATA_MASK));
    158			mlc->icount = 14;
    159			/* printk(KERN_DEBUG PREFIX ">[%x]\n", mlc->ipacket[0]); */
    160			goto wasup;
    161		}
    162		if (time_after(jiffies, mlc->instart + mlc->intimeout)) {
    163			/*	printk("!%i %i",
    164				tv.tv_usec - mlc->instart.tv_usec,
    165				mlc->intimeout);
    166			 */
    167			rc = 1;
    168			up(&mlc->isem);
    169		}
    170		goto done;
    171	}
    172 wasup:
    173	up(&mlc->isem);
    174	rc = 0;
    175 done:
    176	return rc;
    177}
    178
    179static int hp_sdc_mlc_cts(hil_mlc *mlc)
    180{
    181	struct hp_sdc_mlc_priv_s *priv;
    182
    183	priv = mlc->priv;
    184
    185	/* Try to down the semaphores -- they should be up. */
    186	BUG_ON(down_trylock(&mlc->isem));
    187	BUG_ON(down_trylock(&mlc->osem));
    188
    189	up(&mlc->isem);
    190	up(&mlc->osem);
    191
    192	if (down_trylock(&mlc->csem)) {
    193		if (priv->trans.act.semaphore != &mlc->csem)
    194			goto poll;
    195		else
    196			goto busy;
    197	}
    198
    199	if (!(priv->tseq[4] & HP_SDC_USE_LOOP))
    200		goto done;
    201
    202 poll:
    203	priv->trans.act.semaphore = &mlc->csem;
    204	priv->trans.actidx = 0;
    205	priv->trans.idx = 1;
    206	priv->trans.endidx = 5;
    207	priv->tseq[0] =
    208		HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN | HP_SDC_ACT_SEMAPHORE;
    209	priv->tseq[1] = HP_SDC_CMD_READ_USE;
    210	priv->tseq[2] = 1;
    211	priv->tseq[3] = 0;
    212	priv->tseq[4] = 0;
    213	return __hp_sdc_enqueue_transaction(&priv->trans);
    214 busy:
    215	return 1;
    216 done:
    217	priv->trans.act.semaphore = &mlc->osem;
    218	up(&mlc->csem);
    219	return 0;
    220}
    221
    222static int hp_sdc_mlc_out(hil_mlc *mlc)
    223{
    224	struct hp_sdc_mlc_priv_s *priv;
    225
    226	priv = mlc->priv;
    227
    228	/* Try to down the semaphore -- it should be up. */
    229	BUG_ON(down_trylock(&mlc->osem));
    230
    231	if (mlc->opacket & HIL_DO_ALTER_CTRL)
    232		goto do_control;
    233
    234 do_data:
    235	if (priv->emtestmode) {
    236		up(&mlc->osem);
    237		return 0;
    238	}
    239	/* Shouldn't be sending commands when loop may be busy */
    240	BUG_ON(down_trylock(&mlc->csem));
    241	up(&mlc->csem);
    242
    243	priv->trans.actidx = 0;
    244	priv->trans.idx = 1;
    245	priv->trans.act.semaphore = &mlc->osem;
    246	priv->trans.endidx = 6;
    247	priv->tseq[0] =
    248		HP_SDC_ACT_DATAREG | HP_SDC_ACT_POSTCMD | HP_SDC_ACT_SEMAPHORE;
    249	priv->tseq[1] = 0x7;
    250	priv->tseq[2] =
    251		(mlc->opacket &
    252		 (HIL_PKT_ADDR_MASK | HIL_PKT_CMD))
    253		   >> HIL_PKT_ADDR_SHIFT;
    254	priv->tseq[3] =
    255		(mlc->opacket & HIL_PKT_DATA_MASK)
    256		  >> HIL_PKT_DATA_SHIFT;
    257	priv->tseq[4] = 0;  /* No timeout */
    258	if (priv->tseq[3] == HIL_CMD_DHR)
    259		priv->tseq[4] = 1;
    260	priv->tseq[5] = HP_SDC_CMD_DO_HIL;
    261	goto enqueue;
    262
    263 do_control:
    264	priv->emtestmode = mlc->opacket & HIL_CTRL_TEST;
    265
    266	/* we cannot emulate this, it should not be used. */
    267	BUG_ON((mlc->opacket & (HIL_CTRL_APE | HIL_CTRL_IPF)) == HIL_CTRL_APE);
    268
    269	if ((mlc->opacket & HIL_CTRL_ONLY) == HIL_CTRL_ONLY)
    270		goto control_only;
    271
    272	/* Should not send command/data after engaging APE */
    273	BUG_ON(mlc->opacket & HIL_CTRL_APE);
    274
    275	/* Disengaging APE this way would not be valid either since
    276	 * the loop must be allowed to idle.
    277	 *
    278	 * So, it works out that we really never actually send control
    279	 * and data when using SDC, we just send the data.
    280	 */
    281	goto do_data;
    282
    283 control_only:
    284	priv->trans.actidx = 0;
    285	priv->trans.idx = 1;
    286	priv->trans.act.semaphore = &mlc->osem;
    287	priv->trans.endidx = 4;
    288	priv->tseq[0] =
    289	  HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT | HP_SDC_ACT_SEMAPHORE;
    290	priv->tseq[1] = HP_SDC_CMD_SET_LPC;
    291	priv->tseq[2] = 1;
    292	/* priv->tseq[3] = (mlc->ddc + 1) | HP_SDC_LPS_ACSUCC; */
    293	priv->tseq[3] = 0;
    294	if (mlc->opacket & HIL_CTRL_APE) {
    295		priv->tseq[3] |= HP_SDC_LPC_APE_IPF;
    296		BUG_ON(down_trylock(&mlc->csem));
    297	}
    298 enqueue:
    299	return hp_sdc_enqueue_transaction(&priv->trans);
    300}
    301
    302static int __init hp_sdc_mlc_init(void)
    303{
    304	hil_mlc *mlc = &hp_sdc_mlc;
    305	int err;
    306
    307#ifdef __mc68000__
    308	if (!MACH_IS_HP300)
    309		return -ENODEV;
    310#endif
    311
    312	printk(KERN_INFO PREFIX "Registering the System Domain Controller's HIL MLC.\n");
    313
    314	hp_sdc_mlc_priv.emtestmode = 0;
    315	hp_sdc_mlc_priv.trans.seq = hp_sdc_mlc_priv.tseq;
    316	hp_sdc_mlc_priv.trans.act.semaphore = &mlc->osem;
    317	hp_sdc_mlc_priv.got5x = 0;
    318
    319	mlc->cts = &hp_sdc_mlc_cts;
    320	mlc->in	= &hp_sdc_mlc_in;
    321	mlc->out = &hp_sdc_mlc_out;
    322	mlc->priv = &hp_sdc_mlc_priv;
    323
    324	err = hil_mlc_register(mlc);
    325	if (err) {
    326		printk(KERN_WARNING PREFIX "Failed to register MLC structure with hil_mlc\n");
    327		return err;
    328	}
    329
    330	if (hp_sdc_request_hil_irq(&hp_sdc_mlc_isr)) {
    331		printk(KERN_WARNING PREFIX "Request for raw HIL ISR hook denied\n");
    332		if (hil_mlc_unregister(mlc))
    333			printk(KERN_ERR PREFIX "Failed to unregister MLC structure with hil_mlc.\n"
    334				"This is bad.  Could cause an oops.\n");
    335		return -EBUSY;
    336	}
    337
    338	return 0;
    339}
    340
    341static void __exit hp_sdc_mlc_exit(void)
    342{
    343	hil_mlc *mlc = &hp_sdc_mlc;
    344
    345	if (hp_sdc_release_hil_irq(&hp_sdc_mlc_isr))
    346		printk(KERN_ERR PREFIX "Failed to release the raw HIL ISR hook.\n"
    347			"This is bad.  Could cause an oops.\n");
    348
    349	if (hil_mlc_unregister(mlc))
    350		printk(KERN_ERR PREFIX "Failed to unregister MLC structure with hil_mlc.\n"
    351			"This is bad.  Could cause an oops.\n");
    352}
    353
    354module_init(hp_sdc_mlc_init);
    355module_exit(hp_sdc_mlc_exit);