LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: [PATCH 10/15] ps3stor_lib: Add support for multiple regions
From: Andre Heider @ 2011-08-01 21:01 UTC (permalink / raw)
  To: Geert Uytterhoeven; +Cc: Geoff Levand, cbe-oss-dev, Hector Martin, linuxppc-dev
In-Reply-To: <CAMuHMdXJWTw33ehLAPq00WjsebBHtQd5B9nM5Y-Z5JtevzmVqA@mail.gmail.com>

On Mon, Aug 1, 2011 at 10:35 PM, Geert Uytterhoeven
<geert@linux-m68k.org> wrote:
> On Mon, Aug 1, 2011 at 22:03, Andre Heider <a.heider@gmail.com> wrote:
>> =A0 =A0 =A0 =A0priv->gendisk =3D gendisk;
>> +
>> + =A0 =A0 =A0 /* find first accessible region */
>> + =A0 =A0 =A0 for (region_idx =3D 0; region_idx < dev->num_regions; regi=
on_idx++)
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (test_bit(region_idx, &dev->accessible_=
regions)) {
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 priv->region_idx =3D regio=
n_idx;
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
>> +
>
> Why not
>
> priv->region_idx =3D __ffs(dev->accessible_regions);

Right, thanks. Will change that for v2

^ permalink raw reply

* Re: [Cbe-oss-dev] [PATCH 06/15] ps3flash: Fix region align checks
From: Geert Uytterhoeven @ 2011-08-01 21:00 UTC (permalink / raw)
  To: Andre Heider; +Cc: Geoff Levand, cbe-oss-dev, Hector Martin, linuxppc-dev
In-Reply-To: <CAHsu+b-xtj8DvLS-v_5-QZ3Erc-9jpFd0rodeoc4RU9YPho3tw@mail.gmail.com>

On Mon, Aug 1, 2011 at 22:56, Andre Heider <a.heider@gmail.com> wrote:
> On Mon, Aug 1, 2011 at 10:29 PM, Geert Uytterhoeven
> <geert@linux-m68k.org> wrote:
>> On Mon, Aug 1, 2011 at 22:02, Andre Heider <a.heider@gmail.com> wrote:
>>> The region fields used by the align checks are set in
>>> ps3stor_setup(), so move those after that call.
>>
>> Are you sure?
>> Aren't they set in
>> arch/powerpc/platforms/ps3/device-init.c:ps3_setup_storage_dev()?
>
> Hm right, unfortunate commit message... :)
> dev->region_idx is set in ps3stor_probe_access(), which is called from
> ps3stor_setup().
> So the code always checked the first region, where it should check the
> one beeing used.

IC. You're right. That's indeed a bug.

Gr{oetje,eeting}s,

=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k=
.org

In personal conversations with technical people, I call myself a hacker. Bu=
t
when I'm talking to journalists I just say "programmer" or something like t=
hat.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =C2=A0 =C2=A0=C2=A0 =C2=A0=C2=A0 -- Linus Torvalds

^ permalink raw reply

* Re: [Cbe-oss-dev] [PATCH 09/15] ps3: Limit the number of regions per storage device
From: Andre Heider @ 2011-08-01 20:58 UTC (permalink / raw)
  To: Geert Uytterhoeven; +Cc: Geoff Levand, cbe-oss-dev, Hector Martin, linuxppc-dev
In-Reply-To: <CAMuHMdVq+WSjZKVZk1riEFX6NgW6Y+8EhCc0MqMtuDK5KAD7dw@mail.gmail.com>

On Mon, Aug 1, 2011 at 10:30 PM, Geert Uytterhoeven
<geert@linux-m68k.org> wrote:
> On Mon, Aug 1, 2011 at 22:03, Andre Heider <a.heider@gmail.com> wrote:
>> There can be only 8 regions, add a sanity check
>
> Why can there be only 8 regions?

I believe lv1 limits it to 8? I might be mistaken here, it mostly is a
check for the patches after this one

^ permalink raw reply

* Re: [Cbe-oss-dev] [PATCH 06/15] ps3flash: Fix region align checks
From: Andre Heider @ 2011-08-01 20:56 UTC (permalink / raw)
  To: Geert Uytterhoeven; +Cc: Geoff Levand, cbe-oss-dev, Hector Martin, linuxppc-dev
In-Reply-To: <CAMuHMdUgK7ZkaNA+DVg0_TL8fz+AfEOTZqW5WdOY5ybwSP=hcA@mail.gmail.com>

On Mon, Aug 1, 2011 at 10:29 PM, Geert Uytterhoeven
<geert@linux-m68k.org> wrote:
> On Mon, Aug 1, 2011 at 22:02, Andre Heider <a.heider@gmail.com> wrote:
>> The region fields used by the align checks are set in
>> ps3stor_setup(), so move those after that call.
>
> Are you sure?
> Aren't they set in
> arch/powerpc/platforms/ps3/device-init.c:ps3_setup_storage_dev()?

Hm right, unfortunate commit message... :)
dev->region_idx is set in ps3stor_probe_access(), which is called from
ps3stor_setup().
So the code always checked the first region, where it should check the
one beeing used.

Will fix the commit message.

Thanks

^ permalink raw reply

* Re: [PATCH 10/15] ps3stor_lib: Add support for multiple regions
From: Geert Uytterhoeven @ 2011-08-01 20:35 UTC (permalink / raw)
  To: Andre Heider; +Cc: Geoff Levand, cbe-oss-dev, Hector Martin, linuxppc-dev
In-Reply-To: <1312228986-32307-11-git-send-email-a.heider@gmail.com>

