Skip to content

Commit

Permalink
rproc: Refactor and harden driver to prepare for domain restarting
Browse files Browse the repository at this point in the history
A prerequisite patch for implementing domain restart.
Also fix misc issues which I have found during refactoring.
Such as validating vgic_reserve_virq() to return a boolean with
true being a success value, checking the page to be a non-zero value
before releasing as a guest specifying PA above 4GB can crash Xen,
iterate, and checking the rproc channel against rproc_data->vq_cnt
instead of mfis_data->chan_cnt, adding missing __init annotation, etc.

The current diff looks non-ideal, but refer to the subsequent
commit which diff looks much more cleaner.

Signed-off-by: Oleksandr Tyshchenko <[email protected]>
  • Loading branch information
Oleksandr Tyshchenko committed Dec 12, 2024
1 parent 6ef6bb8 commit c04ae0f
Showing 1 changed file with 79 additions and 57 deletions.
136 changes: 79 additions & 57 deletions xen/drivers/passthrough/arm/plat-rcar/rproc.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@

#define MFIS_MAX_CHANNELS 8

#define MFIS_IICR(k) (0x1400 + 0x1008 * k + 0x20 * 0)
#define MFIS_EICR(i) (0x9404 + 0x1020 * 0 + 0x8 * i)
#define MFIS_IICR(k,i) (0x1400 + 0x1008 * (i) + 0x20 * (k))
#define MFIS_EICR(k,i) (0x9404 + 0x1020 * (k) + 0x8 * (i))

#define MFIS_SMC_TRIG ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
ARM_SMCCC_CONV_32, \
Expand Down Expand Up @@ -148,15 +148,15 @@ static void mfis_irq_handler(int irq, void *dev_id, struct cpu_user_regs *regs)

uint32_t val;

val = readl(mfis_data->base + MFIS_EICR(i));
val = readl(mfis_data->base + MFIS_EICR(0,i));

if ( !(val & 0x1 ) )
{
printk(XENLOG_WARNING"MFIS: Spurious IRQ for chan %d\n", i);
break;
}

writel(val & ~0x1, mfis_data->base + MFIS_EICR(i));
writel(val & ~0x1, mfis_data->base + MFIS_EICR(0,i));

if ( mfis_data->domains[i] )
vgic_inject_irq(mfis_data->domains[i], NULL, GUEST_MFIS_SPI, true);
Expand All @@ -167,57 +167,62 @@ static void mfis_irq_handler(int irq, void *dev_id, struct cpu_user_regs *regs)
}
}

static int mfis_add_domain(struct domain* d, int chan)
static int mfis_find_chan(struct domain *d)
{
int ret;
int i;

if ( chan >= mfis_data->chan_cnt )
return -EINVAL;
for ( i = 0; i < mfis_data->chan_cnt; i++)
if ( mfis_data->domains[i] == d )
return i;

ret = vgic_reserve_virq(d, GUEST_MFIS_SPI);
if ( ret < 0)
return ret;
return -ENOENT;
}

mfis_data->domains[chan] = d;
static int mfis_trigger_chan(int chan)
{
uint32_t val;

val = readl(mfis_data->base + MFIS_IICR(0,chan));

printk("MFIS: Added chan %d for domain %d\n", chan, d->domain_id);
if ( val & 0x1 )
return -EBUSY;

writel(1, mfis_data->base + MFIS_IICR(0,chan));
return 0;
}

static int mfis_remove_domain(int chan)
static int mfis_add_domain(struct domain* d, int chan)
{

if ( chan >= mfis_data->chan_cnt )
return -EINVAL;

mfis_data->domains[chan] = NULL;
if ( !vgic_reserve_virq(d, GUEST_MFIS_SPI) )
return -EINVAL;

mfis_data->domains[chan] = d;

printk(XENLOG_INFO"MFIS: Added chan %d for domain %d\n", chan, d->domain_id);

return 0;
}

static int mfis_trigger_chan(struct domain *d)
static int mfis_remove_domain(struct domain *d)
{
int i;
uint32_t val;
int chan = mfis_find_chan(d);

/* Find chan for domain */
for ( i = 0; i < mfis_data->chan_cnt; i++)
if ( mfis_data->domains[i] == d )
{
val = readl(mfis_data->base + MFIS_IICR(i));
if ( chan < 0 )
return 0;

if ( val & 0x1 )
return -EBUSY;
vgic_free_virq(d, GUEST_MFIS_SPI);

writel(1, mfis_data->base + MFIS_IICR(i));
return 0;
}
mfis_data->domains[chan] = NULL;

return -ENOENT;
printk(XENLOG_INFO"MFIS: Removed chan %d for domain %d\n", chan, d->domain_id);

return 0;
}

static int mfis_init(struct dt_device_node *node, const void *data)
static int __init mfis_init(struct dt_device_node *node, const void *data)
{
paddr_t start, len;
int ret, i;
Expand Down Expand Up @@ -451,8 +456,14 @@ static int rproc_assign_domain(struct domain *d, int chan)

if ( chan >= rproc_data->vq_cnt )
return -EINVAL;
ret = mfis_add_domain(d, chan);

if ( rproc_data->channels[chan].d )
{
printk(XENLOG_WARNING"rproc is already assigned\n");
return -EEXIST;
}

ret = mfis_add_domain(d, chan);
if ( ret )
return ret;
rproc_data->channels[chan].d = d;
Expand All @@ -471,11 +482,18 @@ static int rproc_remove_domain(struct domain *d)
if ( !mfis_data )
return 0;

for ( i = 0; i < mfis_data->chan_cnt; i++)
mfis_remove_domain(d);

for ( i = 0; i < rproc_data->vq_cnt; i++)
{
if ( mfis_data->domains[i] == d )
if ( rproc_data->channels[i].d == d )
{
mfis_remove_domain(i);
if ( rproc_data->channels[i].vring_pg[0] )
put_page(rproc_data->channels[i].vring_pg[0]);
if ( rproc_data->channels[i].vring_pg[1] )
put_page(rproc_data->channels[i].vring_pg[1]);

rproc_data->channels[i].d = NULL;
break;
}
}
Expand All @@ -494,6 +512,28 @@ static int rproc_find_chan(struct domain *d)
return -ENOENT;
}

static int mfis_handle_trig(struct domain *d, struct cpu_user_regs *regs)
{
int chan = mfis_find_chan(d);
int ret;

if ( chan < 0 )
{
set_user_reg(regs, 0, MFIS_SMC_ERR_NOT_AVAILABLE);
return chan;
}

ret = mfis_trigger_chan(chan);
if ( ret == 0 )
set_user_reg(regs, 0, ARM_SMCCC_SUCCESS);
else if ( ret == -EBUSY )
set_user_reg(regs, 0, MFIS_SMC_ERR_BUSY);
else
set_user_reg(regs, 0, MFIS_SMC_ERR_NOT_AVAILABLE);

return ret;
}

static int rproc_handle_get_vdev_info(struct domain *d,
struct cpu_user_regs *regs)
{
Expand Down Expand Up @@ -560,20 +600,15 @@ static int rproc_handle_set_vring_data(struct domain *d,
goto got_pa;
}
if ( !pg || t != p2m_ram_rw )
{
if ( pg )
goto put_pg;

goto err;
}

pa = page_to_maddr(pg);
got_pa:
printk("remoteproc: pa = %lx\n", pa);
if ( pa & 0xFFFFFFFF00000000UL )
{
printk(XENLOG_ERR"rproc: provided page is above 4GB\n");
goto put_pg;
goto err;
}

rproc_data->channels[ch].vdev->vring[ring].notifyid = notify_id;
Expand All @@ -591,10 +626,9 @@ static int rproc_handle_set_vring_data(struct domain *d,

return 0;

put_pg:
put_page(pg);

err:
if ( pg )
put_page(pg);
set_user_reg(regs, 0, RPROC_SMC_ERR_NOT_AVAILABLE);

return -EINVAL;
Expand All @@ -611,20 +645,8 @@ static bool rproc_handle_smc(struct cpu_user_regs *regs)
switch ( get_user_reg(regs, 0) )
{
case MFIS_SMC_TRIG:
{
int ret;

ret = mfis_trigger_chan(current->domain);
if ( ret == 0 )
set_user_reg(regs, 0, ARM_SMCCC_SUCCESS);
else if ( ret == -EBUSY )
set_user_reg(regs, 0, MFIS_SMC_ERR_BUSY);
else if ( ret == -EINVAL )
set_user_reg(regs, 0, MFIS_SMC_ERR_NOT_AVAILABLE);
else
set_user_reg(regs, 0, ARM_SMCCC_ERR_UNKNOWN_FUNCTION);
mfis_handle_trig(current->domain, regs);
return true;
}
case RPMSG_SMC_GET_VDEV_INFO:
rproc_handle_get_vdev_info(current->domain, regs);
return true;
Expand Down

0 comments on commit c04ae0f

Please sign in to comment.