On Mon, Aug 1, 2011 at 22:03, Andre Heider <a.heider@gmail.com> wrote:
> Users (ps3disk, ps3flash and ps3rom) retain the old behavior. That is:
> they still only provide access to the first accessible region.
>
> Signed-off-by: Andre Heider <a.heider@gmail.com>
> ---
> =C2=A0arch/powerpc/include/asm/ps3stor.h | =C2=A0 =C2=A04 ++--
> =C2=A0drivers/block/ps3disk.c =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0| =
=C2=A0 15 +++++++++++++--
> =C2=A0drivers/char/ps3flash.c =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0| =
=C2=A0 23 +++++++++++++++++------
> =C2=A0drivers/ps3/ps3stor_lib.c =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0| =C2=
=A0 25 ++++++++++++-------------
> =C2=A0drivers/scsi/ps3rom.c =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0| =C2=A0 11 +++++++----
> =C2=A05 files changed, 51 insertions(+), 27 deletions(-)
>
> diff --git a/arch/powerpc/include/asm/ps3stor.h b/arch/powerpc/include/as=
m/ps3stor.h
> index d51e53c..9871c05 100644
> --- a/arch/powerpc/include/asm/ps3stor.h
> +++ b/arch/powerpc/include/asm/ps3stor.h
> @@ -51,7 +51,6 @@ struct ps3_storage_device {
>
> =C2=A0 =C2=A0 =C2=A0 =C2=A0unsigned int num_regions;
> =C2=A0 =C2=A0 =C2=A0 =C2=A0unsigned long accessible_regions;
> - =C2=A0 =C2=A0 =C2=A0 unsigned int region_idx; =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/* first accessible region */
> =C2=A0 =C2=A0 =C2=A0 =C2=A0struct ps3_storage_region regions[0]; =C2=A0 /=
* Must be last */
> =C2=A0};
>
> @@ -63,7 +62,8 @@ static inline struct ps3_storage_device *to_ps3_storage=
_device(struct device *de
> =C2=A0extern int ps3stor_setup(struct ps3_storage_device *dev,
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 irq_handler_t handler);
> =C2=A0extern void ps3stor_teardown(struct ps3_storage_device *dev);
> -extern u64 ps3stor_read_write_sectors(struct ps3_storage_device *dev, u6=
4 lpar,
> +extern u64 ps3stor_read_write_sectors(struct ps3_storage_device *dev,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 unsigned int region=
_idx, u64 lpar,
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0u64 start_sector=
, u64 sectors,
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0int write);
> =C2=A0extern u64 ps3stor_send_command(struct ps3_storage_device *dev, u64=
 cmd,
> diff --git a/drivers/block/ps3disk.c b/drivers/block/ps3disk.c
> index 8e1ce2e..96e00ff 100644
> --- a/drivers/block/ps3disk.c
> +++ b/drivers/block/ps3disk.c
> @@ -42,6 +42,7 @@ struct ps3disk_private {
> =C2=A0 =C2=A0 =C2=A0 =C2=A0spinlock_t lock; =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0 =C2=A0 =C2=A0/* Request queue spinlock */
> =C2=A0 =C2=A0 =C2=A0 =C2=A0struct request_queue *queue;
> =C2=A0 =C2=A0 =C2=A0 =C2=A0struct gendisk *gendisk;
> + =C2=A0 =C2=A0 =C2=A0 unsigned int region_idx; =C2=A0 =C2=A0 =C2=A0 =C2=
=A0/* first accessible region */
> =C2=A0 =C2=A0 =C2=A0 =C2=A0unsigned int blocking_factor;
> =C2=A0 =C2=A0 =C2=A0 =C2=A0struct request *req;
> =C2=A0 =C2=A0 =C2=A0 =C2=A0u64 raw_capacity;
> @@ -125,7 +126,7 @@ static int ps3disk_submit_request_sg(struct ps3_stora=
ge_device *dev,
> =C2=A0 =C2=A0 =C2=A0 =C2=A0int write =3D rq_data_dir(req), res;
> =C2=A0 =C2=A0 =C2=A0 =C2=A0const char *op =3D write ? "write" : "read";
> =C2=A0 =C2=A0 =C2=A0 =C2=A0u64 start_sector, sectors;
> - =C2=A0 =C2=A0 =C2=A0 unsigned int region_id =3D dev->regions[dev->regio=
n_idx].id;
> + =C2=A0 =C2=A0 =C2=A0 unsigned int region_id =3D dev->regions[priv->regi=
on_idx].id;
>
> =C2=A0#ifdef DEBUG
> =C2=A0 =C2=A0 =C2=A0 =C2=A0unsigned int n =3D 0;
> @@ -408,6 +409,7 @@ static int __devinit ps3disk_probe(struct ps3_system_=
bus_device *_dev)
> =C2=A0 =C2=A0 =C2=A0 =C2=A0unsigned int devidx;
> =C2=A0 =C2=A0 =C2=A0 =C2=A0struct request_queue *queue;
> =C2=A0 =C2=A0 =C2=A0 =C2=A0struct gendisk *gendisk;
> + =C2=A0 =C2=A0 =C2=A0 unsigned int region_idx;
>
> =C2=A0 =C2=A0 =C2=A0 =C2=A0if (dev->blk_size < 512) {
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0dev_err(&dev->sbd.=
core,
> @@ -482,6 +484,14 @@ static int __devinit ps3disk_probe(struct ps3_system=
_bus_device *_dev)
> =C2=A0 =C2=A0 =C2=A0 =C2=A0}
>
> =C2=A0 =C2=A0 =C2=A0 =C2=A0priv->gendisk =3D gendisk;
> +
> + =C2=A0 =C2=A0 =C2=A0 /* find first accessible region */
> + =C2=A0 =C2=A0 =C2=A0 for (region_idx =3D 0; region_idx < dev->num_regio=
ns; region_idx++)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (test_bit(region_id=
x, &dev->accessible_regions)) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 priv->region_idx =3D region_idx;
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 break;
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }
> +

Why not

priv->region_idx =3D __ffs(dev->accessible_regions);

like the original code in ps3stor_probe_access() used? Cfr. the code
you removed:

> diff --git a/drivers/ps3/ps3stor_lib.c b/drivers/ps3/ps3stor_lib.c
> index af0afa1..5bbc023 100644
> --- a/drivers/ps3/ps3stor_lib.c
> +++ b/drivers/ps3/ps3stor_lib.c
> @@ -124,15 +128,8 @@ static int ps3stor_probe_access(struct ps3_storage_d=
evice *dev)
> =C2=A0 =C2=A0 =C2=A0 =C2=A0n =3D hweight_long(dev->accessible_regions);
> =C2=A0 =C2=A0 =C2=A0 =C2=A0if (n > 1)
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0dev_info(&dev->sbd=
.core,
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0"%s:%u: %lu accessible regions found. Only the first "
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0"one will be used\n",
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0"%s:%u: %lu accessible regions found\n",
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 __func__, __LINE__, n);
> - =C2=A0 =C2=A0 =C2=A0 dev->region_idx =3D __ffs(dev->accessible_regions)=
;
> - =C2=A0 =C2=A0 =C2=A0 dev_info(&dev->sbd.core,
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"First accessibl=
e region has index %u start %llu size %llu\n",
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0dev->region_idx,=
 dev->regions[dev->region_idx].start,
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0dev->regions[dev=
->region_idx].size);
> -
> =C2=A0 =C2=A0 =C2=A0 =C2=A0return 0;
> =C2=A0}
>

Same in the other drivers.

Gr{oetje,eeting}s,

=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k=
.org

In personal conversations with technical people, I call myself a hacker. Bu=
t
when I'm talking to journalists I just say "programmer" or something like t=
hat.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =C2=A0 =C2=A0=C2=A0 =C2=A0=C2=A0 -- Linus Torvalds

^ permalink raw reply

* Re: [Cbe-oss-dev] [PATCH 09/15] ps3: Limit the number of regions per storage device
From: Geert Uytterhoeven @ 2011-08-01 20:30 UTC (permalink / raw)
  To: Andre Heider; +Cc: Geoff Levand, cbe-oss-dev, Hector Martin, linuxppc-dev
In-Reply-To: <1312228986-32307-10-git-send-email-a.heider@gmail.com>

On Mon, Aug 1, 2011 at 22:03, Andre Heider <a.heider@gmail.com> wrote:
> There can be only 8 regions, add a sanity check

Why can there be only 8 regions?

> Signed-off-by: Andre Heider <a.heider@gmail.com>
> ---
> =C2=A0arch/powerpc/include/asm/ps3stor.h =C2=A0 =C2=A0 =C2=A0 | =C2=A0 =
=C2=A01 +
> =C2=A0arch/powerpc/platforms/ps3/device-init.c | =C2=A0 =C2=A08 ++++++++
> =C2=A02 files changed, 9 insertions(+), 0 deletions(-)
>
> diff --git a/arch/powerpc/include/asm/ps3stor.h b/arch/powerpc/include/as=
m/ps3stor.h
> index 6fcaf71..d51e53c 100644
> --- a/arch/powerpc/include/asm/ps3stor.h
> +++ b/arch/powerpc/include/asm/ps3stor.h
> @@ -25,6 +25,7 @@
>
> =C2=A0#include <asm/ps3.h>
>
> +#define PS3_STORAGE_MAX_REGIONS =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =C2=A0(8)
>
> =C2=A0struct ps3_storage_region {
> =C2=A0 =C2=A0 =C2=A0 =C2=A0unsigned int id;
> diff --git a/arch/powerpc/platforms/ps3/device-init.c b/arch/powerpc/plat=
forms/ps3/device-init.c
> index 6c4b583..830d741 100644
> --- a/arch/powerpc/platforms/ps3/device-init.c
> +++ b/arch/powerpc/platforms/ps3/device-init.c
> @@ -349,6 +349,14 @@ static int ps3_setup_storage_dev(const struct ps3_re=
pository_device *repo,
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return -ENODEV;
> =C2=A0 =C2=A0 =C2=A0 =C2=A0}
>
> + =C2=A0 =C2=A0 =C2=A0 if (num_regions > PS3_STORAGE_MAX_REGIONS) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 pr_warning("%s:%u: dev=
ice %u:%u reported %u regions, "
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0 =C2=A0"limiting to %u\n", __func__, __LINE__,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0 =C2=A0num_regions, repo->bus_index, repo->dev_index,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0 =C2=A0PS3_STORAGE_MAX_REGIONS);
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 num_regions =3D PS3_ST=
ORAGE_MAX_REGIONS;
> + =C2=A0 =C2=A0 =C2=A0 }
> +
> =C2=A0 =C2=A0 =C2=A0 =C2=A0pr_debug("%s:%u: (%u:%u:%u): port %llu blk_siz=
e %llu num_blocks %llu "
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "num_regions %u\n=
", __func__, __LINE__, repo->bus_index,
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 repo->dev_index, =
repo->dev_type, port, blk_size, num_blocks,

Gr{oetje,eeting}s,

=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k=
.org

In personal conversations with technical people, I call myself a hacker. Bu=
t
when I'm talking to journalists I just say "programmer" or something like t=
hat.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =C2=A0 =C2=A0=C2=A0 =C2=A0=C2=A0 -- Linus Torvalds

^ permalink raw reply

* Re: [Cbe-oss-dev] [PATCH 06/15] ps3flash: Fix region align checks
From: Geert Uytterhoeven @ 2011-08-01 20:29 UTC (permalink / raw)
  To: Andre Heider; +Cc: Geoff Levand, cbe-oss-dev, Hector Martin, linuxppc-dev
In-Reply-To: <1312228986-32307-7-git-send-email-a.heider@gmail.com>

On Mon, Aug 1, 2011 at 22:02, Andre Heider <a.heider@gmail.com> wrote:
> The region fields used by the align checks are set in
> ps3stor_setup(), so move those after that call.

Are you sure?
Aren't they set in
arch/powerpc/platforms/ps3/device-init.c:ps3_setup_storage_dev()?

> Signed-off-by: Andre Heider <a.heider@gmail.com>
> ---
> =C2=A0drivers/char/ps3flash.c | =C2=A0 30 +++++++++++++++---------------
> =C2=A01 files changed, 15 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/char/ps3flash.c b/drivers/char/ps3flash.c
> index 85c004a..69c734a 100644
> --- a/drivers/char/ps3flash.c
> +++ b/drivers/char/ps3flash.c
> @@ -360,21 +360,6 @@ static int __devinit ps3flash_probe(struct ps3_syste=
m_bus_device *_dev)
> =C2=A0 =C2=A0 =C2=A0 =C2=A0int error;
> =C2=A0 =C2=A0 =C2=A0 =C2=A0unsigned long tmp;
>
> - =C2=A0 =C2=A0 =C2=A0 tmp =3D dev->regions[dev->region_idx].start*dev->b=
lk_size;
> - =C2=A0 =C2=A0 =C2=A0 if (tmp % FLASH_BLOCK_SIZE) {
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 dev_err(&dev->sbd.core=
,
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 "%s:%u region start %lu is not aligned\n", __func__,
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 __LINE__, tmp);
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return -EINVAL;
> - =C2=A0 =C2=A0 =C2=A0 }
> - =C2=A0 =C2=A0 =C2=A0 tmp =3D dev->regions[dev->region_idx].size*dev->bl=
k_size;
> - =C2=A0 =C2=A0 =C2=A0 if (tmp % FLASH_BLOCK_SIZE) {
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 dev_err(&dev->sbd.core=
,
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 "%s:%u region size %lu is not aligned\n", __func__,
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 __LINE__, tmp);
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return -EINVAL;
> - =C2=A0 =C2=A0 =C2=A0 }
> -
> =C2=A0 =C2=A0 =C2=A0 =C2=A0/* use static buffer, kmalloc cannot allocate =
256 KiB */
> =C2=A0 =C2=A0 =C2=A0 =C2=A0if (!ps3flash_bounce_buffer.address)
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return -ENODEV;
> @@ -405,6 +390,21 @@ static int __devinit ps3flash_probe(struct ps3_syste=
m_bus_device *_dev)
> =C2=A0 =C2=A0 =C2=A0 =C2=A0if (error)
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0goto fail_free_pri=
v;
>
> + =C2=A0 =C2=A0 =C2=A0 tmp =3D dev->regions[dev->region_idx].start*dev->b=
lk_size;
> + =C2=A0 =C2=A0 =C2=A0 if (tmp % FLASH_BLOCK_SIZE) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 dev_err(&dev->sbd.core=
,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 "%s:%u region start %lu is not aligned\n", __func__,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 __LINE__, tmp);
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return -EINVAL;
> + =C2=A0 =C2=A0 =C2=A0 }
> + =C2=A0 =C2=A0 =C2=A0 tmp =3D dev->regions[dev->region_idx].size*dev->bl=
k_size;
> + =C2=A0 =C2=A0 =C2=A0 if (tmp % FLASH_BLOCK_SIZE) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 dev_err(&dev->sbd.core=
,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 "%s:%u region size %lu is not aligned\n", __func__,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 __LINE__, tmp);
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return -EINVAL;
> + =C2=A0 =C2=A0 =C2=A0 }
> +
> =C2=A0 =C2=A0 =C2=A0 =C2=A0ps3flash_misc.parent =3D &dev->sbd.core;
> =C2=A0 =C2=A0 =C2=A0 =C2=A0error =3D misc_register(&ps3flash_misc);
> =C2=A0 =C2=A0 =C2=A0 =C2=A0if (error) {

Gr{oetje,eeting}s,

=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k=
.org

In personal conversations with technical people, I call myself a hacker. Bu=
t
when I'm talking to journalists I just say "programmer" or something like t=
hat.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =C2=A0 =C2=A0=C2=A0 =C2=A0=C2=A0 -- Linus Torvalds

^ permalink raw reply

* Re: kvm PCI assignment & VFIO ramblings
From: Alex Williamson @ 2011-08-01 20:27 UTC (permalink / raw)
  To: Avi Kivity
  Cc: Alexey Kardashevskiy, kvm, Paul Mackerras, David Gibson,
	Anthony Liguori, linux-pci@vger.kernel.org, linuxppc-dev
In-Reply-To: <4E356221.6010302@redhat.com>

On Sun, 2011-07-31 at 17:09 +0300, Avi Kivity wrote:
> On 07/30/2011 02:58 AM, Benjamin Herrenschmidt wrote:
> > Due to our paravirt nature, we don't need to masquerade the MSI-X table
> > for example. At all. If the guest configures crap into it, too bad, it
> > can only shoot itself in the foot since the host bridge enforce
> > validation anyways as I explained earlier. Because it's all paravirt, we
> > don't need to "translate" the interrupt vectors&  addresses, the guest
> > will call hyercalls to configure things anyways.
> 
> So, you have interrupt redirection?  That is, MSI-x table values encode 
> the vcpu, not pcpu?
> 
> Alex, with interrupt redirection, we can skip this as well?  Perhaps 
> only if the guest enables interrupt redirection?

It's not clear to me how we could skip it.  With VT-d, we'd have to
implement an emulated interrupt remapper and hope that the guest picks
unused indexes in the host interrupt remapping table before it could do
anything useful with direct access to the MSI-X table.  Maybe AMD IOMMU
makes this easier?  Thanks,

Alex

^ permalink raw reply

* [PATCH 15/15] ps3: Add a NOR FLASH driver for PS3s without NAND
From: Andre Heider @ 2011-08-01 20:03 UTC (permalink / raw)
  To: Geoff Levand; +Cc: cbe-oss-dev, Hector Martin, linuxppc-dev
In-Reply-To: <1312228986-32307-1-git-send-email-a.heider@gmail.com>

A gendisk for each accessible region is created, and a default set
of region flags is provided - overwritable via a module param array.

Signed-off-by: Andre Heider <a.heider@gmail.com>
---
 arch/powerpc/include/asm/ps3.h           |    2 +
 arch/powerpc/platforms/ps3/Kconfig       |   15 +
 arch/powerpc/platforms/ps3/device-init.c |    7 +
 arch/powerpc/platforms/ps3/platform.h    |    1 +
 arch/powerpc/platforms/ps3/system-bus.c  |    2 +
 drivers/block/Makefile                   |    1 +
 drivers/block/ps3nflash.c                |  473 ++++++++++++++++++++++++++++++
 7 files changed, 501 insertions(+), 0 deletions(-)
 create mode 100644 drivers/block/ps3nflash.c

diff --git a/arch/powerpc/include/asm/ps3.h b/arch/powerpc/include/asm/ps3.h
index 136354a..fe84231 100644
--- a/arch/powerpc/include/asm/ps3.h
+++ b/arch/powerpc/include/asm/ps3.h
@@ -333,6 +333,7 @@ enum ps3_match_id {
 	PS3_MATCH_ID_SOUND		= 9,
 	PS3_MATCH_ID_GPU		= 10,
 	PS3_MATCH_ID_LPM		= 11,
+	PS3_MATCH_ID_STOR_NFLASH	= 12,
 };
 
 enum ps3_match_sub_id {
@@ -352,6 +353,7 @@ enum ps3_match_sub_id {
 #define PS3_MODULE_ALIAS_GPU_FB		"ps3:10:1"
 #define PS3_MODULE_ALIAS_GPU_RAMDISK	"ps3:10:2"
 #define PS3_MODULE_ALIAS_LPM		"ps3:11:0"
+#define PS3_MODULE_ALIAS_STOR_NFLASH	"ps3:12:0"
 
 enum ps3_system_bus_device_type {
 	PS3_DEVICE_TYPE_IOC0 = 1,
diff --git a/arch/powerpc/platforms/ps3/Kconfig b/arch/powerpc/platforms/ps3/Kconfig
index f8d865c..9e1f841 100644
--- a/arch/powerpc/platforms/ps3/Kconfig
+++ b/arch/powerpc/platforms/ps3/Kconfig
@@ -142,6 +142,21 @@ config PS3_VFLASH
 	  select PS3_FLASH above to add support for FLASH ROM for the
 	  OtherOS LPAR.
 
+config PS3_NFLASH
+	tristate "PS3 NFLASH Storage Driver"
+	depends on PPC_PS3 && BLOCK && EXPERT
+	select PS3_STORAGE
+	help
+	  Include support for the PS3 NOR FLASH Storage.
+
+	  This support is required to access the PS3 NOR flash, which can be
+	  found on SKU models starting around October 2007.
+
+	  The NOR flash contains essential boot loaders. Damaging this area
+	  will render the console unbootable.
+
+	  If unsure, say N.
+
 config PS3_VRAM
 	tristate "PS3 Video RAM Storage Driver"
 	depends on FB_PS3=y && BLOCK && m
diff --git a/arch/powerpc/platforms/ps3/device-init.c b/arch/powerpc/platforms/ps3/device-init.c
index 7a3dbf8..c2fbe33 100644
--- a/arch/powerpc/platforms/ps3/device-init.c
+++ b/arch/powerpc/platforms/ps3/device-init.c
@@ -601,6 +601,13 @@ static int ps3_setup_dynamic_device(const struct ps3_repository_device *repo)
 				 __func__, __LINE__);
 		break;
 
+	case PS3_DEV_TYPE_STOR_NFLASH:
+		result = ps3_setup_storage_dev(repo, PS3_MATCH_ID_STOR_NFLASH);
+		if (result)
+			pr_debug("%s:%u ps3_setup_storage_dev failed\n",
+				 __func__, __LINE__);
+		break;
+
 	default:
 		result = 0;
 		pr_debug("%s:%u: unsupported dev_type %u\n", __func__, __LINE__,
diff --git a/arch/powerpc/platforms/ps3/platform.h b/arch/powerpc/platforms/ps3/platform.h
index 1ba15b8..94b5048 100644
--- a/arch/powerpc/platforms/ps3/platform.h
+++ b/arch/powerpc/platforms/ps3/platform.h
@@ -87,6 +87,7 @@ enum ps3_dev_type {
 	PS3_DEV_TYPE_STOR_ROM = TYPE_ROM,	/* 5 */
 	PS3_DEV_TYPE_SB_GPIO = 6,
 	PS3_DEV_TYPE_STOR_FLASH = TYPE_RBC,	/* 14 */
+	PS3_DEV_TYPE_STOR_NFLASH = 254,
 };
 
 int ps3_repository_read_bus_str(unsigned int bus_index, const char *bus_str,
diff --git a/arch/powerpc/platforms/ps3/system-bus.c b/arch/powerpc/platforms/ps3/system-bus.c
index 23083c3..810bbc5 100644
--- a/arch/powerpc/platforms/ps3/system-bus.c
+++ b/arch/powerpc/platforms/ps3/system-bus.c
@@ -174,6 +174,7 @@ int ps3_open_hv_device(struct ps3_system_bus_device *dev)
 	case PS3_MATCH_ID_STOR_DISK:
 	case PS3_MATCH_ID_STOR_ROM:
 	case PS3_MATCH_ID_STOR_FLASH:
+	case PS3_MATCH_ID_STOR_NFLASH:
 		return ps3_open_hv_device_sb(dev);
 
 	case PS3_MATCH_ID_SOUND:
@@ -212,6 +213,7 @@ int ps3_close_hv_device(struct ps3_system_bus_device *dev)
 	case PS3_MATCH_ID_STOR_DISK:
 	case PS3_MATCH_ID_STOR_ROM:
 	case PS3_MATCH_ID_STOR_FLASH:
+	case PS3_MATCH_ID_STOR_NFLASH:
 		return ps3_close_hv_device_sb(dev);
 
 	case PS3_MATCH_ID_SOUND:
diff --git a/drivers/block/Makefile b/drivers/block/Makefile
index 8b02899..ab2bb06 100644
--- a/drivers/block/Makefile
+++ b/drivers/block/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_BLK_DEV_FD)	+= floppy.o
 obj-$(CONFIG_AMIGA_FLOPPY)	+= amiflop.o
 obj-$(CONFIG_PS3_DISK)		+= ps3disk.o
 obj-$(CONFIG_PS3_VFLASH)	+= ps3vflash.o
+obj-$(CONFIG_PS3_NFLASH)	+= ps3nflash.o
 obj-$(CONFIG_PS3_VRAM)		+= ps3vram.o
 obj-$(CONFIG_ATARI_FLOPPY)	+= ataflop.o
 obj-$(CONFIG_AMIGA_Z2RAM)	+= z2ram.o
diff --git a/drivers/block/ps3nflash.c b/drivers/block/ps3nflash.c
new file mode 100644
index 0000000..5a7127e
--- /dev/null
+++ b/drivers/block/ps3nflash.c
@@ -0,0 +1,473 @@
+/*
+ * PS3 NOR FLASH Storage Driver
+ *
+ * Copyright (C) 2011 Andre Heider <a.heider@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/ata.h>
+#include <linux/blkdev.h>
+#include <linux/slab.h>
+
+#include <asm/lv1call.h>
+#include <asm/ps3stor.h>
+#include <asm/firmware.h>
+
+
+#define DEVICE_NAME		"ps3nflash"
+
+#define BOUNCE_SIZE		(64*1024)
+
+#define PS3NFLASH_MAX_DISKS	16
+#define PS3NFLASH_MINORS	16
+
+
+#define PS3NFLASH_NAME		"ps3n%c%c"
+
+
+static unsigned int region_flags[] = {
+	PS3_STORAGE_FLAG_SKIP_ACL,
+	PS3_STORAGE_FLAG_SKIP_ACL,
+	PS3_STORAGE_FLAG_SKIP_ACL,
+	PS3_STORAGE_FLAG_SKIP_ACL,
+	PS3_STORAGE_FLAG_DEFAULT,
+	PS3_STORAGE_FLAG_DEFAULT,
+	PS3_STORAGE_FLAG_DEFAULT,
+	PS3_STORAGE_FLAG_DEFAULT,
+};
+module_param_array(region_flags, uint, NULL, S_IRUGO);
+MODULE_PARM_DESC(region_flags, "Region flags");
+
+struct ps3nflash_private {
+	spinlock_t lock;		/* Request queue spinlock */
+	struct request_queue *queue;
+	unsigned int blocking_factor;
+	struct request *req;
+	unsigned int devidx;
+	struct gendisk *gendisk[0];	/* Must be last */
+};
+
+static int ps3nflash_major;
+
+
+static const struct block_device_operations ps3nflash_fops = {
+	.owner		= THIS_MODULE,
+};
+
+
+static void ps3nflash_scatter_gather(struct ps3_storage_device *dev,
+				     struct request *req, int gather)
+{
+	unsigned int offset = 0;
+	struct req_iterator iter;
+	struct bio_vec *bvec;
+	unsigned int i = 0;
+	size_t size;
+	void *buf;
+
+	rq_for_each_segment(bvec, req, iter) {
+		unsigned long flags;
+		dev_dbg(&dev->sbd.core,
+			"%s:%u: bio %u: %u segs %u sectors from %lu\n",
+			__func__, __LINE__, i, bio_segments(iter.bio),
+			bio_sectors(iter.bio), iter.bio->bi_sector);
+
+		size = bvec->bv_len;
+		buf = bvec_kmap_irq(bvec, &flags);
+		if (gather)
+			memcpy(dev->bounce_buf+offset, buf, size);
+		else
+			memcpy(buf, dev->bounce_buf+offset, size);
+		offset += size;
+		flush_kernel_dcache_page(bvec->bv_page);
+		bvec_kunmap_irq(buf, &flags);
+		i++;
+	}
+}
+
+static int ps3nflash_submit_request_sg(struct ps3_storage_device *dev,
+				       struct request *req)
+{
+	struct ps3nflash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
+	int write = rq_data_dir(req), res;
+	const char *op = write ? "write" : "read";
+	u64 start_sector, sectors;
+	unsigned int region_idx = MINOR(disk_devt(req->rq_disk)) &
+				  (PS3NFLASH_MINORS - 1);
+	unsigned int region_id = dev->regions[region_idx].id;
+	u64 flags = dev->regions[region_idx].flags;
+
+#ifdef DEBUG
+	unsigned int n = 0;
+	struct bio_vec *bv;
+	struct req_iterator iter;
+
+	rq_for_each_segment(bv, req, iter)
+		n++;
+	dev_dbg(&dev->sbd.core,
+		"%s:%u: %s req has %u bvecs for %u sectors\n",
+		__func__, __LINE__, op, n, blk_rq_sectors(req));
+#endif
+
+	start_sector = blk_rq_pos(req) * priv->blocking_factor;
+	sectors = blk_rq_sectors(req) * priv->blocking_factor;
+	dev_dbg(&dev->sbd.core, "%s:%u: %s %llu sectors starting at %llu\n",
+		__func__, __LINE__, op, sectors, start_sector);
+
+	if (write) {
+		ps3nflash_scatter_gather(dev, req, 1);
+
+		res = lv1_storage_write(dev->sbd.dev_id, region_id,
+					start_sector, sectors, flags,
+					dev->bounce_lpar, &dev->tag);
+	} else {
+		res = lv1_storage_read(dev->sbd.dev_id, region_id,
+				       start_sector, sectors, flags,
+				       dev->bounce_lpar, &dev->tag);
+	}
+	if (res) {
+		dev_err(&dev->sbd.core, "%s:%u: %s failed %d\n", __func__,
+			__LINE__, op, res);
+		__blk_end_request_all(req, -EIO);
+		return 0;
+	}
+
+	priv->req = req;
+	return 1;
+}
+
+static void ps3nflash_do_request(struct ps3_storage_device *dev,
+				 struct request_queue *q)
+{
+	struct request *req;
+
+	dev_dbg(&dev->sbd.core, "%s:%u\n", __func__, __LINE__);
+
+	while ((req = blk_fetch_request(q))) {
+		if (req->cmd_type == REQ_TYPE_FS) {
+			if (ps3nflash_submit_request_sg(dev, req))
+				break;
+		} else {
+			blk_dump_rq_flags(req, DEVICE_NAME " bad request");
+			__blk_end_request_all(req, -EIO);
+			continue;
+		}
+	}
+}
+
+static void ps3nflash_request(struct request_queue *q)
+{
+	struct ps3_storage_device *dev = q->queuedata;
+	struct ps3nflash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
+
+	if (priv->req) {
+		dev_dbg(&dev->sbd.core, "%s:%u busy\n", __func__, __LINE__);
+		return;
+	}
+
+	ps3nflash_do_request(dev, q);
+}
+
+static irqreturn_t ps3nflash_interrupt(int irq, void *data)
+{
+	struct ps3_storage_device *dev = data;
+	struct ps3nflash_private *priv;
+	struct request *req;
+	int res, read, error;
+	u64 tag, status;
+	const char *op;
+
+	res = lv1_storage_get_async_status(dev->sbd.dev_id, &tag, &status);
+
+	if (tag != dev->tag)
+		dev_err(&dev->sbd.core,
+			"%s:%u: tag mismatch, got %llx, expected %llx\n",
+			__func__, __LINE__, tag, dev->tag);
+
+	if (res) {
+		dev_err(&dev->sbd.core, "%s:%u: res=%d status=0x%llx\n",
+			__func__, __LINE__, res, status);
+		return IRQ_HANDLED;
+	}
+
+	priv = ps3_system_bus_get_drvdata(&dev->sbd);
+	req = priv->req;
+	if (!req) {
+		dev_dbg(&dev->sbd.core,
+			"%s:%u non-block layer request completed\n", __func__,
+			__LINE__);
+		dev->lv1_status = status;
+		complete(&dev->done);
+		return IRQ_HANDLED;
+	}
+
+	read = !rq_data_dir(req);
+	op = read ? "read" : "write";
+
+	if (status) {
+		dev_dbg(&dev->sbd.core, "%s:%u: %s failed 0x%llx\n", __func__,
+			__LINE__, op, status);
+		error = -EIO;
+	} else {
+		dev_dbg(&dev->sbd.core, "%s:%u: %s completed\n", __func__,
+			__LINE__, op);
+		error = 0;
+		if (read)
+			ps3nflash_scatter_gather(dev, req, 0);
+	}
+
+	spin_lock(&priv->lock);
+	__blk_end_request_all(req, error);
+	priv->req = NULL;
+	ps3nflash_do_request(dev, priv->queue);
+	spin_unlock(&priv->lock);
+
+	return IRQ_HANDLED;
+}
+
+static unsigned long ps3nflash_mask;
+
+static DEFINE_MUTEX(ps3nflash_mask_mutex);
+
+static int __devinit ps3nflash_probe(struct ps3_system_bus_device *_dev)
+{
+	struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
+	struct ps3nflash_private *priv;
+	int error;
+	unsigned int devidx;
+	struct request_queue *queue;
+	struct gendisk *gendisk;
+	u64 raw_capacity;
+	unsigned int region_idx;
+
+	if (dev->blk_size < 512) {
+		dev_err(&dev->sbd.core,
+			"%s:%u: cannot handle block size %llu\n", __func__,
+			__LINE__, dev->blk_size);
+		return -EINVAL;
+	}
+
+	BUILD_BUG_ON(PS3NFLASH_MAX_DISKS > BITS_PER_LONG);
+	mutex_lock(&ps3nflash_mask_mutex);
+	devidx = find_first_zero_bit(&ps3nflash_mask, PS3NFLASH_MAX_DISKS);
+	if (devidx >= PS3NFLASH_MAX_DISKS) {
+		dev_err(&dev->sbd.core, "%s:%u: Too many disks\n", __func__,
+			__LINE__);
+		mutex_unlock(&ps3nflash_mask_mutex);
+		return -ENOSPC;
+	}
+	__set_bit(devidx, &ps3nflash_mask);
+	mutex_unlock(&ps3nflash_mask_mutex);
+
+	priv = kzalloc(sizeof(*priv) +
+		       dev->num_regions * sizeof(struct gendisk),
+		       GFP_KERNEL);
+	if (!priv) {
+		error = -ENOMEM;
+		goto fail;
+	}
+
+	ps3_system_bus_set_drvdata(_dev, priv);
+	spin_lock_init(&priv->lock);
+
+	dev->bounce_size = BOUNCE_SIZE;
+	dev->bounce_buf = kmalloc(BOUNCE_SIZE, GFP_DMA);
+	if (!dev->bounce_buf) {
+		error = -ENOMEM;
+		goto fail_free_priv;
+	}
+
+	for (region_idx = 0; region_idx < dev->num_regions; region_idx++)
+		dev->regions[region_idx].flags = region_flags[region_idx];
+
+	error = ps3stor_setup(dev, ps3nflash_interrupt);
+	if (error)
+		goto fail_free_bounce;
+
+	priv->devidx = devidx;
+
+	queue = blk_init_queue(ps3nflash_request, &priv->lock);
+	if (!queue) {
+		dev_err(&dev->sbd.core, "%s:%u: blk_init_queue failed\n",
+			__func__, __LINE__);
+		error = -ENOMEM;
+		goto fail_teardown;
+	}
+
+	priv->queue = queue;
+	queue->queuedata = dev;
+
+	blk_queue_bounce_limit(queue, BLK_BOUNCE_HIGH);
+
+	blk_queue_max_hw_sectors(queue, dev->bounce_size >> 9);
+	blk_queue_segment_boundary(queue, -1UL);
+	blk_queue_dma_alignment(queue, dev->blk_size-1);
+	blk_queue_logical_block_size(queue, dev->blk_size);
+
+	blk_queue_flush(queue, REQ_FLUSH);
+
+	blk_queue_max_segments(queue, -1);
+	blk_queue_max_segment_size(queue, dev->bounce_size);
+
+	if (test_bit(0, &dev->accessible_regions) == 0) {
+		raw_capacity = 0;
+		for (region_idx = 0; region_idx < dev->num_regions;
+		     region_idx++)
+			if (test_bit(region_idx, &dev->accessible_regions))
+				raw_capacity += dev->regions[0].size;
+	} else {
+		raw_capacity = dev->regions[0].size;
+	}
+
+	dev_info(&dev->sbd.core, "%llu MiB\n", raw_capacity >> 11);
+
+	for (region_idx = 0; region_idx < dev->num_regions; region_idx++) {
+		if (test_bit(region_idx, &dev->accessible_regions) == 0)
+			continue;
+
+		gendisk = alloc_disk(PS3NFLASH_MINORS *
+				     PS3_STORAGE_MAX_REGIONS);
+		if (!gendisk) {
+			dev_err(&dev->sbd.core, "%s:%u: alloc_disk failed\n",
+				__func__, __LINE__);
+			error = -ENOMEM;
+			goto fail_cleanup_queue;
+		}
+
+		priv->gendisk[region_idx] = gendisk;
+		gendisk->major = ps3nflash_major;
+		gendisk->first_minor = devidx * PS3NFLASH_MINORS + region_idx;
+		gendisk->fops = &ps3nflash_fops;
+		gendisk->queue = queue;
+		gendisk->private_data = dev;
+		gendisk->driverfs_dev = &dev->sbd.core;
+
+		snprintf(gendisk->disk_name, sizeof(gendisk->disk_name),
+			 PS3NFLASH_NAME, '1' + devidx, 'a' + region_idx);
+
+		priv->blocking_factor = dev->blk_size >> 9;
+		set_capacity(gendisk, dev->regions[region_idx].size *
+				      priv->blocking_factor);
+
+		dev_info(&dev->sbd.core,
+			 "%s (%lu MiB region)\n",
+			 gendisk->disk_name, get_capacity(gendisk) >> 11);
+
+		add_disk(gendisk);
+	}
+
+	return 0;
+
+fail_cleanup_queue:
+	for (region_idx = 0; region_idx < dev->num_regions; region_idx++)
+		if (priv->gendisk[region_idx])
+			del_gendisk(priv->gendisk[region_idx]);
+
+	blk_cleanup_queue(queue);
+fail_teardown:
+	ps3stor_teardown(dev);
+fail_free_bounce:
+	kfree(dev->bounce_buf);
+fail_free_priv:
+	kfree(priv);
+	ps3_system_bus_set_drvdata(_dev, NULL);
+fail:
+	mutex_lock(&ps3nflash_mask_mutex);
+	__clear_bit(devidx, &ps3nflash_mask);
+	mutex_unlock(&ps3nflash_mask_mutex);
+	return error;
+}
+
+static int ps3nflash_remove(struct ps3_system_bus_device *_dev)
+{
+	struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
+	struct ps3nflash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
+	unsigned int region_idx;
+
+	mutex_lock(&ps3nflash_mask_mutex);
+	__clear_bit(priv->devidx, &ps3nflash_mask);
+	mutex_unlock(&ps3nflash_mask_mutex);
+
+	for (region_idx = 0; region_idx < dev->num_regions; region_idx++) {
+		if (test_bit(region_idx, &dev->accessible_regions) == 0)
+			continue;
+
+		del_gendisk(priv->gendisk[region_idx]);
+	}
+
+	blk_cleanup_queue(priv->queue);
+
+	for (region_idx = 0; region_idx < dev->num_regions; region_idx++) {
+		if (test_bit(region_idx, &dev->accessible_regions) == 0)
+			continue;
+
+		put_disk(priv->gendisk[region_idx]);
+	}
+
+	ps3stor_teardown(dev);
+	kfree(dev->bounce_buf);
+	kfree(priv);
+	ps3_system_bus_set_drvdata(_dev, NULL);
+	return 0;
+}
+
+static struct ps3_system_bus_driver ps3nflash = {
+	.match_id	= PS3_MATCH_ID_STOR_NFLASH,
+	.core.name	= DEVICE_NAME,
+	.core.owner	= THIS_MODULE,
+	.probe		= ps3nflash_probe,
+	.remove		= ps3nflash_remove,
+	.shutdown	= ps3nflash_remove,
+};
+
+
+static int __init ps3nflash_init(void)
+{
+	int error;
+
+	if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
+		return -ENODEV;
+
+	error = register_blkdev(0, DEVICE_NAME);
+	if (error <= 0) {
+		printk(KERN_ERR "%s:%u: register_blkdev failed %d\n", __func__,
+		       __LINE__, error);
+		return error;
+	}
+	ps3nflash_major = error;
+
+	pr_info("%s:%u: registered block device major %d\n", __func__,
+		__LINE__, ps3nflash_major);
+
+	error = ps3_system_bus_driver_register(&ps3nflash);
+	if (error)
+		unregister_blkdev(ps3nflash_major, DEVICE_NAME);
+
+	return error;
+}
+
+static void __exit ps3nflash_exit(void)
+{
+	ps3_system_bus_driver_unregister(&ps3nflash);
+	unregister_blkdev(ps3nflash_major, DEVICE_NAME);
+}
+
+module_init(ps3nflash_init);
+module_exit(ps3nflash_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("PS3 NOR FLASH Storage Driver");
+MODULE_AUTHOR("Andre Heider");
+MODULE_ALIAS(PS3_MODULE_ALIAS_STOR_NFLASH);
-- 
1.7.5.4

^ permalink raw reply related

* [PATCH 14/15] ps3: Add a vflash driver for lpars other than OtherOS
From: Andre Heider @ 2011-08-01 20:03 UTC (permalink / raw)
  To: Geoff Levand; +Cc: cbe-oss-dev, Hector Martin, linuxppc-dev
In-Reply-To: <1312228986-32307-1-git-send-email-a.heider@gmail.com>

This driver refuses to work on OtherOS, and hence complements the
ps3flash driver - which only works on OtherOS.
A gendisk for each accessible region is created, and a default set
of region flags is provided - overwritable via a module param array.

Signed-off-by: Andre Heider <a.heider@gmail.com>
---
 arch/powerpc/platforms/ps3/Kconfig |   16 +-
 drivers/block/Makefile             |    1 +
 drivers/block/ps3vflash.c          |  508 ++++++++++++++++++++++++++++++++++++
 3 files changed, 524 insertions(+), 1 deletions(-)
 create mode 100644 drivers/block/ps3vflash.c

diff --git a/arch/powerpc/platforms/ps3/Kconfig b/arch/powerpc/platforms/ps3/Kconfig
index 5eb956a..f8d865c 100644
--- a/arch/powerpc/platforms/ps3/Kconfig
+++ b/arch/powerpc/platforms/ps3/Kconfig
@@ -121,13 +121,27 @@ config PS3_FLASH
 
 	  This support is required to access the PS3 FLASH ROM, which
 	  contains the boot loader and some boot options.
-	  This driver only supports the deprecated OtherOS LPAR.
+	  This driver only supports the deprecated OtherOS LPAR, select
+	  PS3_VFLASH below for all other LPARs.
 	  In general, all users will say Y or M.
 
 	  As this driver needs a fixed buffer of 256 KiB of memory, it can
 	  be disabled on the kernel command line using "ps3flash=off", to
 	  not allocate this fixed buffer.
 
+config PS3_VFLASH
+	tristate "PS3 VFLASH Storage Driver"
+	depends on PPC_PS3 && BLOCK
+	select PS3_STORAGE
+	help
+	  Include support for the PS3 virtual FLASH Storage.
+
+	  This support is required to access the PS3 virtual FLASH ROM, which
+	  contains the boot loader and some boot options.
+	  This driver only supports LPARs other than the deprecated OtherOS,
+	  select PS3_FLASH above to add support for FLASH ROM for the
+	  OtherOS LPAR.
+
 config PS3_VRAM
 	tristate "PS3 Video RAM Storage Driver"
 	depends on FB_PS3=y && BLOCK && m
diff --git a/drivers/block/Makefile b/drivers/block/Makefile
index 40528ba..8b02899 100644
--- a/drivers/block/Makefile
+++ b/drivers/block/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_BLK_DEV_SWIM)	+= swim_mod.o
 obj-$(CONFIG_BLK_DEV_FD)	+= floppy.o
 obj-$(CONFIG_AMIGA_FLOPPY)	+= amiflop.o
 obj-$(CONFIG_PS3_DISK)		+= ps3disk.o
+obj-$(CONFIG_PS3_VFLASH)	+= ps3vflash.o
 obj-$(CONFIG_PS3_VRAM)		+= ps3vram.o
 obj-$(CONFIG_ATARI_FLOPPY)	+= ataflop.o
 obj-$(CONFIG_AMIGA_Z2RAM)	+= z2ram.o
diff --git a/drivers/block/ps3vflash.c b/drivers/block/ps3vflash.c
new file mode 100644
index 0000000..a59e9f7
--- /dev/null
+++ b/drivers/block/ps3vflash.c
@@ -0,0 +1,508 @@
+/*
+ * PS3 VFLASH Storage Driver
+ *
+ * Copyright (C) 2011 Andre Heider <a.heider@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/ata.h>
+#include <linux/blkdev.h>
+#include <linux/slab.h>
+
+#include <asm/lv1call.h>
+#include <asm/ps3stor.h>
+#include <asm/firmware.h>
+
+
+#define DEVICE_NAME		"ps3vflash"
+
+#define BOUNCE_SIZE		(64*1024)
+
+#define PS3VFLASH_MAX_DISKS	16
+#define PS3VFLASH_MINORS	16
+
+
+#define PS3VFLASH_NAME		"ps3v%c%c"
+
+
+static unsigned int region_flags[] = {
+	PS3_STORAGE_FLAG_SKIP_ACL | PS3_STORAGE_FLAG_ALT_KEY,	/* raw vflash */
+	PS3_STORAGE_FLAG_SKIP_ACL,				/* lpar 1 */
+	PS3_STORAGE_FLAG_ALT_KEY,				/* dev_flash */
+	PS3_STORAGE_FLAG_ALT_KEY,				/* dev_flash2 */
+	PS3_STORAGE_FLAG_ALT_KEY,				/* dev_flash3 */
+	PS3_STORAGE_FLAG_DEFAULT,				/* otheros */
+	PS3_STORAGE_FLAG_SKIP_ACL,				/* unknown */
+	PS3_STORAGE_FLAG_DEFAULT,				/* n/a */
+};
+module_param_array(region_flags, uint, NULL, S_IRUGO);
+MODULE_PARM_DESC(region_flags, "Region flags");
+
+struct ps3vflash_private {
+	spinlock_t lock;		/* Request queue spinlock */
+	struct request_queue *queue;
+	unsigned int blocking_factor;
+	struct request *req;
+	unsigned int devidx;
+	struct gendisk *gendisk[0];	/* Must be last */
+};
+
+
+#define LV1_STORAGE_ATA_FLUSH_CACHE	(0x31)
+
+static int ps3vflash_major;
+
+
+static const struct block_device_operations ps3vflash_fops = {
+	.owner		= THIS_MODULE,
+};
+
+
+static void ps3vflash_scatter_gather(struct ps3_storage_device *dev,
+				     struct request *req, int gather)
+{
+	unsigned int offset = 0;
+	struct req_iterator iter;
+	struct bio_vec *bvec;
+	unsigned int i = 0;
+	size_t size;
+	void *buf;
+
+	rq_for_each_segment(bvec, req, iter) {
+		unsigned long flags;
+		dev_dbg(&dev->sbd.core,
+			"%s:%u: bio %u: %u segs %u sectors from %lu\n",
+			__func__, __LINE__, i, bio_segments(iter.bio),
+			bio_sectors(iter.bio), iter.bio->bi_sector);
+
+		size = bvec->bv_len;
+		buf = bvec_kmap_irq(bvec, &flags);
+		if (gather)
+			memcpy(dev->bounce_buf+offset, buf, size);
+		else
+			memcpy(buf, dev->bounce_buf+offset, size);
+		offset += size;
+		flush_kernel_dcache_page(bvec->bv_page);
+		bvec_kunmap_irq(buf, &flags);
+		i++;
+	}
+}
+
+static int ps3vflash_submit_request_sg(struct ps3_storage_device *dev,
+				       struct request *req)
+{
+	struct ps3vflash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
+	int write = rq_data_dir(req), res;
+	const char *op = write ? "write" : "read";
+	u64 start_sector, sectors;
+	unsigned int region_idx = MINOR(disk_devt(req->rq_disk)) &
+				  (PS3VFLASH_MINORS - 1);
+	unsigned int region_id = dev->regions[region_idx].id;
+	u64 flags = dev->regions[region_idx].flags;
+
+#ifdef DEBUG
+	unsigned int n = 0;
+	struct bio_vec *bv;
+	struct req_iterator iter;
+
+	rq_for_each_segment(bv, req, iter)
+		n++;
+	dev_dbg(&dev->sbd.core,
+		"%s:%u: %s req has %u bvecs for %u sectors\n",
+		__func__, __LINE__, op, n, blk_rq_sectors(req));
+#endif
+
+	start_sector = blk_rq_pos(req) * priv->blocking_factor;
+	sectors = blk_rq_sectors(req) * priv->blocking_factor;
+	dev_dbg(&dev->sbd.core, "%s:%u: %s %llu sectors starting at %llu\n",
+		__func__, __LINE__, op, sectors, start_sector);
+
+	if (write) {
+		ps3vflash_scatter_gather(dev, req, 1);
+
+		res = lv1_storage_write(dev->sbd.dev_id, region_id,
+					start_sector, sectors, flags,
+					dev->bounce_lpar, &dev->tag);
+	} else {
+		res = lv1_storage_read(dev->sbd.dev_id, region_id,
+				       start_sector, sectors, flags,
+				       dev->bounce_lpar, &dev->tag);
+	}
+	if (res) {
+		dev_err(&dev->sbd.core, "%s:%u: %s failed %d\n", __func__,
+			__LINE__, op, res);
+		__blk_end_request_all(req, -EIO);
+		return 0;
+	}
+
+	priv->req = req;
+	return 1;
+}
+
+static int ps3vflash_submit_flush_request(struct ps3_storage_device *dev,
+					  struct request *req)
+{
+	struct ps3vflash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
+	u64 res;
+
+	dev_dbg(&dev->sbd.core, "%s:%u: flush request\n", __func__, __LINE__);
+
+	res = lv1_storage_send_device_command(dev->sbd.dev_id,
+					      LV1_STORAGE_ATA_FLUSH_CACHE,
+					      0, 0, 0, 0, &dev->tag);
+	if (res) {
+		dev_err(&dev->sbd.core, "%s:%u: sync cache failed 0x%llx\n",
+			__func__, __LINE__, res);
+		__blk_end_request_all(req, -EIO);
+		return 0;
+	}
+
+	priv->req = req;
+	return 1;
+}
+
+static void ps3vflash_do_request(struct ps3_storage_device *dev,
+				 struct request_queue *q)
+{
+	struct request *req;
+
+	dev_dbg(&dev->sbd.core, "%s:%u\n", __func__, __LINE__);
+
+	while ((req = blk_fetch_request(q))) {
+		if (req->cmd_flags & REQ_FLUSH) {
+			if (ps3vflash_submit_flush_request(dev, req))
+				break;
+		} else if (req->cmd_type == REQ_TYPE_FS) {
+			if (ps3vflash_submit_request_sg(dev, req))
+				break;
+		} else {
+			blk_dump_rq_flags(req, DEVICE_NAME " bad request");
+			__blk_end_request_all(req, -EIO);
+			continue;
+		}
+	}
+}
+
+static void ps3vflash_request(struct request_queue *q)
+{
+	struct ps3_storage_device *dev = q->queuedata;
+	struct ps3vflash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
+
+	if (priv->req) {
+		dev_dbg(&dev->sbd.core, "%s:%u busy\n", __func__, __LINE__);
+		return;
+	}
+
+	ps3vflash_do_request(dev, q);
+}
+
+static irqreturn_t ps3vflash_interrupt(int irq, void *data)
+{
+	struct ps3_storage_device *dev = data;
+	struct ps3vflash_private *priv;
+	struct request *req;
+	int res, read, error;
+	u64 tag, status;
+	const char *op;
+
+	res = lv1_storage_get_async_status(dev->sbd.dev_id, &tag, &status);
+
+	if (tag != dev->tag)
+		dev_err(&dev->sbd.core,
+			"%s:%u: tag mismatch, got %llx, expected %llx\n",
+			__func__, __LINE__, tag, dev->tag);
+
+	if (res) {
+		dev_err(&dev->sbd.core, "%s:%u: res=%d status=0x%llx\n",
+			__func__, __LINE__, res, status);
+		return IRQ_HANDLED;
+	}
+
+	priv = ps3_system_bus_get_drvdata(&dev->sbd);
+	req = priv->req;
+	if (!req) {
+		dev_dbg(&dev->sbd.core,
+			"%s:%u non-block layer request completed\n", __func__,
+			__LINE__);
+		dev->lv1_status = status;
+		complete(&dev->done);
+		return IRQ_HANDLED;
+	}
+
+	if (req->cmd_flags & REQ_FLUSH) {
+		read = 0;
+		op = "flush";
+	} else {
+		read = !rq_data_dir(req);
+		op = read ? "read" : "write";
+	}
+	if (status) {
+		dev_dbg(&dev->sbd.core, "%s:%u: %s failed 0x%llx\n", __func__,
+			__LINE__, op, status);
+		error = -EIO;
+	} else {
+		dev_dbg(&dev->sbd.core, "%s:%u: %s completed\n", __func__,
+			__LINE__, op);
+		error = 0;
+		if (read)
+			ps3vflash_scatter_gather(dev, req, 0);
+	}
+
+	spin_lock(&priv->lock);
+	__blk_end_request_all(req, error);
+	priv->req = NULL;
+	ps3vflash_do_request(dev, priv->queue);
+	spin_unlock(&priv->lock);
+
+	return IRQ_HANDLED;
+}
+
+static unsigned long ps3vflash_mask;
+
+static DEFINE_MUTEX(ps3vflash_mask_mutex);
+
+static int __devinit ps3vflash_probe(struct ps3_system_bus_device *_dev)
+{
+	struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
+	struct ps3vflash_private *priv;
+	int error;
+	unsigned int devidx;
+	struct request_queue *queue;
+	struct gendisk *gendisk;
+	u64 raw_capacity;
+	unsigned int region_idx;
+
+	if (dev->blk_size < 512) {
+		dev_err(&dev->sbd.core,
+			"%s:%u: cannot handle block size %llu\n", __func__,
+			__LINE__, dev->blk_size);
+		return -EINVAL;
+	}
+
+	BUILD_BUG_ON(PS3VFLASH_MAX_DISKS > BITS_PER_LONG);
+	mutex_lock(&ps3vflash_mask_mutex);
+	devidx = find_first_zero_bit(&ps3vflash_mask, PS3VFLASH_MAX_DISKS);
+	if (devidx >= PS3VFLASH_MAX_DISKS) {
+		dev_err(&dev->sbd.core, "%s:%u: Too many disks\n", __func__,
+			__LINE__);
+		mutex_unlock(&ps3vflash_mask_mutex);
+		return -ENOSPC;
+	}
+	__set_bit(devidx, &ps3vflash_mask);
+	mutex_unlock(&ps3vflash_mask_mutex);
+
+	priv = kzalloc(sizeof(*priv) +
+		       dev->num_regions * sizeof(struct gendisk),
+		       GFP_KERNEL);
+	if (!priv) {
+		error = -ENOMEM;
+		goto fail;
+	}
+
+	ps3_system_bus_set_drvdata(_dev, priv);
+	spin_lock_init(&priv->lock);
+
+	dev->bounce_size = BOUNCE_SIZE;
+	dev->bounce_buf = kmalloc(BOUNCE_SIZE, GFP_DMA);
+	if (!dev->bounce_buf) {
+		error = -ENOMEM;
+		goto fail_free_priv;
+	}
+
+	for (region_idx = 0; region_idx < dev->num_regions; region_idx++)
+		dev->regions[region_idx].flags = region_flags[region_idx];
+
+	error = ps3stor_setup(dev, ps3vflash_interrupt);
+	if (error)
+		goto fail_free_bounce;
+
+	priv->devidx = devidx;
+
+	queue = blk_init_queue(ps3vflash_request, &priv->lock);
+	if (!queue) {
+		dev_err(&dev->sbd.core, "%s:%u: blk_init_queue failed\n",
+			__func__, __LINE__);
+		error = -ENOMEM;
+		goto fail_teardown;
+	}
+
+	priv->queue = queue;
+	queue->queuedata = dev;
+
+	blk_queue_bounce_limit(queue, BLK_BOUNCE_HIGH);
+
+	blk_queue_max_hw_sectors(queue, dev->bounce_size >> 9);
+	blk_queue_segment_boundary(queue, -1UL);
+	blk_queue_dma_alignment(queue, dev->blk_size-1);
+	blk_queue_logical_block_size(queue, dev->blk_size);
+
+	blk_queue_flush(queue, REQ_FLUSH);
+
+	blk_queue_max_segments(queue, -1);
+	blk_queue_max_segment_size(queue, dev->bounce_size);
+
+	if (test_bit(0, &dev->accessible_regions) == 0) {
+		raw_capacity = 0;
+		for (region_idx = 0; region_idx < dev->num_regions;
+		     region_idx++)
+			if (test_bit(region_idx, &dev->accessible_regions))
+				raw_capacity += dev->regions[0].size;
+	} else {
+		raw_capacity = dev->regions[0].size;
+	}
+
+	dev_info(&dev->sbd.core, "%llu MiB\n", raw_capacity >> 11);
+
+	for (region_idx = 0; region_idx < dev->num_regions; region_idx++) {
+		if (test_bit(region_idx, &dev->accessible_regions) == 0)
+			continue;
+
+		gendisk = alloc_disk(PS3VFLASH_MINORS *
+				     PS3_STORAGE_MAX_REGIONS);
+		if (!gendisk) {
+			dev_err(&dev->sbd.core, "%s:%u: alloc_disk failed\n",
+				__func__, __LINE__);
+			error = -ENOMEM;
+			goto fail_cleanup_queue;
+		}
+
+		priv->gendisk[region_idx] = gendisk;
+		gendisk->major = ps3vflash_major;
+		gendisk->first_minor = devidx * PS3VFLASH_MINORS + region_idx;
+		gendisk->fops = &ps3vflash_fops;
+		gendisk->queue = queue;
+		gendisk->private_data = dev;
+		gendisk->driverfs_dev = &dev->sbd.core;
+
+		snprintf(gendisk->disk_name, sizeof(gendisk->disk_name),
+			 PS3VFLASH_NAME, '1' + devidx, 'a' + region_idx);
+
+		priv->blocking_factor = dev->blk_size >> 9;
+		set_capacity(gendisk, dev->regions[region_idx].size *
+				      priv->blocking_factor);
+
+		dev_info(&dev->sbd.core,
+			 "%s (%lu MiB region)\n",
+			 gendisk->disk_name, get_capacity(gendisk) >> 11);
+
+		add_disk(gendisk);
+	}
+
+	return 0;
+
+fail_cleanup_queue:
+	for (region_idx = 0; region_idx < dev->num_regions; region_idx++)
+		if (priv->gendisk[region_idx])
+			del_gendisk(priv->gendisk[region_idx]);
+
+	blk_cleanup_queue(queue);
+fail_teardown:
+	ps3stor_teardown(dev);
+fail_free_bounce:
+	kfree(dev->bounce_buf);
+fail_free_priv:
+	kfree(priv);
+	ps3_system_bus_set_drvdata(_dev, NULL);
+fail:
+	mutex_lock(&ps3vflash_mask_mutex);
+	__clear_bit(devidx, &ps3vflash_mask);
+	mutex_unlock(&ps3vflash_mask_mutex);
+	return error;
+}
+
+static int ps3vflash_remove(struct ps3_system_bus_device *_dev)
+{
+	struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
+	struct ps3vflash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
+	unsigned int region_idx;
+
+	mutex_lock(&ps3vflash_mask_mutex);
+	__clear_bit(priv->devidx, &ps3vflash_mask);
+	mutex_unlock(&ps3vflash_mask_mutex);
+
+	for (region_idx = 0; region_idx < dev->num_regions; region_idx++) {
+		if (test_bit(region_idx, &dev->accessible_regions) == 0)
+			continue;
+
+		del_gendisk(priv->gendisk[region_idx]);
+	}
+
+	blk_cleanup_queue(priv->queue);
+
+	for (region_idx = 0; region_idx < dev->num_regions; region_idx++) {
+		if (test_bit(region_idx, &dev->accessible_regions) == 0)
+			continue;
+
+		put_disk(priv->gendisk[region_idx]);
+	}
+
+	ps3stor_teardown(dev);
+	kfree(dev->bounce_buf);
+	kfree(priv);
+	ps3_system_bus_set_drvdata(_dev, NULL);
+	return 0;
+}
+
+static struct ps3_system_bus_driver ps3vflash = {
+	.match_id	= PS3_MATCH_ID_STOR_FLASH,
+	.core.name	= DEVICE_NAME,
+	.core.owner	= THIS_MODULE,
+	.probe		= ps3vflash_probe,
+	.remove		= ps3vflash_remove,
+	.shutdown	= ps3vflash_remove,
+};
+
+
+static int __init ps3vflash_init(void)
+{
+	int error;
+
+	if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
+		return -ENODEV;
+
+	if (ps3_get_ss_laid() == PS3_SS_LAID_OTHEROS)
+		return -ENODEV;
+
+	error = register_blkdev(0, DEVICE_NAME);
+	if (error <= 0) {
+		printk(KERN_ERR "%s:%u: register_blkdev failed %d\n", __func__,
+		       __LINE__, error);
+		return error;
+	}
+	ps3vflash_major = error;
+
+	pr_info("%s:%u: registered block device major %d\n", __func__,
+		__LINE__, ps3vflash_major);
+
+	error = ps3_system_bus_driver_register(&ps3vflash);
+	if (error)
+		unregister_blkdev(ps3vflash_major, DEVICE_NAME);
+
+	return error;
+}
+
+static void __exit ps3vflash_exit(void)
+{
+	ps3_system_bus_driver_unregister(&ps3vflash);
+	unregister_blkdev(ps3vflash_major, DEVICE_NAME);
+}
+
+module_init(ps3vflash_init);
+module_exit(ps3vflash_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("PS3 VFLASH Storage Driver");
+MODULE_AUTHOR("Andre Heider");
+MODULE_ALIAS(PS3_MODULE_ALIAS_STOR_FLASH);
-- 
1.7.5.4

^ permalink raw reply related

* [PATCH 13/15] ps3disk: Use region flags
From: Andre Heider @ 2011-08-01 20:03 UTC (permalink / raw)
  To: Geoff Levand; +Cc: cbe-oss-dev, Hector Martin, linuxppc-dev
In-Reply-To: <1312228986-32307-1-git-send-email-a.heider@gmail.com>

Provide a set of default region flags and make them overwritable via
a module parameter array.
Set PS3_STORAGE_FLAG_SKIP_ACL for region 0, so it can be accessed
from the GameOS lpar.

Signed-off-by: Andre Heider <a.heider@gmail.com>
---
 drivers/block/ps3disk.c |   16 ++++++++++++++++
 1 files changed, 16 insertions(+), 0 deletions(-)

diff --git a/drivers/block/ps3disk.c b/drivers/block/ps3disk.c
index 30dae10..483806e 100644
--- a/drivers/block/ps3disk.c
+++ b/drivers/block/ps3disk.c
@@ -39,6 +39,19 @@
 #define PS3DISK_NAME		"ps3d%c%c"
 
 
+static unsigned int region_flags[] = {
+	PS3_STORAGE_FLAG_SKIP_ACL,		/* raw disk */
+	PS3_STORAGE_FLAG_DEFAULT,		/* core os */
+	PS3_STORAGE_FLAG_DEFAULT,		/* gameos pup */
+	PS3_STORAGE_FLAG_DEFAULT,		/* n/a */
+	PS3_STORAGE_FLAG_DEFAULT,		/* n/a */
+	PS3_STORAGE_FLAG_DEFAULT,		/* n/a */
+	PS3_STORAGE_FLAG_DEFAULT,		/* n/a */
+	PS3_STORAGE_FLAG_DEFAULT,		/* n/a */
+};
+module_param_array(region_flags, uint, NULL, S_IRUGO);
+MODULE_PARM_DESC(region_flags, "Region flags");
+
 struct ps3disk_private {
 	spinlock_t lock;		/* Request queue spinlock */
 	struct request_queue *queue;
@@ -453,6 +466,9 @@ static int __devinit ps3disk_probe(struct ps3_system_bus_device *_dev)
 		goto fail_free_priv;
 	}
 
+	for (region_idx = 0; region_idx < dev->num_regions; region_idx++)
+		dev->regions[region_idx].flags = region_flags[region_idx];
+
 	error = ps3stor_setup(dev, ps3disk_interrupt);
 	if (error)
 		goto fail_free_bounce;
-- 
1.7.5.4

^ permalink raw reply related

* [PATCH 12/15] ps3stor_lib: Add support for storage access flags
From: Andre Heider @ 2011-08-01 20:03 UTC (permalink / raw)
  To: Geoff Levand; +Cc: cbe-oss-dev, Hector Martin, linuxppc-dev
In-Reply-To: <1312228986-32307-1-git-send-email-a.heider@gmail.com>

Users can now set the access flags in the region struct. This is
required for accessing the first region, or selecting an alternative
decryption key for the vflash partitions.

Signed-off-by: Andre Heider <a.heider@gmail.com>
---
 arch/powerpc/include/asm/ps3stor.h       |    8 +++++++-
 arch/powerpc/platforms/ps3/device-init.c |    1 +
 drivers/block/ps3disk.c                  |    5 +++--
 drivers/ps3/ps3stor_lib.c                |    5 +++--
 4 files changed, 14 insertions(+), 5 deletions(-)

diff --git a/arch/powerpc/include/asm/ps3stor.h b/arch/powerpc/include/asm/ps3stor.h
index 9871c05..f29aa37 100644
--- a/arch/powerpc/include/asm/ps3stor.h
+++ b/arch/powerpc/include/asm/ps3stor.h
@@ -25,12 +25,18 @@
 
 #include <asm/ps3.h>
 
-#define PS3_STORAGE_MAX_REGIONS		(8)
+#define PS3_STORAGE_MAX_REGIONS			(8)
+
+#define PS3_STORAGE_FLAG_DEFAULT		(0)
+#define PS3_STORAGE_FLAG_SKIP_ACL		(1 << 1)
+#define PS3_STORAGE_FLAG_ALT_KEY		(1 << 2)
+#define PS3_STORAGE_FLAG_UNENCRYPTED	(1 << 5)
 
 struct ps3_storage_region {
 	unsigned int id;
 	u64 start;
 	u64 size;
+	u64 flags;
 };
 
 struct ps3_storage_device {
diff --git a/arch/powerpc/platforms/ps3/device-init.c b/arch/powerpc/platforms/ps3/device-init.c
index 830d741..7a3dbf8 100644
--- a/arch/powerpc/platforms/ps3/device-init.c
+++ b/arch/powerpc/platforms/ps3/device-init.c
@@ -409,6 +409,7 @@ static int ps3_setup_storage_dev(const struct ps3_repository_device *repo,
 		p->regions[i].id = id;
 		p->regions[i].start = start;
 		p->regions[i].size = size;
+		p->regions[i].flags = PS3_STORAGE_FLAG_DEFAULT;
 	}
 
 	result = ps3_system_bus_device_register(&p->sbd);
diff --git a/drivers/block/ps3disk.c b/drivers/block/ps3disk.c
index cba8b45..30dae10 100644
--- a/drivers/block/ps3disk.c
+++ b/drivers/block/ps3disk.c
@@ -130,6 +130,7 @@ static int ps3disk_submit_request_sg(struct ps3_storage_device *dev,
 	unsigned int region_idx = MINOR(disk_devt(req->rq_disk)) &
 				  (PS3DISK_MINORS - 1);
 	unsigned int region_id = dev->regions[region_idx].id;
+	u64 flags = dev->regions[region_idx].flags;
 
 #ifdef DEBUG
 	unsigned int n = 0;
@@ -152,11 +153,11 @@ static int ps3disk_submit_request_sg(struct ps3_storage_device *dev,
 		ps3disk_scatter_gather(dev, req, 1);
 
 		res = lv1_storage_write(dev->sbd.dev_id, region_id,
-					start_sector, sectors, 0,
+					start_sector, sectors, flags,
 					dev->bounce_lpar, &dev->tag);
 	} else {
 		res = lv1_storage_read(dev->sbd.dev_id, region_id,
-				       start_sector, sectors, 0,
+				       start_sector, sectors, flags,
 				       dev->bounce_lpar, &dev->tag);
 	}
 	if (res) {
diff --git a/drivers/ps3/ps3stor_lib.c b/drivers/ps3/ps3stor_lib.c
index 5bbc023..8bb54ac 100644
--- a/drivers/ps3/ps3stor_lib.c
+++ b/drivers/ps3/ps3stor_lib.c
@@ -275,6 +275,7 @@ u64 ps3stor_read_write_sectors(struct ps3_storage_device *dev,
 			       u64 start_sector, u64 sectors, int write)
 {
 	unsigned int region_id = dev->regions[region_idx].id;
+	u64 flags = dev->regions[region_idx].flags;
 	const char *op = write ? "write" : "read";
 	int res;
 
@@ -283,10 +284,10 @@ u64 ps3stor_read_write_sectors(struct ps3_storage_device *dev,
 
 	init_completion(&dev->done);
 	res = write ? lv1_storage_write(dev->sbd.dev_id, region_id,
-					start_sector, sectors, 0, lpar,
+					start_sector, sectors, flags, lpar,
 					&dev->tag)
 		    : lv1_storage_read(dev->sbd.dev_id, region_id,
-				       start_sector, sectors, 0, lpar,
+				       start_sector, sectors, flags, lpar,
 				       &dev->tag);
 	if (res) {
 		dev_dbg(&dev->sbd.core, "%s:%u: %s failed %d\n", __func__,
-- 
1.7.5.4

^ permalink raw reply related

* [PATCH 11/15] ps3disk: Provide a gendisk per accessible region
From: Andre Heider @ 2011-08-01 20:03 UTC (permalink / raw)
  To: Geoff Levand; +Cc: cbe-oss-dev, Hector Martin, linuxppc-dev
In-Reply-To: <1312228986-32307-1-git-send-email-a.heider@gmail.com>

This changes the behavior to name the block devices for lpars
other than OtherOS. Instead of a single disk with an alphanumeric
suffix (/dev/ps3da), disks are now numeric, while each
accessible region gets its own alphanumeric suffix:

/dev/ps3d1a
/dev/ps3d1b

The old behavior for OtherOS is kept:
- only one region will be exposed as block device
- the block device name stays the same

Signed-off-by: Andre Heider <a.heider@gmail.com>
---
 drivers/block/ps3disk.c |  118 ++++++++++++++++++++++++++++++----------------
 1 files changed, 77 insertions(+), 41 deletions(-)

diff --git a/drivers/block/ps3disk.c b/drivers/block/ps3disk.c
index 96e00ff..cba8b45 100644
--- a/drivers/block/ps3disk.c
+++ b/drivers/block/ps3disk.c
@@ -35,18 +35,19 @@
 #define PS3DISK_MINORS		16
 
 
-#define PS3DISK_NAME		"ps3d%c"
+#define PS3DISK_NAME_OTHEROS	"ps3d%c"
+#define PS3DISK_NAME		"ps3d%c%c"
 
 
 struct ps3disk_private {
 	spinlock_t lock;		/* Request queue spinlock */
 	struct request_queue *queue;
-	struct gendisk *gendisk;
-	unsigned int region_idx;	/* first accessible region */
 	unsigned int blocking_factor;
 	struct request *req;
+	unsigned int devidx;
 	u64 raw_capacity;
 	unsigned char model[ATA_ID_PROD_LEN+1];
+	struct gendisk *gendisk[0];	/* Must be last */
 };
 
 
@@ -126,7 +127,9 @@ static int ps3disk_submit_request_sg(struct ps3_storage_device *dev,
 	int write = rq_data_dir(req), res;
 	const char *op = write ? "write" : "read";
 	u64 start_sector, sectors;
-	unsigned int region_id = dev->regions[priv->region_idx].id;
+	unsigned int region_idx = MINOR(disk_devt(req->rq_disk)) &
+				  (PS3DISK_MINORS - 1);
+	unsigned int region_id = dev->regions[region_idx].id;
 
 #ifdef DEBUG
 	unsigned int n = 0;
@@ -410,6 +413,7 @@ static int __devinit ps3disk_probe(struct ps3_system_bus_device *_dev)
 	struct request_queue *queue;
 	struct gendisk *gendisk;
 	unsigned int region_idx;
+	unsigned int otheros = ps3_get_ss_laid() == PS3_SS_LAID_OTHEROS;
 
 	if (dev->blk_size < 512) {
 		dev_err(&dev->sbd.core,
@@ -430,7 +434,9 @@ static int __devinit ps3disk_probe(struct ps3_system_bus_device *_dev)
 	__set_bit(devidx, &ps3disk_mask);
 	mutex_unlock(&ps3disk_mask_mutex);
 
-	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	priv = kzalloc(sizeof(*priv) +
+		       dev->num_regions * sizeof(struct gendisk),
+		       GFP_KERNEL);
 	if (!priv) {
 		error = -ENOMEM;
 		goto fail;
@@ -450,6 +456,7 @@ static int __devinit ps3disk_probe(struct ps3_system_bus_device *_dev)
 	if (error)
 		goto fail_free_bounce;
 
+	priv->devidx = devidx;
 	ps3disk_identify(dev);
 
 	queue = blk_init_queue(ps3disk_request, &priv->lock);
@@ -475,45 +482,60 @@ static int __devinit ps3disk_probe(struct ps3_system_bus_device *_dev)
 	blk_queue_max_segments(queue, -1);
 	blk_queue_max_segment_size(queue, dev->bounce_size);
 
-	gendisk = alloc_disk(PS3DISK_MINORS);
-	if (!gendisk) {
-		dev_err(&dev->sbd.core, "%s:%u: alloc_disk failed\n", __func__,
-			__LINE__);
-		error = -ENOMEM;
-		goto fail_cleanup_queue;
-	}
+	dev_info(&dev->sbd.core, "%s (%llu MiB)\n",
+		 priv->model, priv->raw_capacity >> 11);
 
-	priv->gendisk = gendisk;
+	for (region_idx = 0; region_idx < dev->num_regions; region_idx++) {
+		if (test_bit(region_idx, &dev->accessible_regions) == 0)
+			continue;
 
-	/* find first accessible region */
-	for (region_idx = 0; region_idx < dev->num_regions; region_idx++)
-		if (test_bit(region_idx, &dev->accessible_regions)) {
-			priv->region_idx = region_idx;
-			break;
+		gendisk = alloc_disk(PS3DISK_MINORS * PS3_STORAGE_MAX_REGIONS);
+		if (!gendisk) {
+			dev_err(&dev->sbd.core, "%s:%u: alloc_disk failed\n",
+				__func__, __LINE__);
+			error = -ENOMEM;
+			goto fail_cleanup_queue;
 		}
 
-	gendisk->major = ps3disk_major;
-	gendisk->first_minor = devidx * PS3DISK_MINORS;
-	gendisk->fops = &ps3disk_fops;
-	gendisk->queue = queue;
-	gendisk->private_data = dev;
-	gendisk->driverfs_dev = &dev->sbd.core;
-	snprintf(gendisk->disk_name, sizeof(gendisk->disk_name), PS3DISK_NAME,
-		 devidx+'a');
-	priv->blocking_factor = dev->blk_size >> 9;
-	set_capacity(gendisk,
-		     dev->regions[priv->region_idx].size *
-				 priv->blocking_factor);
-
-	dev_info(&dev->sbd.core,
-		 "%s is a %s (%llu MiB total, %lu MiB for OtherOS)\n",
-		 gendisk->disk_name, priv->model, priv->raw_capacity >> 11,
-		 get_capacity(gendisk) >> 11);
-
-	add_disk(gendisk);
+		priv->gendisk[region_idx] = gendisk;
+		gendisk->major = ps3disk_major;
+		gendisk->first_minor = devidx * PS3DISK_MINORS + region_idx;
+		gendisk->fops = &ps3disk_fops;
+		gendisk->queue = queue;
+		gendisk->private_data = dev;
+		gendisk->driverfs_dev = &dev->sbd.core;
+
+		if (otheros) {
+			/* keep the old block device name for OtherOS */
+			snprintf(gendisk->disk_name, sizeof(gendisk->disk_name),
+				 PS3DISK_NAME_OTHEROS, 'a' + devidx);
+		} else {
+			snprintf(gendisk->disk_name, sizeof(gendisk->disk_name),
+				 PS3DISK_NAME, '1' + devidx, 'a' + region_idx);
+		}
+
+		priv->blocking_factor = dev->blk_size >> 9;
+		set_capacity(gendisk, dev->regions[region_idx].size *
+				      priv->blocking_factor);
+
+		dev_info(&dev->sbd.core,
+			 "%s (%lu MiB region)\n",
+			 gendisk->disk_name, get_capacity(gendisk) >> 11);
+
+		add_disk(gendisk);
+
+		/* keep old behavior for OtherOS - only one region */
+		if (otheros)
+			break;
+	}
+
 	return 0;
 
 fail_cleanup_queue:
+	for (region_idx = 0; region_idx < dev->num_regions; region_idx++)
+		if (priv->gendisk[region_idx])
+			del_gendisk(priv->gendisk[region_idx]);
+
 	blk_cleanup_queue(queue);
 fail_teardown:
 	ps3stor_teardown(dev);
@@ -533,14 +555,28 @@ static int ps3disk_remove(struct ps3_system_bus_device *_dev)
 {
 	struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
 	struct ps3disk_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
+	unsigned int region_idx;
 
 	mutex_lock(&ps3disk_mask_mutex);
-	__clear_bit(MINOR(disk_devt(priv->gendisk)) / PS3DISK_MINORS,
-		    &ps3disk_mask);
+	__clear_bit(priv->devidx, &ps3disk_mask);
 	mutex_unlock(&ps3disk_mask_mutex);
-	del_gendisk(priv->gendisk);
+
+	for (region_idx = 0; region_idx < dev->num_regions; region_idx++) {
+		if (test_bit(region_idx, &dev->accessible_regions) == 0)
+			continue;
+
+		del_gendisk(priv->gendisk[region_idx]);
+	}
+
 	blk_cleanup_queue(priv->queue);
-	put_disk(priv->gendisk);
+
+	for (region_idx = 0; region_idx < dev->num_regions; region_idx++) {
+		if (test_bit(region_idx, &dev->accessible_regions) == 0)
+			continue;
+
+		put_disk(priv->gendisk[region_idx]);
+	}
+
 	dev_notice(&dev->sbd.core, "Synchronizing disk cache\n");
 	ps3disk_sync_cache(dev);
 	ps3stor_teardown(dev);
-- 
1.7.5.4

^ permalink raw reply related

* [PATCH 10/15] ps3stor_lib: Add support for multiple regions
From: Andre Heider @ 2011-08-01 20:03 UTC (permalink / raw)
  To: Geoff Levand; +Cc: cbe-oss-dev, Hector Martin, linuxppc-dev
In-Reply-To: <1312228986-32307-1-git-send-email-a.heider@gmail.com>

Users (ps3disk, ps3flash and ps3rom) retain the old behavior. That is:
they still only provide access to the first accessible region.

Signed-off-by: Andre Heider <a.heider@gmail.com>
---
 arch/powerpc/include/asm/ps3stor.h |    4 ++--
 drivers/block/ps3disk.c            |   15 +++++++++++++--
 drivers/char/ps3flash.c            |   23 +++++++++++++++++------
 drivers/ps3/ps3stor_lib.c          |   25 ++++++++++++-------------
 drivers/scsi/ps3rom.c              |   11 +++++++----
 5 files changed, 51 insertions(+), 27 deletions(-)

diff --git a/arch/powerpc/include/asm/ps3stor.h b/arch/powerpc/include/asm/ps3stor.h
index d51e53c..9871c05 100644
--- a/arch/powerpc/include/asm/ps3stor.h
+++ b/arch/powerpc/include/asm/ps3stor.h
@@ -51,7 +51,6 @@ struct ps3_storage_device {
 
 	unsigned int num_regions;
 	unsigned long accessible_regions;
-	unsigned int region_idx;		/* first accessible region */
 	struct ps3_storage_region regions[0];	/* Must be last */
 };
 
@@ -63,7 +62,8 @@ static inline struct ps3_storage_device *to_ps3_storage_device(struct device *de
 extern int ps3stor_setup(struct ps3_storage_device *dev,
 			 irq_handler_t handler);
 extern void ps3stor_teardown(struct ps3_storage_device *dev);
-extern u64 ps3stor_read_write_sectors(struct ps3_storage_device *dev, u64 lpar,
+extern u64 ps3stor_read_write_sectors(struct ps3_storage_device *dev,
+				      unsigned int region_idx, u64 lpar,
 				      u64 start_sector, u64 sectors,
 				      int write);
 extern u64 ps3stor_send_command(struct ps3_storage_device *dev, u64 cmd,
diff --git a/drivers/block/ps3disk.c b/drivers/block/ps3disk.c
index 8e1ce2e..96e00ff 100644
--- a/drivers/block/ps3disk.c
+++ b/drivers/block/ps3disk.c
@@ -42,6 +42,7 @@ struct ps3disk_private {
 	spinlock_t lock;		/* Request queue spinlock */
 	struct request_queue *queue;
 	struct gendisk *gendisk;
+	unsigned int region_idx;	/* first accessible region */
 	unsigned int blocking_factor;
 	struct request *req;
 	u64 raw_capacity;
@@ -125,7 +126,7 @@ static int ps3disk_submit_request_sg(struct ps3_storage_device *dev,
 	int write = rq_data_dir(req), res;
 	const char *op = write ? "write" : "read";
 	u64 start_sector, sectors;
-	unsigned int region_id = dev->regions[dev->region_idx].id;
+	unsigned int region_id = dev->regions[priv->region_idx].id;
 
 #ifdef DEBUG
 	unsigned int n = 0;
@@ -408,6 +409,7 @@ static int __devinit ps3disk_probe(struct ps3_system_bus_device *_dev)
 	unsigned int devidx;
 	struct request_queue *queue;
 	struct gendisk *gendisk;
+	unsigned int region_idx;
 
 	if (dev->blk_size < 512) {
 		dev_err(&dev->sbd.core,
@@ -482,6 +484,14 @@ static int __devinit ps3disk_probe(struct ps3_system_bus_device *_dev)
 	}
 
 	priv->gendisk = gendisk;
+
+	/* find first accessible region */
+	for (region_idx = 0; region_idx < dev->num_regions; region_idx++)
+		if (test_bit(region_idx, &dev->accessible_regions)) {
+			priv->region_idx = region_idx;
+			break;
+		}
+
 	gendisk->major = ps3disk_major;
 	gendisk->first_minor = devidx * PS3DISK_MINORS;
 	gendisk->fops = &ps3disk_fops;
@@ -492,7 +502,8 @@ static int __devinit ps3disk_probe(struct ps3_system_bus_device *_dev)
 		 devidx+'a');
 	priv->blocking_factor = dev->blk_size >> 9;
 	set_capacity(gendisk,
-		     dev->regions[dev->region_idx].size*priv->blocking_factor);
+		     dev->regions[priv->region_idx].size *
+				 priv->blocking_factor);
 
 	dev_info(&dev->sbd.core,
 		 "%s is a %s (%llu MiB total, %lu MiB for OtherOS)\n",
diff --git a/drivers/char/ps3flash.c b/drivers/char/ps3flash.c
index b1e8659..47b1dc7 100644
--- a/drivers/char/ps3flash.c
+++ b/drivers/char/ps3flash.c
@@ -36,6 +36,7 @@
 struct ps3flash_private {
 	struct mutex mutex;	/* Bounce buffer mutex */
 	u64 chunk_sectors;
+	unsigned int region_idx;	/* first accessible region */
 	int tag;		/* Start sector of buffer, -1 if invalid */
 	bool dirty;
 };
@@ -46,7 +47,8 @@ static int ps3flash_read_write_sectors(struct ps3_storage_device *dev,
 				       u64 start_sector, int write)
 {
 	struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
-	u64 res = ps3stor_read_write_sectors(dev, dev->bounce_lpar,
+	u64 res = ps3stor_read_write_sectors(dev, priv->region_idx,
+					     dev->bounce_lpar,
 					     start_sector, priv->chunk_sectors,
 					     write);
 	if (res) {
@@ -98,6 +100,7 @@ static int ps3flash_fetch(struct ps3_storage_device *dev, u64 start_sector)
 static loff_t ps3flash_llseek(struct file *file, loff_t offset, int origin)
 {
 	struct ps3_storage_device *dev = ps3flash_dev;
+	struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
 	loff_t res;
 
 	mutex_lock(&file->f_mapping->host->i_mutex);
@@ -106,7 +109,7 @@ static loff_t ps3flash_llseek(struct file *file, loff_t offset, int origin)
 		offset += file->f_pos;
 		break;
 	case 2:
-		offset += dev->regions[dev->region_idx].size*dev->blk_size;
+		offset += dev->regions[priv->region_idx].size*dev->blk_size;
 		break;
 	}
 	if (offset < 0) {
@@ -136,7 +139,7 @@ static ssize_t ps3flash_read(char __user *userbuf, void *kernelbuf,
 		"%s:%u: Reading %zu bytes at position %lld to U0x%p/K0x%p\n",
 		__func__, __LINE__, count, *pos, userbuf, kernelbuf);
 
-	size = dev->regions[dev->region_idx].size*dev->blk_size;
+	size = dev->regions[priv->region_idx].size*dev->blk_size;
 	if (*pos >= size || !count)
 		return 0;
 
@@ -205,7 +208,7 @@ static ssize_t ps3flash_write(const char __user *userbuf,
 		"%s:%u: Writing %zu bytes at position %lld from U0x%p/K0x%p\n",
 		__func__, __LINE__, count, *pos, userbuf, kernelbuf);
 
-	size = dev->regions[dev->region_idx].size*dev->blk_size;
+	size = dev->regions[priv->region_idx].size*dev->blk_size;
 	if (*pos >= size || !count)
 		return 0;
 
@@ -359,6 +362,7 @@ static int __devinit ps3flash_probe(struct ps3_system_bus_device *_dev)
 	struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
 	struct ps3flash_private *priv;
 	int error;
+	unsigned int region_idx;
 	unsigned long tmp;
 
 	/* use static buffer, kmalloc cannot allocate 256 KiB */
@@ -391,14 +395,21 @@ static int __devinit ps3flash_probe(struct ps3_system_bus_device *_dev)
 	if (error)
 		goto fail_free_priv;
 
-	tmp = dev->regions[dev->region_idx].start*dev->blk_size;
+	/* find first accessible region */
+	for (region_idx = 0; region_idx < dev->num_regions; region_idx++)
+		if (test_bit(region_idx, &dev->accessible_regions)) {
+			priv->region_idx = region_idx;
+			break;
+		}
+
+	tmp = dev->regions[priv->region_idx].start*dev->blk_size;
 	if (tmp % FLASH_BLOCK_SIZE) {
 		dev_err(&dev->sbd.core,
 			"%s:%u region start %lu is not aligned\n", __func__,
 			__LINE__, tmp);
 		return -EINVAL;
 	}
-	tmp = dev->regions[dev->region_idx].size*dev->blk_size;
+	tmp = dev->regions[priv->region_idx].size*dev->blk_size;
 	if (tmp % FLASH_BLOCK_SIZE) {
 		dev_err(&dev->sbd.core,
 			"%s:%u region size %lu is not aligned\n", __func__,
diff --git a/drivers/ps3/ps3stor_lib.c b/drivers/ps3/ps3stor_lib.c
index af0afa1..5bbc023 100644
--- a/drivers/ps3/ps3stor_lib.c
+++ b/drivers/ps3/ps3stor_lib.c
@@ -101,9 +101,8 @@ static int ps3stor_probe_access(struct ps3_storage_device *dev)
 			"%s:%u: checking accessibility of region %u\n",
 			__func__, __LINE__, i);
 
-		dev->region_idx = i;
-		res = ps3stor_read_write_sectors(dev, dev->bounce_lpar, 0, 1,
-						 0);
+		res = ps3stor_read_write_sectors(dev, i, dev->bounce_lpar,
+						 0, 1, 0);
 		if (res) {
 			dev_dbg(&dev->sbd.core, "%s:%u: read failed, "
 				"region %u is not accessible\n", __func__,
@@ -117,6 +116,11 @@ static int ps3stor_probe_access(struct ps3_storage_device *dev)
 
 		/* We can access at least one region */
 		error = 0;
+
+		dev_info(&dev->sbd.core,
+			 "Accessible region found: #%u start %llu size %llu\n",
+			 i, dev->regions[i].start, dev->regions[i].size);
+
 	}
 	if (error)
 		return error;
@@ -124,15 +128,8 @@ static int ps3stor_probe_access(struct ps3_storage_device *dev)
 	n = hweight_long(dev->accessible_regions);
 	if (n > 1)
 		dev_info(&dev->sbd.core,
-			 "%s:%u: %lu accessible regions found. Only the first "
-			 "one will be used\n",
+			 "%s:%u: %lu accessible regions found\n",
 			 __func__, __LINE__, n);
-	dev->region_idx = __ffs(dev->accessible_regions);
-	dev_info(&dev->sbd.core,
-		 "First accessible region has index %u start %llu size %llu\n",
-		 dev->region_idx, dev->regions[dev->region_idx].start,
-		 dev->regions[dev->region_idx].size);
-
 	return 0;
 }
 
@@ -264,6 +261,7 @@ EXPORT_SYMBOL_GPL(ps3stor_teardown);
 /**
  *	ps3stor_read_write_sectors - read/write from/to a storage device
  *	@dev: Pointer to a struct ps3_storage_device
+ *	@region_idx: Index of the region to access
  *	@lpar: HV logical partition address
  *	@start_sector: First sector to read/write
  *	@sectors: Number of sectors to read/write
@@ -272,10 +270,11 @@ EXPORT_SYMBOL_GPL(ps3stor_teardown);
  *	Returns 0 for success, -1 in case of failure to submit the command, or
  *	an LV1 status value in case of other errors
  */
-u64 ps3stor_read_write_sectors(struct ps3_storage_device *dev, u64 lpar,
+u64 ps3stor_read_write_sectors(struct ps3_storage_device *dev,
+			       unsigned int region_idx, u64 lpar,
 			       u64 start_sector, u64 sectors, int write)
 {
-	unsigned int region_id = dev->regions[dev->region_idx].id;
+	unsigned int region_id = dev->regions[region_idx].id;
 	const char *op = write ? "write" : "read";
 	int res;
 
diff --git a/drivers/scsi/ps3rom.c b/drivers/scsi/ps3rom.c
index cd178b9..68db03c 100644
--- a/drivers/scsi/ps3rom.c
+++ b/drivers/scsi/ps3rom.c
@@ -39,6 +39,7 @@
 
 #define PS3ROM_MAX_SECTORS		(BOUNCE_SIZE >> 9)
 
+#define PS3ROM_REGION_IDX		0
 
 struct ps3rom_private {
 	struct ps3_storage_device *dev;
@@ -177,8 +178,9 @@ static int ps3rom_read_request(struct ps3_storage_device *dev,
 		__func__, __LINE__, sectors, start_sector);
 
 	res = lv1_storage_read(dev->sbd.dev_id,
-			       dev->regions[dev->region_idx].id, start_sector,
-			       sectors, 0, dev->bounce_lpar, &dev->tag);
+			       dev->regions[PS3ROM_REGION_IDX].id,
+			       start_sector, sectors, 0,
+			       dev->bounce_lpar, &dev->tag);
 	if (res) {
 		dev_err(&dev->sbd.core, "%s:%u: read failed %d\n", __func__,
 			__LINE__, res);
@@ -200,8 +202,9 @@ static int ps3rom_write_request(struct ps3_storage_device *dev,
 	scsi_sg_copy_to_buffer(cmd, dev->bounce_buf, dev->bounce_size);
 
 	res = lv1_storage_write(dev->sbd.dev_id,
-				dev->regions[dev->region_idx].id, start_sector,
-				sectors, 0, dev->bounce_lpar, &dev->tag);
+				dev->regions[PS3ROM_REGION_IDX].id,
+				start_sector, sectors, 0,
+				dev->bounce_lpar, &dev->tag);
 	if (res) {
 		dev_err(&dev->sbd.core, "%s:%u: write failed %d\n", __func__,
 			__LINE__, res);
-- 
1.7.5.4

^ permalink raw reply related

* [PATCH 09/15] ps3: Limit the number of regions per storage device
From: Andre Heider @ 2011-08-01 20:03 UTC (permalink / raw)
  To: Geoff Levand; +Cc: cbe-oss-dev, Hector Martin, linuxppc-dev
In-Reply-To: <1312228986-32307-1-git-send-email-a.heider@gmail.com>

There can be only 8 regions, add a sanity check

Signed-off-by: Andre Heider <a.heider@gmail.com>
---
 arch/powerpc/include/asm/ps3stor.h       |    1 +
 arch/powerpc/platforms/ps3/device-init.c |    8 ++++++++
 2 files changed, 9 insertions(+), 0 deletions(-)

diff --git a/arch/powerpc/include/asm/ps3stor.h b/arch/powerpc/include/asm/ps3stor.h
index 6fcaf71..d51e53c 100644
--- a/arch/powerpc/include/asm/ps3stor.h
+++ b/arch/powerpc/include/asm/ps3stor.h
@@ -25,6 +25,7 @@
 
 #include <asm/ps3.h>
 
+#define PS3_STORAGE_MAX_REGIONS		(8)
 
 struct ps3_storage_region {
 	unsigned int id;
diff --git a/arch/powerpc/platforms/ps3/device-init.c b/arch/powerpc/platforms/ps3/device-init.c
index 6c4b583..830d741 100644
--- a/arch/powerpc/platforms/ps3/device-init.c
+++ b/arch/powerpc/platforms/ps3/device-init.c
@@ -349,6 +349,14 @@ static int ps3_setup_storage_dev(const struct ps3_repository_device *repo,
 		return -ENODEV;
 	}
 
+	if (num_regions > PS3_STORAGE_MAX_REGIONS) {
+		pr_warning("%s:%u: device %u:%u reported %u regions, "
+			   "limiting to %u\n", __func__, __LINE__,
+			   num_regions, repo->bus_index, repo->dev_index,
+			   PS3_STORAGE_MAX_REGIONS);
+		num_regions = PS3_STORAGE_MAX_REGIONS;
+	}
+
 	pr_debug("%s:%u: (%u:%u:%u): port %llu blk_size %llu num_blocks %llu "
 		 "num_regions %u\n", __func__, __LINE__, repo->bus_index,
 		 repo->dev_index, repo->dev_type, port, blk_size, num_blocks,
-- 
1.7.5.4

^ permalink raw reply related

* [PATCH 08/15] ps3: Only prealloc the flash bounce buffer for the OtherOS lpar
From: Andre Heider @ 2011-08-01 20:02 UTC (permalink / raw)
  To: Geoff Levand; +Cc: cbe-oss-dev, Hector Martin, linuxppc-dev
In-Reply-To: <1312228986-32307-1-git-send-email-a.heider@gmail.com>

It's only used by the ps3flash driver, which only supports the
OtherOS lpar.

Signed-off-by: Andre Heider <a.heider@gmail.com>
---
 arch/powerpc/platforms/ps3/setup.c |    5 ++++-
 1 files changed, 4 insertions(+), 1 deletions(-)

diff --git a/arch/powerpc/platforms/ps3/setup.c b/arch/powerpc/platforms/ps3/setup.c
index f430279..923be8a 100644
--- a/arch/powerpc/platforms/ps3/setup.c
+++ b/arch/powerpc/platforms/ps3/setup.c
@@ -233,7 +233,10 @@ static void __init ps3_setup_arch(void)
 #endif
 
 	prealloc_ps3fb_videomemory();
-	prealloc_ps3flash_bounce_buffer();
+
+	/* the ps3flash driver only works for OtherOS */
+	if (ps3_get_ss_laid() == PS3_SS_LAID_OTHEROS)
+		prealloc_ps3flash_bounce_buffer();
 
 	ppc_md.power_save = ps3_power_save;
 	ps3_os_area_init();
-- 
1.7.5.4

^ permalink raw reply related

* [PATCH 07/15] ps3flash: Refuse to work in lpars other than OtherOS
From: Andre Heider @ 2011-08-01 20:02 UTC (permalink / raw)
  To: Geoff Levand; +Cc: cbe-oss-dev, Hector Martin, linuxppc-dev
In-Reply-To: <1312228986-32307-1-git-send-email-a.heider@gmail.com>

The driver implements a character and misc device, meant for the
axed OtherOS to exchange various settings with GameOS.
Since Firmware 3.21 there is no GameOS support anymore to write these
settings, so limit the driver to the OtherOS environment.

Signed-off-by: Andre Heider <a.heider@gmail.com>
---
 arch/powerpc/platforms/ps3/Kconfig |    1 +
 drivers/char/ps3flash.c            |    7 +++++++
 2 files changed, 8 insertions(+), 0 deletions(-)

diff --git a/arch/powerpc/platforms/ps3/Kconfig b/arch/powerpc/platforms/ps3/Kconfig
index 84df5c8..5eb956a 100644
--- a/arch/powerpc/platforms/ps3/Kconfig
+++ b/arch/powerpc/platforms/ps3/Kconfig
@@ -121,6 +121,7 @@ config PS3_FLASH
 
 	  This support is required to access the PS3 FLASH ROM, which
 	  contains the boot loader and some boot options.
+	  This driver only supports the deprecated OtherOS LPAR.
 	  In general, all users will say Y or M.
 
 	  As this driver needs a fixed buffer of 256 KiB of memory, it can
diff --git a/drivers/char/ps3flash.c b/drivers/char/ps3flash.c
index 69c734a..b1e8659 100644
--- a/drivers/char/ps3flash.c
+++ b/drivers/char/ps3flash.c
@@ -25,6 +25,7 @@
 
 #include <asm/lv1call.h>
 #include <asm/ps3stor.h>
+#include <asm/firmware.h>
 
 
 #define DEVICE_NAME		"ps3flash"
@@ -455,6 +456,12 @@ static struct ps3_system_bus_driver ps3flash = {
 
 static int __init ps3flash_init(void)
 {
+	if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
+		return -ENODEV;
+
+	if (ps3_get_ss_laid() != PS3_SS_LAID_OTHEROS)
+		return -ENODEV;
+
 	return ps3_system_bus_driver_register(&ps3flash);
 }
 
-- 
1.7.5.4

^ permalink raw reply related

* [PATCH 06/15] ps3flash: Fix region align checks
From: Andre Heider @ 2011-08-01 20:02 UTC (permalink / raw)
  To: Geoff Levand; +Cc: cbe-oss-dev, Hector Martin, linuxppc-dev
In-Reply-To: <1312228986-32307-1-git-send-email-a.heider@gmail.com>

The region fields used by the align checks are set in
ps3stor_setup(), so move those after that call.

Signed-off-by: Andre Heider <a.heider@gmail.com>
---
 drivers/char/ps3flash.c |   30 +++++++++++++++---------------
 1 files changed, 15 insertions(+), 15 deletions(-)

diff --git a/drivers/char/ps3flash.c b/drivers/char/ps3flash.c
index 85c004a..69c734a 100644
--- a/drivers/char/ps3flash.c
+++ b/drivers/char/ps3flash.c
@@ -360,21 +360,6 @@ static int __devinit ps3flash_probe(struct ps3_system_bus_device *_dev)
 	int error;
 	unsigned long tmp;
 
-	tmp = dev->regions[dev->region_idx].start*dev->blk_size;
-	if (tmp % FLASH_BLOCK_SIZE) {
-		dev_err(&dev->sbd.core,
-			"%s:%u region start %lu is not aligned\n", __func__,
-			__LINE__, tmp);
-		return -EINVAL;
-	}
-	tmp = dev->regions[dev->region_idx].size*dev->blk_size;
-	if (tmp % FLASH_BLOCK_SIZE) {
-		dev_err(&dev->sbd.core,
-			"%s:%u region size %lu is not aligned\n", __func__,
-			__LINE__, tmp);
-		return -EINVAL;
-	}
-
 	/* use static buffer, kmalloc cannot allocate 256 KiB */
 	if (!ps3flash_bounce_buffer.address)
 		return -ENODEV;
@@ -405,6 +390,21 @@ static int __devinit ps3flash_probe(struct ps3_system_bus_device *_dev)
 	if (error)
 		goto fail_free_priv;
 
+	tmp = dev->regions[dev->region_idx].start*dev->blk_size;
+	if (tmp % FLASH_BLOCK_SIZE) {
+		dev_err(&dev->sbd.core,
+			"%s:%u region start %lu is not aligned\n", __func__,
+			__LINE__, tmp);
+		return -EINVAL;
+	}
+	tmp = dev->regions[dev->region_idx].size*dev->blk_size;
+	if (tmp % FLASH_BLOCK_SIZE) {
+		dev_err(&dev->sbd.core,
+			"%s:%u region size %lu is not aligned\n", __func__,
+			__LINE__, tmp);
+		return -EINVAL;
+	}
+
 	ps3flash_misc.parent = &dev->sbd.core;
 	error = misc_register(&ps3flash_misc);
 	if (error) {
-- 
1.7.5.4

^ permalink raw reply related

* [PATCH 05/15] ps3: Detect the current lpar environment
From: Andre Heider @ 2011-08-01 20:02 UTC (permalink / raw)
  To: Geoff Levand; +Cc: cbe-oss-dev, Hector Martin, linuxppc-dev
In-Reply-To: <1312228986-32307-1-git-send-email-a.heider@gmail.com>

There is more than the OtherOS lpar the kernel can be launched in.
Detect it by reading the ss laid repository node, and be verbose about
it.

Signed-off-by: Andre Heider <a.heider@gmail.com>
---
 arch/powerpc/include/asm/ps3.h          |    7 +++++++
 arch/powerpc/platforms/ps3/platform.h   |    4 ++++
 arch/powerpc/platforms/ps3/repository.c |   19 +++++++++++++++++++
 arch/powerpc/platforms/ps3/setup.c      |   22 ++++++++++++++++++++++
 4 files changed, 52 insertions(+), 0 deletions(-)

diff --git a/arch/powerpc/include/asm/ps3.h b/arch/powerpc/include/asm/ps3.h
index 7f065e1..136354a 100644
--- a/arch/powerpc/include/asm/ps3.h
+++ b/arch/powerpc/include/asm/ps3.h
@@ -39,6 +39,13 @@ union ps3_firmware_version {
 void ps3_get_firmware_version(union ps3_firmware_version *v);
 int ps3_compare_firmware_version(u16 major, u16 minor, u16 rev);
 
+enum ps3_ss_laid {
+	PS3_SS_LAID_GAMEOS = 0x1070000002000001UL,
+	PS3_SS_LAID_OTHEROS = 0x1080000004000001UL,
+};
+
+enum ps3_ss_laid ps3_get_ss_laid(void);
+
 /* 'Other OS' area */
 
 enum ps3_param_av_multi_out {
diff --git a/arch/powerpc/platforms/ps3/platform.h b/arch/powerpc/platforms/ps3/platform.h
index 9a196a8..1ba15b8 100644
--- a/arch/powerpc/platforms/ps3/platform.h
+++ b/arch/powerpc/platforms/ps3/platform.h
@@ -232,4 +232,8 @@ int ps3_repository_read_spu_resource_id(unsigned int res_index,
 int ps3_repository_read_vuart_av_port(unsigned int *port);
 int ps3_repository_read_vuart_sysmgr_port(unsigned int *port);
 
+/* repository ss info */
+
+int ps3_repository_read_ss_laid(enum ps3_ss_laid *laid);
+
 #endif
diff --git a/arch/powerpc/platforms/ps3/repository.c b/arch/powerpc/platforms/ps3/repository.c
index 5e304c2..6fa3e96 100644
--- a/arch/powerpc/platforms/ps3/repository.c
+++ b/arch/powerpc/platforms/ps3/repository.c
@@ -1002,6 +1002,25 @@ int ps3_repository_read_lpm_privileges(unsigned int be_index, u64 *lpar,
 			    lpar, rights);
 }
 
+/**
+ * ps3_repository_read_ss_laid - Read the lpar auth id
+ */
+
+int ps3_repository_read_ss_laid(enum ps3_ss_laid *laid)
+{
+	int result;
+	u64 id, v1;
+
+	lv1_get_logical_partition_id(&id);
+	result = read_node(PS3_LPAR_ID_PME,
+			   make_first_field("ss", 0),
+			   make_field("laid", 0),
+			   id, 0,
+			   &v1, NULL);
+	*laid = v1;
+	return result;
+}
+
 #if defined(DEBUG)
 
 int ps3_repository_dump_resource_info(const struct ps3_repository_device *repo)
diff --git a/arch/powerpc/platforms/ps3/setup.c b/arch/powerpc/platforms/ps3/setup.c
index 149bea2..f430279 100644
--- a/arch/powerpc/platforms/ps3/setup.c
+++ b/arch/powerpc/platforms/ps3/setup.c
@@ -47,6 +47,7 @@ DEFINE_MUTEX(ps3_gpu_mutex);
 EXPORT_SYMBOL_GPL(ps3_gpu_mutex);
 
 static union ps3_firmware_version ps3_firmware_version;
+static enum ps3_ss_laid ps3_ss_laid;
 
 void ps3_get_firmware_version(union ps3_firmware_version *v)
 {
@@ -68,6 +69,12 @@ int ps3_compare_firmware_version(u16 major, u16 minor, u16 rev)
 }
 EXPORT_SYMBOL_GPL(ps3_compare_firmware_version);
 
+enum ps3_ss_laid ps3_get_ss_laid(void)
+{
+	return ps3_ss_laid;
+}
+EXPORT_SYMBOL_GPL(ps3_get_ss_laid);
+
 static void ps3_power_save(void)
 {
 	/*
@@ -192,6 +199,7 @@ static int ps3_set_dabr(unsigned long dabr)
 
 static void __init ps3_setup_arch(void)
 {
+	const char *laid_str;
 
 	DBG(" -> %s:%d\n", __func__, __LINE__);
 
@@ -200,6 +208,20 @@ static void __init ps3_setup_arch(void)
 	       ps3_firmware_version.major, ps3_firmware_version.minor,
 	       ps3_firmware_version.rev);
 
+	ps3_repository_read_ss_laid(&ps3_ss_laid);
+	switch (ps3_ss_laid) {
+	case PS3_SS_LAID_GAMEOS:
+		laid_str = "GameOS";
+		break;
+	case PS3_SS_LAID_OTHEROS:
+		laid_str = "OtherOS";
+		break;
+	default:
+		laid_str = "unknown";
+		break;
+	}
+	printk(KERN_INFO "Running in %s lpar\n", laid_str);
+
 	ps3_spu_set_platform();
 
 #ifdef CONFIG_SMP
-- 
1.7.5.4

^ permalink raw reply related

* [PATCH 04/15] ps3: MEMORY_HOTPLUG is not a requirement anymore
From: Andre Heider @ 2011-08-01 20:02 UTC (permalink / raw)
  To: Geoff Levand; +Cc: cbe-oss-dev, Hector Martin, linuxppc-dev
In-Reply-To: <1312228986-32307-1-git-send-email-a.heider@gmail.com>

Signed-off-by: Andre Heider <a.heider@gmail.com>
---
 arch/powerpc/platforms/ps3/Kconfig |    1 -
 1 files changed, 0 insertions(+), 1 deletions(-)

diff --git a/arch/powerpc/platforms/ps3/Kconfig b/arch/powerpc/platforms/ps3/Kconfig
index 476d9d9..84df5c8 100644
--- a/arch/powerpc/platforms/ps3/Kconfig
+++ b/arch/powerpc/platforms/ps3/Kconfig
@@ -7,7 +7,6 @@ config PPC_PS3
 	select USB_OHCI_BIG_ENDIAN_MMIO
 	select USB_ARCH_HAS_EHCI
 	select USB_EHCI_BIG_ENDIAN_MMIO
-	select MEMORY_HOTPLUG
 	select PPC_PCI_CHOICE
 	help
 	  This option enables support for the Sony PS3 game console
-- 
1.7.5.4

^ permalink raw reply related

* [PATCH 03/15] [PS3] Add region 1 memory early
From: Andre Heider @ 2011-08-01 20:02 UTC (permalink / raw)
  To: Geoff Levand; +Cc: cbe-oss-dev, Hector Martin, linuxppc-dev
In-Reply-To: <1312228986-32307-1-git-send-email-a.heider@gmail.com>

From: Hector Martin <hector@marcansoft.com>

Signed-off-by: Hector Martin <hector@marcansoft.com>
[a.heider: Various cleanups to make checkpatch.pl happy]
Signed-off-by: Andre Heider <a.heider@gmail.com>
---
 arch/powerpc/platforms/ps3/mm.c |   62 +++++++--------------------------------
 1 files changed, 11 insertions(+), 51 deletions(-)

diff --git a/arch/powerpc/platforms/ps3/mm.c b/arch/powerpc/platforms/ps3/mm.c
index 30bb096..15876d6 100644
--- a/arch/powerpc/platforms/ps3/mm.c
+++ b/arch/powerpc/platforms/ps3/mm.c
@@ -331,57 +331,6 @@ static int ps3_mm_get_devtree_highmem(struct mem_region *r)
 	}
 }
 
-/**
- * ps3_mm_add_memory - hot add memory
- */
-
-static int __init ps3_mm_add_memory(void)
-{
-	int result;
-	unsigned long start_addr;
-	unsigned long start_pfn;
-	unsigned long nr_pages;
-
-	if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
-		return -ENODEV;
-
-	BUG_ON(!mem_init_done);
-
-	if (!map.r1.size) {
-		DBG("%s:%d: no region 1, not adding memory\n",
-		    __func__, __LINE__);
-		return 0;
-	}
-
-	start_addr = map.rm.size;
-	start_pfn = start_addr >> PAGE_SHIFT;
-	nr_pages = (map.r1.size + PAGE_SIZE - 1) >> PAGE_SHIFT;
-
-	DBG("%s:%d: start_addr %lxh, start_pfn %lxh, nr_pages %lxh\n",
-		__func__, __LINE__, start_addr, start_pfn, nr_pages);
-
-	result = add_memory(0, start_addr, map.r1.size);
-
-	if (result) {
-		pr_err("%s:%d: add_memory failed: (%d)\n",
-			__func__, __LINE__, result);
-		return result;
-	}
-
-	memblock_add(start_addr, map.r1.size);
-	memblock_analyze();
-
-	result = online_pages(start_pfn, nr_pages);
-
-	if (result)
-		pr_err("%s:%d: online_pages failed: (%d)\n",
-			__func__, __LINE__, result);
-
-	return result;
-}
-
-device_initcall(ps3_mm_add_memory);
-
 /*============================================================================*/
 /* dma routines                                                               */
 /*============================================================================*/
@@ -1280,6 +1229,17 @@ void __init ps3_mm_init(void)
 	/* correct map.total for the real total amount of memory we use */
 	map.total = map.rm.size + map.r1.size;
 
+	if (!map.r1.size) {
+		DBG("%s:%d: no region 1, not adding memory\n",
+			__func__, __LINE__);
+	} else {
+		DBG("%s:%d: adding memory: start %llxh, size %llxh\n",
+			__func__, __LINE__, map.rm.size, map.r1.size);
+
+		memblock_add(map.rm.size, map.r1.size);
+		memblock_analyze();
+	}
+
 	DBG(" <- %s:%d\n", __func__, __LINE__);
 }
 
-- 
1.7.5.4

^ permalink raw reply related

* [PATCH 02/15] [PS3] Get lv1 high memory region from devtree
From: Andre Heider @ 2011-08-01 20:02 UTC (permalink / raw)
  To: Geoff Levand; +Cc: cbe-oss-dev, Hector Martin, linuxppc-dev
In-Reply-To: <1312228986-32307-1-git-send-email-a.heider@gmail.com>

From: Hector Martin <hector@marcansoft.com>

This lets the bootloader preallocate the high lv1 region and pass its
location to the kernel through the devtree. Thus, it can be used to hold
the initrd. If the property doesn't exist, the kernel retains the old
behavior and attempts to allocate the region itself.

Signed-off-by: Hector Martin <hector@marcansoft.com>
[a.heider: Various cleanups to make checkpatch.pl happy]
Signed-off-by: Andre Heider <a.heider@gmail.com>
---
 arch/powerpc/platforms/ps3/mm.c |   61 +++++++++++++++++++++++++++++++++++++-
 1 files changed, 59 insertions(+), 2 deletions(-)

diff --git a/arch/powerpc/platforms/ps3/mm.c b/arch/powerpc/platforms/ps3/mm.c
index c204588..30bb096 100644
--- a/arch/powerpc/platforms/ps3/mm.c
+++ b/arch/powerpc/platforms/ps3/mm.c
@@ -110,6 +110,7 @@ struct map {
 	u64 htab_size;
 	struct mem_region rm;
 	struct mem_region r1;
+	int destroy_r1;
 };
 
 #define debug_dump_map(x) _debug_dump_map(x, __func__, __LINE__)
@@ -287,6 +288,49 @@ static void ps3_mm_region_destroy(struct mem_region *r)
 	}
 }
 
+static int ps3_mm_scan_memory(unsigned long node, const char *uname,
+			      int depth, void *data)
+{
+	struct mem_region *r = data;
+	void *p;
+	u64 prop[2];
+	unsigned long l;
+	char *type = of_get_flat_dt_prop(node, "device_type", NULL);
+
+	if (type == NULL)
+		return 0;
+	if (strcmp(type, "memory") != 0)
+		return 0;
+
+	p = of_get_flat_dt_prop(node, "sony,lv1-highmem", &l);
+	if (p == NULL)
+		return 0;
+
+	BUG_ON(l != sizeof(prop));
+	memcpy(prop, p, sizeof(prop));
+
+	r->base = prop[0];
+	r->size = prop[1];
+	r->offset = r->base - map.rm.size;
+
+	return -1;
+}
+
+static int ps3_mm_get_devtree_highmem(struct mem_region *r)
+{
+	r->size = r->base = r->offset = 0;
+	of_scan_flat_dt(ps3_mm_scan_memory, r);
+
+	if (r->base && r->size) {
+		DBG("%s:%d got high region from devtree: %llxh %llxh\n",
+		__func__, __LINE__, r->base, r->size);
+		return 0;
+	} else {
+		DBG("%s:%d no high region in devtree...\n", __func__, __LINE__);
+		return -1;
+	}
+}
+
 /**
  * ps3_mm_add_memory - hot add memory
  */
@@ -303,6 +347,12 @@ static int __init ps3_mm_add_memory(void)
 
 	BUG_ON(!mem_init_done);
 
+	if (!map.r1.size) {
+		DBG("%s:%d: no region 1, not adding memory\n",
+		    __func__, __LINE__);
+		return 0;
+	}
+
 	start_addr = map.rm.size;
 	start_pfn = start_addr >> PAGE_SHIFT;
 	nr_pages = (map.r1.size + PAGE_SIZE - 1) >> PAGE_SHIFT;
@@ -1219,7 +1269,13 @@ void __init ps3_mm_init(void)
 
 
 	/* arrange to do this in ps3_mm_add_memory */
-	ps3_mm_region_create(&map.r1, map.total - map.rm.size);
+
+	if (ps3_mm_get_devtree_highmem(&map.r1) == 0) {
+		map.destroy_r1 = 0;
+	} else {
+		ps3_mm_region_create(&map.r1, map.total - map.rm.size);
+		map.destroy_r1 = 1;
+	}
 
 	/* correct map.total for the real total amount of memory we use */
 	map.total = map.rm.size + map.r1.size;
@@ -1233,5 +1289,6 @@ void __init ps3_mm_init(void)
 
 void ps3_mm_shutdown(void)
 {
-	ps3_mm_region_destroy(&map.r1);
+	if (map.destroy_r1)
+		ps3_mm_region_destroy(&map.r1);
 }
-- 
1.7.5.4

^ permalink raw reply related

* [PATCH 01/15] [PS3] Add udbg driver using the PS3 gelic Ethernet device
From: Andre Heider @ 2011-08-01 20:02 UTC (permalink / raw)
  To: Geoff Levand; +Cc: cbe-oss-dev, Hector Martin, linuxppc-dev
In-Reply-To: <1312228986-32307-1-git-send-email-a.heider@gmail.com>

From: Hector Martin <hector@marcansoft.com>

Signed-off-by: Hector Martin <hector@marcansoft.com>
[a.heider: Various cleanups to make checkpatch.pl happy]
Signed-off-by: Andre Heider <a.heider@gmail.com>
---
 arch/powerpc/Kconfig.debug              |    8 +
 arch/powerpc/include/asm/udbg.h         |    1 +
 arch/powerpc/kernel/udbg.c              |    2 +
 arch/powerpc/platforms/ps3/Kconfig      |   12 ++
 arch/powerpc/platforms/ps3/Makefile     |    1 +
 arch/powerpc/platforms/ps3/gelic_udbg.c |  272 +++++++++++++++++++++++++++++++
 drivers/net/ps3_gelic_net.c             |    3 +
 drivers/net/ps3_gelic_net.h             |    6 +
 8 files changed, 305 insertions(+), 0 deletions(-)
 create mode 100644 arch/powerpc/platforms/ps3/gelic_udbg.c

diff --git a/arch/powerpc/Kconfig.debug b/arch/powerpc/Kconfig.debug
index 2d38a50..47edab6 100644
--- a/arch/powerpc/Kconfig.debug
+++ b/arch/powerpc/Kconfig.debug
@@ -267,6 +267,14 @@ config PPC_EARLY_DEBUG_USBGECKO
 	  Select this to enable early debugging for Nintendo GameCube/Wii
 	  consoles via an external USB Gecko adapter.
 
+config PPC_EARLY_DEBUG_PS3GELIC
+	bool "Early debugging through the PS3 Ethernet port"
+	depends on PPC_PS3
+	select PS3GELIC_UDBG
+	help
+	  Select this to enable early debugging for the PlayStation3 via
+	  UDP broadcasts sent out through the Ethernet port.
+
 endchoice
 
 config PPC_EARLY_DEBUG_44x_PHYSLOW
diff --git a/arch/powerpc/include/asm/udbg.h b/arch/powerpc/include/asm/udbg.h
index 11ae699..58f0ab9 100644
--- a/arch/powerpc/include/asm/udbg.h
+++ b/arch/powerpc/include/asm/udbg.h
@@ -52,6 +52,7 @@ extern void __init udbg_init_44x_as1(void);
 extern void __init udbg_init_40x_realmode(void);
 extern void __init udbg_init_cpm(void);
 extern void __init udbg_init_usbgecko(void);
+extern void __init udbg_init_ps3gelic(void);
 
 #endif /* __KERNEL__ */
 #endif /* _ASM_POWERPC_UDBG_H */
diff --git a/arch/powerpc/kernel/udbg.c b/arch/powerpc/kernel/udbg.c
index e39cad8..9ea9897 100644
--- a/arch/powerpc/kernel/udbg.c
+++ b/arch/powerpc/kernel/udbg.c
@@ -62,6 +62,8 @@ void __init udbg_early_init(void)
 	udbg_init_cpm();
 #elif defined(CONFIG_PPC_EARLY_DEBUG_USBGECKO)
 	udbg_init_usbgecko();
+#elif defined(CONFIG_PPC_EARLY_DEBUG_PS3GELIC)
+	udbg_init_ps3gelic();
 #endif
 
 #ifdef CONFIG_PPC_EARLY_DEBUG
diff --git a/arch/powerpc/platforms/ps3/Kconfig b/arch/powerpc/platforms/ps3/Kconfig
index dfe316b..476d9d9 100644
--- a/arch/powerpc/platforms/ps3/Kconfig
+++ b/arch/powerpc/platforms/ps3/Kconfig
@@ -148,4 +148,16 @@ config PS3_LPM
 	  profiling support of the Cell processor with programs like
 	  oprofile and perfmon2, then say Y or M, otherwise say N.
 
+config PS3GELIC_UDBG
+	bool "PS3 udbg output via UDP broadcasts on Ethernet"
+	depends on PPC_PS3
+	help
+	  Enables udbg early debugging output by sending broadcast UDP
+	  via the Ethernet port (UDP port number 18194).
+
+	  This driver uses a trivial implementation and is independent
+	  from the main network driver.
+
+	  If in doubt, say N here.
+
 endmenu
diff --git a/arch/powerpc/platforms/ps3/Makefile b/arch/powerpc/platforms/ps3/Makefile
index ac1bdf8..02b9e63 100644
--- a/arch/powerpc/platforms/ps3/Makefile
+++ b/arch/powerpc/platforms/ps3/Makefile
@@ -2,6 +2,7 @@ obj-y += setup.o mm.o time.o hvcall.o htab.o repository.o
 obj-y += interrupt.o exports.o os-area.o
 obj-y += system-bus.o
 
+obj-$(CONFIG_PS3GELIC_UDBG) += gelic_udbg.o
 obj-$(CONFIG_SMP) += smp.o
 obj-$(CONFIG_SPU_BASE) += spu.o
 obj-y += device-init.o
diff --git a/arch/powerpc/platforms/ps3/gelic_udbg.c b/arch/powerpc/platforms/ps3/gelic_udbg.c
new file mode 100644
index 0000000..9b383bd
--- /dev/null
+++ b/arch/powerpc/platforms/ps3/gelic_udbg.c
@@ -0,0 +1,272 @@
+/*
+ * arch/powerpc/platforms/ps3/gelic_udbg.c
+ *
+ * udbg debug output routine via GELIC UDP broadcasts
+ * Copyright (C) 2010 Hector Martin <hector@marcansoft.com>
+ * Copyright (C) 2011 Andre Heider <a.heider@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ */
+
+#include <asm/io.h>
+#include <asm/udbg.h>
+#include <asm/lv1call.h>
+
+#define GELIC_BUS_ID 1
+#define GELIC_DEVICE_ID 0
+#define GELIC_DEBUG_PORT 18194
+#define GELIC_MAX_MESSAGE_SIZE 1000
+
+#define GELIC_LV1_GET_MAC_ADDRESS 1
+#define GELIC_LV1_GET_VLAN_ID 4
+#define GELIC_LV1_VLAN_TX_ETHERNET_0 2
+
+#define GELIC_DESCR_DMA_STAT_MASK 0xf0000000
+#define GELIC_DESCR_DMA_CARDOWNED 0xa0000000
+
+#define GELIC_DESCR_TX_DMA_IKE 0x00080000
+#define GELIC_DESCR_TX_DMA_NO_CHKSUM 0x00000000
+#define GELIC_DESCR_TX_DMA_FRAME_TAIL 0x00040000
+
+#define GELIC_DESCR_DMA_CMD_NO_CHKSUM (GELIC_DESCR_DMA_CARDOWNED | \
+				       GELIC_DESCR_TX_DMA_IKE | \
+				       GELIC_DESCR_TX_DMA_NO_CHKSUM)
+
+static u64 bus_addr;
+
+struct gelic_descr {
+	/* as defined by the hardware */
+	u32 buf_addr;
+	u32 buf_size;
+	u32 next_descr_addr;
+	u32 dmac_cmd_status;
+	u32 result_size;
+	u32 valid_size;	/* all zeroes for tx */
+	u32 data_status;
+	u32 data_error;	/* all zeroes for tx */
+} __attribute__((aligned(32)));
+
+struct debug_block {
+	struct gelic_descr descr;
+	u8 pkt[1520];
+} __packed;
+
+struct ethhdr {
+	u8 dest[6];
+	u8 src[6];
+	u16 type;
+} __packed;
+
+struct vlantag {
+	u16 vlan;
+	u16 subtype;
+} __packed;
+
+struct iphdr {
+	u8 ver_len;
+	u8 dscp_ecn;
+	u16 total_length;
+	u16 ident;
+	u16 frag_off_flags;
+	u8 ttl;
+	u8 proto;
+	u16 checksum;
+	u32 src;
+	u32 dest;
+} __packed;
+
+struct udphdr {
+	u16 src;
+	u16 dest;
+	u16 len;
+	u16 checksum;
+} __packed;
+
+static __iomem struct ethhdr *h_eth;
+static __iomem struct vlantag *h_vlan;
+static __iomem struct iphdr *h_ip;
+static __iomem struct udphdr *h_udp;
+
+static __iomem char *pmsg;
+static __iomem char *pmsgc;
+
+static __iomem struct debug_block dbg __attribute__((aligned(32)));
+
+static int header_size;
+
+static void map_dma_mem(int bus_id, int dev_id, void *start, size_t len,
+			u64 *real_bus_addr)
+{
+	s64 result;
+	u64 real_addr = ((u64)start) & 0x0fffffffffffffffUL;
+	u64 real_end = real_addr + len;
+	u64 map_start = real_addr & ~0xfff;
+	u64 map_end = (real_end + 0xfff) & ~0xfff;
+	u64 bus_addr = 0;
+
+	u64 flags = 0xf800000000000000UL;
+
+	result = lv1_allocate_device_dma_region(bus_id, dev_id,
+						map_end - map_start, 12, 0,
+						&bus_addr);
+	if (result)
+		lv1_panic(0);
+
+	result = lv1_map_device_dma_region(bus_id, dev_id, map_start,
+					   bus_addr, map_end - map_start,
+					   flags);
+	if (result)
+		lv1_panic(0);
+
+	*real_bus_addr = bus_addr + real_addr - map_start;
+}
+
+static int unmap_dma_mem(int bus_id, int dev_id, u64 bus_addr, size_t len)
+{
+	s64 result;
+	u64 real_bus_addr;
+
+	real_bus_addr = bus_addr & ~0xfff;
+	len += bus_addr - real_bus_addr;
+	len = (len + 0xfff) & ~0xfff;
+
+	result = lv1_unmap_device_dma_region(bus_id, dev_id, real_bus_addr,
+					     len);
+	if (result)
+		return result;
+
+	return lv1_free_device_dma_region(bus_id, dev_id, real_bus_addr);
+}
+
+static void gelic_debug_init(void)
+{
+	s64 result;
+	u64 v2;
+	u64 mac;
+	u64 vlan_id;
+
+	result = lv1_open_device(GELIC_BUS_ID, GELIC_DEVICE_ID, 0);
+	if (result)
+		lv1_panic(0);
+
+	map_dma_mem(GELIC_BUS_ID, GELIC_DEVICE_ID, &dbg, sizeof(dbg),
+		    &bus_addr);
+
+	memset(&dbg, 0, sizeof(dbg));
+
+	dbg.descr.buf_addr = bus_addr + offsetof(struct debug_block, pkt);
+
+	wmb();
+
+	result = lv1_net_control(GELIC_BUS_ID, GELIC_DEVICE_ID,
+				 GELIC_LV1_GET_MAC_ADDRESS, 0, 0, 0,
+				 &mac, &v2);
+	if (result)
+		lv1_panic(0);
+
+	mac <<= 16;
+
+	h_eth = (struct ethhdr *)dbg.pkt;
+
+	memset(&h_eth->dest, 0xff, 6);
+	memcpy(&h_eth->src, &mac, 6);
+
+	header_size = sizeof(struct ethhdr);
+
+	result = lv1_net_control(GELIC_BUS_ID, GELIC_DEVICE_ID,
+				 GELIC_LV1_GET_VLAN_ID,
+				 GELIC_LV1_VLAN_TX_ETHERNET_0, 0, 0,
+				 &vlan_id, &v2);
+	if (result == 0) {
+		h_eth->type = 0x8100;
+
+		header_size += sizeof(struct vlantag);
+		h_vlan = (struct vlantag *)(h_eth + 1);
+		h_vlan->vlan = vlan_id;
+		h_vlan->subtype = 0x0800;
+		h_ip = (struct iphdr *)(h_vlan + 1);
+	} else {
+		h_eth->type = 0x0800;
+		h_ip = (struct iphdr *)(h_eth + 1);
+	}
+
+	header_size += sizeof(struct iphdr);
+	h_ip->ver_len = 0x45;
+	h_ip->ttl = 10;
+	h_ip->proto = 0x11;
+	h_ip->src = 0x00000000;
+	h_ip->dest = 0xffffffff;
+
+	header_size += sizeof(struct udphdr);
+	h_udp = (struct udphdr *)(h_ip + 1);
+	h_udp->src = GELIC_DEBUG_PORT;
+	h_udp->dest = GELIC_DEBUG_PORT;
+
+	pmsgc = pmsg = (char *)(h_udp + 1);
+}
+
+static void gelic_debug_shutdown(void)
+{
+	if (bus_addr)
+		unmap_dma_mem(GELIC_BUS_ID, GELIC_DEVICE_ID,
+			      bus_addr, sizeof(dbg));
+	lv1_close_device(GELIC_BUS_ID, GELIC_DEVICE_ID);
+}
+
+static void gelic_sendbuf(int msgsize)
+{
+	u16 *p;
+	u32 sum;
+	int i;
+
+	dbg.descr.buf_size = header_size + msgsize;
+	h_ip->total_length = msgsize + sizeof(struct udphdr) +
+			     sizeof(struct iphdr);
+	h_udp->len = msgsize + sizeof(struct udphdr);
+
+	h_ip->checksum = 0;
+	sum = 0;
+	p = (u16 *)h_ip;
+	for (i = 0; i < 5; i++)
+		sum += *p++;
+	h_ip->checksum = ~(sum + (sum >> 16));
+
+	dbg.descr.dmac_cmd_status = GELIC_DESCR_DMA_CMD_NO_CHKSUM |
+				    GELIC_DESCR_TX_DMA_FRAME_TAIL;
+	dbg.descr.result_size = 0;
+	dbg.descr.data_status = 0;
+
+	wmb();
+
+	lv1_net_start_tx_dma(GELIC_BUS_ID, GELIC_DEVICE_ID, bus_addr, 0);
+
+	while ((dbg.descr.dmac_cmd_status & GELIC_DESCR_DMA_STAT_MASK) ==
+	       GELIC_DESCR_DMA_CARDOWNED)
+		cpu_relax();
+}
+
+static void ps3gelic_udbg_putc(char ch)
+{
+	*pmsgc++ = ch;
+	if (ch == '\n' || (pmsgc-pmsg) >= GELIC_MAX_MESSAGE_SIZE) {
+		gelic_sendbuf(pmsgc-pmsg);
+		pmsgc = pmsg;
+	}
+}
+
+void __init udbg_init_ps3gelic(void)
+{
+	gelic_debug_init();
+	udbg_putc = ps3gelic_udbg_putc;
+}
+
+void udbg_shutdown_ps3gelic(void)
+{
+	udbg_putc = NULL;
+	gelic_debug_shutdown();
+}
+EXPORT_SYMBOL(udbg_shutdown_ps3gelic);
diff --git a/drivers/net/ps3_gelic_net.c b/drivers/net/ps3_gelic_net.c
index 035a676..4488655 100644
--- a/drivers/net/ps3_gelic_net.c
+++ b/drivers/net/ps3_gelic_net.c
@@ -1688,6 +1688,9 @@ static int __devinit ps3_gelic_driver_probe(struct ps3_system_bus_device *dev)
 	int result;
 
 	pr_debug("%s: called\n", __func__);
+
+	udbg_shutdown_ps3gelic();
+
 	result = ps3_open_hv_device(dev);
 
 	if (result) {
diff --git a/drivers/net/ps3_gelic_net.h b/drivers/net/ps3_gelic_net.h
index 07bdc17..57ef974 100644
--- a/drivers/net/ps3_gelic_net.h
+++ b/drivers/net/ps3_gelic_net.h
@@ -360,6 +360,12 @@ static inline void *port_priv(struct gelic_port *port)
 	return port->priv;
 }
 
+#ifdef CONFIG_PPC_EARLY_DEBUG_PS3GELIC
+extern void udbg_shutdown_ps3gelic(void);
+#else
+static inline void udbg_shutdown_ps3gelic(void) {}
+#endif
+
 extern int gelic_card_set_irq_mask(struct gelic_card *card, u64 mask);
 /* shared netdev ops */
 extern void gelic_card_up(struct gelic_card *card);
-- 
1.7.5.4

^ permalink raw reply related

* [PATCH 00/15] ps3: Support more than the OtherOS lpar
From: Andre Heider @ 2011-08-01 20:02 UTC (permalink / raw)
  To: Geoff Levand; +Cc: cbe-oss-dev, Hector Martin, linuxppc-dev

This series addresses various issues and extends support when running
in lpars like GameOS. Included are some patches from Hector Martin, which
I found useful.

The ps3disk driver now creates multiple block devices instead of just one.
On the GameOS lpar we have access to all regions, and - depending on the
customizable layout - the linux partitions are likely not on the first.
The device names look similar the bsd slices, a bit unusual, but the best
I could think of. Better suggestions?

There're 2 new drivers: ps3vflash and ps3nflash. These are just modified
copies of the ps3disk driver:
  diff -u drivers/block/ps3disk.c drivers/block/ps3vflash.c
and "worse":
  diff -u drivers/block/ps3vflash.c drivers/block/ps3nflash.c
I'm not sure what the desired way to handle these similarities are. Should
that be merged to avoid code duplication? If so, how?

Patches are based on 2.6.39 since master doesn't boot with smp on my
console.  I wasn't able to pinpoint the cause so far (not that I tried
too hard). If anything get accepted I'll rebase :)

All patches were tested with the AsbestOS bootloader from Hector
Martin (http://git.marcansoft.com/?p=asbestos.git) on a PS3 slim.

Note: There are various patches floating around from anonymous
developers. None of those are signed-off and had various issues.
While some of those do contain simliar changes, I redid everything
in this series from scratch (except the patches from Hector).

Please let me know If there're any issues!

Thanks,
Andre

Andre Heider (12):
  ps3: MEMORY_HOTPLUG is not a requirement anymore
  ps3: Detect the current lpar environment
  ps3flash: Fix region align checks
  ps3flash: Refuse to work in lpars other than OtherOS
  ps3: Only prealloc the flash bounce buffer for the OtherOS lpar
  ps3: Limit the number of regions per storage device
  ps3stor_lib: Add support for multiple regions
  ps3disk: Provide a gendisk per accessible region
  ps3stor_lib: Add support for storage access flags
  ps3disk: Use region flags
  ps3: Add a vflash driver for lpars other than OtherOS
  ps3: Add a NOR FLASH driver for PS3s without NAND

Hector Martin (3):
  [PS3] Add udbg driver using the PS3 gelic Ethernet device
  [PS3] Get lv1 high memory region from devtree
  [PS3] Add region 1 memory early

 arch/powerpc/Kconfig.debug               |    8 +
 arch/powerpc/include/asm/ps3.h           |    9 +
 arch/powerpc/include/asm/ps3stor.h       |   11 +-
 arch/powerpc/include/asm/udbg.h          |    1 +
 arch/powerpc/kernel/udbg.c               |    2 +
 arch/powerpc/platforms/ps3/Kconfig       |   43 +++-
 arch/powerpc/platforms/ps3/Makefile      |    1 +
 arch/powerpc/platforms/ps3/device-init.c |   16 +
 arch/powerpc/platforms/ps3/gelic_udbg.c  |  272 ++++++++++++++++
 arch/powerpc/platforms/ps3/mm.c          |   91 ++++---
 arch/powerpc/platforms/ps3/platform.h    |    5 +
 arch/powerpc/platforms/ps3/repository.c  |   19 ++
 arch/powerpc/platforms/ps3/setup.c       |   27 ++-
 arch/powerpc/platforms/ps3/system-bus.c  |    2 +
 drivers/block/Makefile                   |    2 +
 drivers/block/ps3disk.c                  |  136 ++++++---
 drivers/block/ps3nflash.c                |  473 +++++++++++++++++++++++++++
 drivers/block/ps3vflash.c                |  508 ++++++++++++++++++++++++++++++
 drivers/char/ps3flash.c                  |   56 +++--
 drivers/net/ps3_gelic_net.c              |    3 +
 drivers/net/ps3_gelic_net.h              |    6 +
 drivers/ps3/ps3stor_lib.c                |   30 +-
 drivers/scsi/ps3rom.c                    |   11 +-
 23 files changed, 1617 insertions(+), 115 deletions(-)
 create mode 100644 arch/powerpc/platforms/ps3/gelic_udbg.c
 create mode 100644 drivers/block/ps3nflash.c
 create mode 100644 drivers/block/ps3vflash.c

-- 
1.7.5.4

^ permalink raw reply

* Re: kvm PCI assignment & VFIO ramblings
From: Alex Williamson @ 2011-08-01 18:59 UTC (permalink / raw)
  To: Benjamin Herrenschmidt
  Cc: chrisw, Alexey Kardashevskiy, kvm, Paul Mackerras,
	linux-pci@vger.kernel.org, qemu-devel, David Gibson, aafabbri,
	iommu, Anthony Liguori, linuxppc-dev, benve
In-Reply-To: <1312070097.8793.786.camel@pasglop>

On Sun, 2011-07-31 at 09:54 +1000, Benjamin Herrenschmidt wrote:
> On Sat, 2011-07-30 at 12:20 -0600, Alex Williamson wrote:
> 
> > On x86, the USB controllers don't typically live behind a PCIe-to-PCI
> > bridge, so don't suffer the source identifier problem, but they do often
> > share an interrupt.  But even then, we can count on most modern devices
> > supporting PCI2.3, and thus the DisINTx feature, which allows us to
> > share interrupts.  In any case, yes, it's more rare but we need to know
> > how to handle devices behind PCI bridges.  However I disagree that we
> > need to assign all the devices behind such a bridge to the guest.
> 
> Well, ok so let's dig a bit more here :-) First, yes I agree they don't
> all need to appear to the guest. My point is really that we must prevent
> them to be "used" by somebody else, either host or another guest.
> 
> Now once you get there, I personally prefer having a clear "group"
> ownership rather than having devices stay in some "limbo" under vfio
> control but it's an implementation detail.
> 
> Regarding DisINTx, well, it's a bit like putting separate PCIe functions
> into separate guests, it looks good ... but you are taking a chance.
> Note that I do intend to do some of that for power ... well I think, I
> haven't completely made my mind.
> 
> pHyp for has a stricter requirement, PEs essentially are everything
> behind a bridge. If you have a slot, you have some kind of bridge above
> this slot and everything on it will be a PE.
> 
> The problem I see is that with your filtering of config space, BAR
> emulation, DisINTx etc... you essentially assume that you can reasonably
> reliably isolate devices. But in practice, it's chancy. Some devices for
> example have "backdoors" into their own config space via MMIO. If I have
> such a device in a guest, I can completely override your DisINTx and
> thus DOS your host or another guest with a shared interrupt. I can move
> my MMIO around and DOS another function by overlapping the addresses.
> 
> You can really only be protect yourself against a device if you have it
> behind a bridge (in addition to having a filtering iommu), which limits
> the MMIO span (and thus letting the guest whack the BARs randomly will
> only allow that guest to shoot itself in the foot).
> 
> Some bridges also provide a way to block INTx below them which comes in
> handy but it's bridge specific. Some devices can be coerced to send the
> INTx "assert" message and never de-assert it (for example by doing a
> soft-reset while it's asserted, which can be done with some devices with
> an MMIO).
> 
> Anything below a PCIe -> PCI/PCI-X needs to also be "grouped" due to
> simple lack of proper filtering by the iommu (PCI-X in theory has RIDs
> and fowards them up, but this isn't very reliable, for example it fails
> over with split transactions).
> 
> Fortunately in PCIe land, we most have bridges above everything. The
> problem somewhat remains with functions of a device, how can you be sure
> that there isn't a way via some MMIO to create side effects on the other
> functions of the device ? (For example by checkstopping the whole
> thing). You can't really :-)
> 
> So it boils down of the "level" of safety/isolation you want to provide,
> and I suppose to some extent it's a user decision but the user needs to
> be informed to some extent. A hard problem :-)
>  
> > There's a difference between removing the device from the host and
> > exposing the device to the guest.  If I have a NIC and HBA behind a
> > bridge, it's perfectly reasonable that I might only assign the NIC to
> > the guest, but as you describe, we then need to prevent the host, or any
> > other guest from making use of the HBA.
> 
> Yes. However the other device is in "limbo" and it may be not clear to
> the user why it can't be used anymore :-)
> 
> The question is more, the user needs to "know" (or libvirt does, or
> somebody ... ) that in order to pass-through device A, it must also
> "remove" device B from the host. How can you even provide a meaningful
> error message to the user if all VFIO does is give you something like
> -EBUSY ?
> 
> So the information about the grouping constraint must trickle down
> somewhat.
> 
> Look at it from a GUI perspective for example. Imagine a front-end
> showing you devices in your system and allowing you to "Drag & drop"
> them to your guest. How do you represent that need for grouping ? First
> how do you expose it from kernel/libvirt to the GUI tool and how do you
> represent it to the user ?
> 
> By grouping the devices in logical groups which end up being the
> "objects" you can drag around, at least you provide some amount of
> clarity. Now if you follow that path down to how the GUI app, libvirt
> and possibly qemu need to know / resolve the dependency, being given the
> "groups" as the primary information of what can be used for pass-through
> makes everything a lot simpler.
>  
> > > - The -minimum- granularity of pass-through is not always a single
> > > device and not always under SW control
> > 
> > But IMHO, we need to preserve the granularity of exposing a device to a
> > guest as a single device.  That might mean some devices are held hostage
> > by an agent on the host.
> 
> Maybe but wouldn't that be even more confusing from a user perspective ?
> And I think it makes it harder from an implementation of admin &
> management tools perspective too.
> 
> > > - Having a magic heuristic in libvirt to figure out those constraints is
> > > WRONG. This reeks of XFree 4 PCI layer trying to duplicate the kernel
> > > knowledge of PCI resource management and getting it wrong in many many
> > > cases, something that took years to fix essentially by ripping it all
> > > out. This is kernel knowledge and thus we need the kernel to expose in a
> > > way or another what those constraints are, what those "partitionable
> > > groups" are.
> > > 
> > > - That does -not- mean that we cannot specify for each individual device
> > > within such a group where we want to put it in qemu (what devfn etc...).
> > > As long as there is a clear understanding that the "ownership" of the
> > > device goes with the group, this is somewhat orthogonal to how they are
> > > represented in qemu. (Not completely... if the iommu is exposed to the
> > > guest ,via paravirt for example, some of these constraints must be
> > > exposed but I'll talk about that more later).
> > 
> > Or we can choose not to expose all of the devices in the group to the
> > guest?
> 
> As I said, I don't mind if you don't, I'm just worried about the
> consequences of that from a usability standpoint. Having advanced
> command line option to fine tune is fine. Being able to specify within a
> "group" which devices to show and at what address if fine.
> 
> But I believe the basic entity to be manipulated from an interface
> standpoitn remains the group.
> 
> To get back to my GUI example, once you've D&D your group of devices
> over, you can have the option to open that group and check/uncheck
> individual devices & assign them addresses if you want. That doesn't
> change the fact that practically speaking, the whole group is now owned
> by the guest.
> 
> I will go further than that actually. If you look at how the isolation
> HW works on POWER, the fact that I have the MMIO segmentation means that
> I can simply give the entire group MMIO space to the guest. No problem
> of small BARs, no need to slow-map them ... etc.. that's a pretty handy
> feature don't you think ?
> 
> But that means that those other devices -will- be there, mapped along
> with the one you care about. We may not expose it in config space but it
> will be accessible. I suppose we can keep its IO/MEM decoding disabled.
> But my point is that for all intend and purpose, it's actually owned by
> the guest.
> 
> > > The interface currently proposed for VFIO (and associated uiommu)
> > > doesn't handle that problem at all. Instead, it is entirely centered
> > > around a specific "feature" of the VTd iommu's for creating arbitrary
> > > domains with arbitrary devices (tho those devices -do- have the same
> > > constraints exposed above, don't try to put 2 legacy PCI devices behind
> > > the same bridge into 2 different domains !), but the API totally ignores
> > > the problem, leaves it to libvirt "magic foo" and focuses on something
> > > that is both quite secondary in the grand scheme of things, and quite
> > > x86 VTd specific in the implementation and API definition.
> > 
> > To be fair, libvirt's "magic foo" is built out of the necessity that
> > nobody else is defining the rules.
> 
> Sure, which is why I propose that the kernel exposes the rules since
> it's really the one right place to have that sort of HW constraint
> knowledge, especially since it can be partially at least platform
> specific.
>  
>  .../...

I'll try to consolidate my reply to all the above here because there are
too many places above to interject and make this thread even more
difficult to respond to.  Much of what you're discussion above comes
down to policy.  Do we trust DisINTx?  Do we trust multi-function
devices?  I have no doubt there are devices we can use as examples for
each behaving badly.  On x86 this is one of the reasons we have SR-IOV.
Besides splitting a single device into multiple, it makes sure each
devices is actually virtualization friendly.  POWER seems to add
multiple layers of hardware so that you don't actually have to trust the
device, which is a great value add for enterprise systems, but in doing
so it mostly defeats the purpose and functionality of SR-IOV.

How we present this in a GUI is largely irrelevant because something has
to create a superset of what the hardware dictates (can I uniquely
identify transactions from this device, can I protect other devices from
it, etc.), the system policy (do I trust DisINTx, do I trust function
isolation, do I require ACS) and mold that with what the user actually
wants to assign.  For the VFIO kernel interface, we should only be
concerned with the first problem.  Userspace is free to make the rest as
simple or complete as it cares to.  I argue for x86, we want device
level granularity of assignment, but that also tends to be the typical
case (when only factoring in hardware restrictions) due to our advanced
iommus.

> > > Maybe something like /sys/devgroups ? This probably warrants involving
> > > more kernel people into the discussion.
> > 
> > I don't yet buy into passing groups to qemu since I don't buy into the
> > idea of always exposing all of those devices to qemu.  Would it be
> > sufficient to expose iommu nodes in sysfs that link to the devices
> > behind them and describe properties and capabilities of the iommu
> > itself?  More on this at the end.
> 
> Well, iommu aren't the only factor. I mentioned shared interrupts (and
> my unwillingness to always trust DisINTx),

*userspace policy*

>  there's also the MMIO
> grouping I mentioned above (in which case it's an x86 -limitation- with
> small BARs that I don't want to inherit, especially since it's based on
> PAGE_SIZE and we commonly have 64K page size on POWER), etc...

But isn't MMIO grouping effectively *at* the iommu?

> So I'm not too fan of making it entirely look like the iommu is the
> primary factor, but we -can-, that would be workable. I still prefer
> calling a cat a cat and exposing the grouping for what it is, as I think
> I've explained already above, tho. 

The trouble is the "group" analogy is more fitting to a partitionable
system, whereas on x86 we can really mix-n-match devices across iommus
fairly easily.  The iommu seems to be the common point to describe these
differences.

> > > Now some of this can be fixed with tweaks, and we've started doing it
> > > (we have a working pass-through using VFIO, forgot to mention that, it's
> > > just that we don't like what we had to do to get there).
> > 
> > This is a result of wanting to support *unmodified* x86 guests.  We
> > don't have the luxury of having a predefined pvDMA spec that all x86
> > OSes adhere to. 
> 
> No but you could emulate a HW iommu no ?

We can, but then we have to worry about supporting legacy, proprietary
OSes that may not have support or may make use of it differently.  As
Avi mentions, hardware is coming the eases the "pin the whole guest"
requirement and we may implement emulated iommus for the benefit of some
guests.

> >  The 32bit problem is unfortunate, but the priority use
> > case for assigning devices to guests is high performance I/O, which
> > usually entails modern, 64bit hardware.  I'd like to see us get to the
> > point of having emulated IOMMU hardware on x86, which could then be
> > backed by VFIO, but for now guest pinning is the most practical and
> > useful.
> 
> For your current case maybe. It's just not very future proof imho.
> Anyways, it's fixable, but the APIs as they are make it a bit clumsy.

You expect more 32bit devices in the future?

> > > Also our next generation chipset may drop support for PIO completely.
> > > 
> > > On the other hand, because PIO is just a special range of MMIO for us,
> > > we can do normal pass-through on it and don't need any of the emulation
> > > done qemu.
> > 
> > Maybe we can add mmap support to PIO regions on non-x86.
> 
> We have to yes. I haven't looked into it yet, it should be easy if VFIO
> kernel side starts using the "proper" PCI mmap interfaces in kernel (the
> same interfaces sysfs & proc use).

Patches welcome.

> > >   * MMIO constraints
> > > 
> > > The QEMU side VFIO code hard wires various constraints that are entirely
> > > based on various requirements you decided you have on x86 but don't
> > > necessarily apply to us :-)
> > > 
> > > Due to our paravirt nature, we don't need to masquerade the MSI-X table
> > > for example. At all. If the guest configures crap into it, too bad, it
> > > can only shoot itself in the foot since the host bridge enforce
> > > validation anyways as I explained earlier. Because it's all paravirt, we
> > > don't need to "translate" the interrupt vectors & addresses, the guest
> > > will call hyercalls to configure things anyways.
> > 
> > With interrupt remapping, we can allow the guest access to the MSI-X
> > table, but since that takes the host out of the loop, there's
> > effectively no way for the guest to correctly program it directly by
> > itself.
> 
> Right, I think what we need here is some kind of capabilities to
> "disable" those "features" of qemu vfio.c that aren't needed on our
> platform :-) Shouldn't be too hard. We need to make this runtime tho
> since different machines can have different "capabilities".

Sure, we'll probably eventually want a switch to push the MSI-X table to
KVM when it's available.

> > > We don't need to prevent MMIO pass-through for small BARs at all. This
> > > should be some kind of capability or flag passed by the arch. Our
> > > segmentation of the MMIO domain means that we can give entire segments
> > > to the guest and let it access anything in there (those segments are a
> > > multiple of the page size always). Worst case it will access outside of
> > > a device BAR within a segment and will cause the PE to go into error
> > > state, shooting itself in the foot, there is no risk of side effect
> > > outside of the guest boundaries.
> > 
> > Sure, this could be some kind of capability flag, maybe even implicit in
> > certain configurations.
> 
> Yup.
> 
> > > In fact, we don't even need to emulate BAR sizing etc... in theory. Our
> > > paravirt guests expect the BARs to have been already allocated for them
> > > by the firmware and will pick up the addresses from the device-tree :-)
> > > 
> > > Today we use a "hack", putting all 0's in there and triggering the linux
> > > code path to reassign unassigned resources (which will use BAR
> > > emulation) but that's not what we are -supposed- to do. Not a big deal
> > > and having the emulation there won't -hurt- us, it's just that we don't
> > > really need any of it.
> > > 
> > > We have a small issue with ROMs. Our current KVM only works with huge
> > > pages for guest memory but that is being fixed. So the way qemu maps the
> > > ROM copy into the guest address space doesn't work. It might be handy
> > > anyways to have a way for qemu to use MMIO emulation for ROM access as a
> > > fallback. I'll look into it.
> > 
> > So that means ROMs don't work for you on emulated devices either?  The
> > reason we read it once and map it into the guest is because Michael
> > Tsirkin found a section in the PCI spec that indicates devices can share
> > address decoders between BARs and ROM.
> 
> Yes, he is correct.
> 
> >   This means we can't just leave
> > the enabled bit set in the ROM BAR, because it could actually disable an
> > address decoder for a regular BAR.  We could slow-map the actual ROM,
> > enabling it around each read, but shadowing it seemed far more
> > efficient.
> 
> Right. We can slow map the ROM, or we can not care :-) At the end of the
> day, what is the difference here between a "guest" under qemu and the
> real thing bare metal on the machine ? IE. They have the same issue vs.
> accessing the ROM. IE. I don't see why qemu should try to make it safe
> to access it at any time while it isn't on a real machine. Since VFIO
> resets the devices before putting them in guest space, they should be
> accessible no ? (Might require a hard reset for some devices tho ... )

My primary motivator for doing the ROM the way it's done today is that I
get to push all the ROM handling off to QEMU core PCI code.  The ROM for
an assigned device is handled exactly like the ROM for an emulated
device except it might be generated by reading it from the hardware.
This gives us the benefit of things like rombar=0 if I want to hide the
ROM or romfile=<file> if I want to load an ipxe image for a device that
may not even have a physical ROM.  Not to mention I don't have to
special case ROM handling routines in VFIO.  So it actually has little
to do w/ making it safe to access the ROM at any time.

> In any case, it's not a big deal and we can sort it out, I'm happy to
> fallback to slow map to start with and eventually we will support small
> pages mappings on POWER anyways, it's a temporary limitation.

Perhaps this could also be fixed in the generic QEMU PCI ROM support so
it works for emulated devices too... code reuse paying off already ;)

> > >   * EEH
> > > 
> > > This is the name of those fancy error handling & isolation features I
> > > mentioned earlier. To some extent it's a superset of AER, but we don't
> > > generally expose AER to guests (or even the host), it's swallowed by
> > > firmware into something else that provides a superset (well mostly) of
> > > the AER information, and allow us to do those additional things like
> > > isolating/de-isolating, reset control etc...
> > > 
> > > Here too, we'll need arch specific APIs through VFIO. Not necessarily a
> > > huge deal, I mention it for completeness.
> > 
> > We expect to do AER via the VFIO netlink interface, which even though
> > its bashed below, would be quite extensible to supporting different
> > kinds of errors.
> 
> As could platform specific ioctls :-)

Is qemu going to poll for errors?

> > >    * Misc
> > > 
> > > There's lots of small bits and pieces... in no special order:
> > > 
> > >  - netlink ? WTF ! Seriously, we don't need a hybrid API with a bit of
> > > netlink and a bit of ioctl's ... it's not like there's something
> > > fundamentally  better for netlink vs. ioctl... it really depends what
> > > you are doing, and in this case I fail to see what netlink brings you
> > > other than bloat and more stupid userspace library deps.
> > 
> > The netlink interface is primarily for host->guest signaling.  I've only
> > implemented the remove command (since we're lacking a pcie-host in qemu
> > to do AER), but it seems to work quite well.  If you have suggestions
> > for how else we might do it, please let me know.  This seems to be the
> > sort of thing netlink is supposed to be used for.
> 
> I don't understand what the advantage of netlink is compared to just
> extending your existing VFIO ioctl interface, possibly using children
> fd's as we do for example with spufs but it's not a huge deal. It just
> that netlink has its own gotchas and I don't like multi-headed
> interfaces.

We could do yet another eventfd that triggers the VFIO user to go call
an ioctl to see what happened, but then we're locked into an ioctl
interface for something that we may want to more easily extend over
time.  As I said, it feels like this is what netlink is for and the
arguments against seem to be more gut reaction.

> > >  - I don't like too much the fact that VFIO provides yet another
> > > different API to do what we already have at least 2 kernel APIs for, ie,
> > > BAR mapping and config space access. At least it should be better at
> > > using the backend infrastructure of the 2 others (sysfs & procfs). I
> > > understand it wants to filter in some case (config space) and -maybe-
> > > yet another API is the right way to go but allow me to have my doubts.
> > 
> > The use of PCI sysfs is actually one of my complaints about current
> > device assignment.  To do assignment with an unprivileged guest we need
> > to open the PCI sysfs config file for it, then change ownership on a
> > handful of other PCI sysfs files, then there's this other pci-stub thing
> > to maintain ownership, but the kvm ioctls don't actually require it and
> > can grab onto any free device...  We are duplicating some of that in
> > VFIO, but we also put the ownership of the device behind a single device
> > file.  We do have the uiommu problem that we can't give an unprivileged
> > user ownership of that, but your usage model may actually make that
> > easier.  More below...
> > 
> > > One thing I thought about but you don't seem to like it ... was to use
> > > the need to represent the partitionable entity as groups in sysfs that I
> > > talked about earlier. Those could have per-device subdirs with the usual
> > > config & resource files, same semantic as the ones in the real device,
> > > but when accessed via the group they get filtering. I might or might not
> > > be practical in the end, tbd, but it would allow apps using a slightly
> > > modified libpci for example to exploit some of this.
> > 
> > I may be tainted by our disagreement that all the devices in a group
> > need to be exposed to the guest and qemu could just take a pointer to a
> > sysfs directory.  That seems very unlike qemu and pushes more of the
> > policy into qemu, which seems like the wrong direction.
> 
> I don't see how it pushes "policy" into qemu.
> 
> The "policy" here is imposed by the HW setup and exposed by the
> kernel :-) Giving qemu a group means qemu takes "owership" of that bunch
> of devices, so far I don't see what's policy about that. From there, it
> would be "handy" for people to just stop there and just see all the
> devices of the group show up in the guest, but by all means feel free to
> suggest a command line interface that allows to more precisely specify
> which of the devices in the group to pass through and at what address.

That's exactly the policy I'm thinking of.  Here's a group of devices,
do something with them...  Does qemu assign them all?  where?  does it
allow hotplug?  do we have ROMs?  should we?  from where?

> > >  - The qemu vfio code hooks directly into ioapic ... of course that
> > > won't fly with anything !x86
> > 
> > I spent a lot of time looking for an architecture neutral solution here,
> > but I don't think it exists.  Please prove me wrong.
> 
> No it doesn't I agree, that's why it should be some kind of notifier or
> function pointer setup by the platform specific code.

Hmm... it is.  I added a pci_get_irq() that returns a
platform/architecture specific translation of a PCI interrupt to it's
resulting system interrupt.  Implement this in your PCI root bridge.
There's a notifier for when this changes, so vfio will check
pci_get_irq() again, also to be implemented in the PCI root bridge code.
And a notifier that gets registered with that system interrupt and gets
notice for EOI... implemented in x86 ioapic, somewhere else for power.

> >   The problem is
> > that we have to disable INTx on an assigned device after it fires (VFIO
> > does this automatically).  If we don't do this, a non-responsive or
> > malicious guest could sit on the interrupt, causing it to fire
> > repeatedly as a DoS on the host.  The only indication that we can rely
> > on to re-enable INTx is when the guest CPU writes an EOI to the APIC.
> > We can't just wait for device accesses because a) the device CSRs are
> > (hopefully) direct mapped and we'd have to slow map them or attempt to
> > do some kind of dirty logging to detect when they're accesses b) what
> > constitutes an interrupt service is device specific.
> > 
> > That means we need to figure out how PCI interrupt 'A' (or B...)
> > translates to a GSI (Global System Interrupt - ACPI definition, but
> > hopefully a generic concept).  That GSI identifies a pin on an IOAPIC,
> > which will also see the APIC EOI.  And just to spice things up, the
> > guest can change the PCI to GSI mappings via ACPI.  I think the set of
> > callbacks I've added are generic (maybe I left ioapic in the name), but
> > yes they do need to be implemented for other architectures.  Patches
> > appreciated from those with knowledge of the systems and/or access to
> > device specs.  This is the only reason that I make QEMU VFIO only build
> > for x86.
> 
> Right, and we need to cook a similiar sauce for POWER, it's an area that
> has to be arch specific (and in fact specific to the specific HW machine
> being emulated), so we just need to find out what's the cleanest way for
> the plaform to "register" the right callbacks here.

Aside from the ioapic, I hope it's obvious hooks in the PCI root bridge
emulation.

[snip]
> > Rather than your "groups" idea, I've been mulling over whether we can
> > just expose the dependencies, configuration, and capabilities in sysfs
> > and build qemu commandlines to describe it.  For instance, if we simply
> > start with creating iommu nodes in sysfs, we could create links under
> > each iommu directory to the devices behind them.  Some kind of
> > capability file could define properties like whether it's page table
> > based or fixed iova window or the granularity of mapping the devices
> > behind it.  Once we have that, we could probably make uiommu attach to
> > each of those nodes.
> 
> Well, s/iommu/groups and you are pretty close to my original idea :-)
> 
> I don't mind that much what the details are, but I like the idea of not
> having to construct a 3-pages command line every time I want to
> pass-through a device, most "simple" usage scenario don't care that
> much.
> 
> > That means we know /dev/uiommu7 (random example) is our access to a
> > specific iommu with a given set of devices behind it.
> 
> Linking those sysfs iommus or groups to a /dev/ entry is fine by me.
>   
> >   If that iommu is
> > a PE (via those capability files), then a user space entity (trying hard
> > not to call it libvirt) can unbind all those devices from the host,
> > maybe bind the ones it wants to assign to a guest to vfio and bind the
> > others to pci-stub for safe keeping.  If you trust a user with
> > everything in a PE, bind all the devices to VFIO, chown all
> > the /dev/vfioX entries for those devices, and the /dev/uiommuX device.
> >
> > We might then come up with qemu command lines to describe interesting
> > configurations, such as:
> > 
> > -device iommu,model=PAPR,uiommu=/dev/uiommu7,id=iommu.0 \
> > -device pci-bus,...,iommu=iommu0,id=pci.0 \
> > -device vfio,host=ssss:bb:dd.f,bus=pci.0,addr=dd.f,id=hostdev0
> > 
> > The userspace entity would obviously need to put things in the same PE
> > in the right place, but it doesn't seem to take a lot of sysfs info to
> > get that right.
> > 
> > Today we do DMA mapping via the VFIO device because the capabilities of
> > the IOMMU domains change depending on which devices are connected (for
> > VT-d, the least common denominator of the IOMMUs in play).  Forcing the
> > DMA mappings through VFIO naturally forces the call order.  If we moved
> > to something like above, we could switch the DMA mapping to the uiommu
> > device, since the IOMMU would have fixed capabilities.
> 
> That makes sense.
> 
> > What gaps would something like this leave for your IOMMU granularity
> > problems?  I'll need to think through how it works when we don't want to
> > expose the iommu to the guest, maybe a model=none (default) that doesn't
> > need to be connected to a pci bus and maps all guest memory.  Thanks,
> 
> Well, I would map those "iommus" to PEs, so what remains is the path to
> put all the "other" bits and pieces such as inform qemu of the location
> and size of the MMIO segment(s) (so we can map the whole thing and not
> bother with individual BARs) etc... 

My assumption is that PEs are largely defined by the iommus already.
Are MMIO segments a property of the iommu too?  Thanks,

Alex

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox