diff mbox

[02/10] hw/sd/sd.c: QOMify

Message ID 1449851831-4966-3-git-send-email-peter.maydell@linaro.org
State New
Headers show

Commit Message

Peter Maydell Dec. 11, 2015, 4:37 p.m. UTC
Turn the SD card into a QOM device.
This conversion only changes the device itself; the various
functions which are effectively methods on the device are not
touched at this point.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/sd/sd.c         | 99 ++++++++++++++++++++++++++++++++++++++++++------------
 include/hw/sd/sd.h |  3 ++
 2 files changed, 80 insertions(+), 22 deletions(-)

Comments

Alistair Francis Dec. 17, 2015, 9:45 p.m. UTC | #1
On Fri, Dec 11, 2015 at 8:37 AM, Peter Maydell <peter.maydell@linaro.org> wrote:
> Turn the SD card into a QOM device.
> This conversion only changes the device itself; the various
> functions which are effectively methods on the device are not
> touched at this point.
>
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
> ---
>  hw/sd/sd.c         | 99 ++++++++++++++++++++++++++++++++++++++++++------------
>  include/hw/sd/sd.h |  3 ++
>  2 files changed, 80 insertions(+), 22 deletions(-)
>
> diff --git a/hw/sd/sd.c b/hw/sd/sd.c
> index 1a9935c..7c79217 100644
> --- a/hw/sd/sd.c
> +++ b/hw/sd/sd.c
> @@ -33,6 +33,8 @@
>  #include "sysemu/block-backend.h"
>  #include "hw/sd/sd.h"
>  #include "qemu/bitmap.h"
> +#include "hw/qdev-properties.h"
> +#include "qemu/error-report.h"
>
>  //#define DEBUG_SD 1
>
> @@ -77,6 +79,8 @@ enum SDCardStates {
>  };
>
>  struct SDState {
> +    DeviceState parent_obj;
> +
>      uint32_t mode;    /* current card mode, one of SDCardModes */
>      int32_t state;    /* current card state, one of SDCardStates */
>      uint32_t ocr;
> @@ -472,34 +476,26 @@ static const VMStateDescription sd_vmstate = {
>      }
>  };
>
> -/* We do not model the chip select pin, so allow the board to select
> -   whether card should be in SSI or MMC/SD mode.  It is also up to the
> -   board to ensure that ssi transfers only occur when the chip select
> -   is asserted.  */
> +/* Legacy initialization function for use by non-qdevified callers */
>  SDState *sd_init(BlockBackend *blk, bool is_spi)
>  {
> -    SDState *sd;
> +    DeviceState *dev;
> +    Error *err = NULL;
>
> -    if (blk && blk_is_read_only(blk)) {
> -        fprintf(stderr, "sd_init: Cannot use read-only drive\n");
> +    dev = qdev_create(NULL, TYPE_SD);
> +    qdev_prop_set_drive(dev, "drive", blk, &err);
> +    if (err) {
> +        error_report("sd_init failed: %s", error_get_pretty(err));
>          return NULL;
>      }
> -
> -    sd = (SDState *) g_malloc0(sizeof(SDState));
> -    sd->buf = blk_blockalign(blk, 512);
> -    sd->spi = is_spi;
> -    sd->enable = true;
> -    sd->blk = blk;
> -    sd_reset(sd);
> -    if (sd->blk) {
> -        /* Attach dev if not already attached.  (This call ignores an
> -         * error return code if sd->blk is already attached.) */
> -        /* FIXME ignoring blk_attach_dev() failure is dangerously brittle */
> -        blk_attach_dev(sd->blk, sd);

This functionality is removed with this patch. If the block is not
already attached won't this cause errors?

> -        blk_set_dev_ops(sd->blk, &sd_block_ops, sd);
> +    qdev_prop_set_bit(dev, "spi", is_spi);
> +    object_property_set_bool(OBJECT(dev), true, "realized", &err);
> +    if (err) {
> +        error_report("sd_init failed: %s", error_get_pretty(err));
> +        return NULL;
>      }
> -    vmstate_register(NULL, -1, &sd_vmstate, sd);
> -    return sd;
> +
> +    return SD(dev);
>  }
>
>  void sd_set_cb(SDState *sd, qemu_irq readonly, qemu_irq insert)
> @@ -1765,3 +1761,62 @@ void sd_enable(SDState *sd, bool enable)
>  {
>      sd->enable = enable;
>  }
> +
> +static void sd_instance_init(Object *obj)
> +{
> +    SDState *sd = SD(obj);
> +
> +    sd->enable = true;
> +}
> +
> +static void sd_realize(DeviceState *dev, Error ** errp)

You have one too many spaces here.

Thanks,

Alistair

> +{
> +    SDState *sd = SD(dev);
> +
> +    if (sd->blk && blk_is_read_only(sd->blk)) {
> +        error_setg(errp, "Cannot use read-only drive as SD card");
> +        return;
> +    }
> +
> +    sd->buf = blk_blockalign(sd->blk, 512);
> +
> +    if (sd->blk) {
> +        blk_set_dev_ops(sd->blk, &sd_block_ops, sd);
> +    }
> +
> +    sd_reset(sd);
> +}
> +
> +static Property sd_properties[] = {
> +    DEFINE_PROP_DRIVE("drive", SDState, blk),
> +    /* We do not model the chip select pin, so allow the board to select
> +     * whether card should be in SSI or MMC/SD mode.  It is also up to the
> +     * board to ensure that ssi transfers only occur when the chip select
> +     * is asserted.  */
> +    DEFINE_PROP_BOOL("spi", SDState, spi, false),
> +    DEFINE_PROP_END_OF_LIST()
> +};
> +
> +static void sd_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    dc->realize = sd_realize;
> +    dc->props = sd_properties;
> +    dc->vmsd = &sd_vmstate;
> +}
> +
> +static const TypeInfo sd_info = {
> +    .name = TYPE_SD,
> +    .parent = TYPE_DEVICE,
> +    .instance_size = sizeof(SDState),
> +    .class_init = sd_class_init,
> +    .instance_init = sd_instance_init,
> +};
> +
> +static void sd_register_types(void)
> +{
> +    type_register_static(&sd_info);
> +}
> +
> +type_init(sd_register_types)
> diff --git a/include/hw/sd/sd.h b/include/hw/sd/sd.h
> index 79adb5b..6997dfc 100644
> --- a/include/hw/sd/sd.h
> +++ b/include/hw/sd/sd.h
> @@ -68,6 +68,9 @@ typedef struct {
>
>  typedef struct SDState SDState;
>
> +#define TYPE_SD "sd"
> +#define SD(obj) OBJECT_CHECK(SDState, (obj), TYPE_SD)
> +
>  SDState *sd_init(BlockBackend *bs, bool is_spi);
>  int sd_do_command(SDState *sd, SDRequest *req,
>                    uint8_t *response);
> --
> 1.9.1
>
>
Peter Maydell Dec. 17, 2015, 10:11 p.m. UTC | #2
On 17 December 2015 at 21:45, Alistair Francis
<alistair.francis@xilinx.com> wrote:
> On Fri, Dec 11, 2015 at 8:37 AM, Peter Maydell <peter.maydell@linaro.org> wrote:
>> Turn the SD card into a QOM device.
>> This conversion only changes the device itself; the various
>> functions which are effectively methods on the device are not
>> touched at this point.
>>
>> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

>> @@ -472,34 +476,26 @@ static const VMStateDescription sd_vmstate = {
>>      }
>>  };
>>
>> -/* We do not model the chip select pin, so allow the board to select
>> -   whether card should be in SSI or MMC/SD mode.  It is also up to the
>> -   board to ensure that ssi transfers only occur when the chip select
>> -   is asserted.  */
>> +/* Legacy initialization function for use by non-qdevified callers */
>>  SDState *sd_init(BlockBackend *blk, bool is_spi)
>>  {
>> -    SDState *sd;
>> +    DeviceState *dev;
>> +    Error *err = NULL;
>>
>> -    if (blk && blk_is_read_only(blk)) {
>> -        fprintf(stderr, "sd_init: Cannot use read-only drive\n");
>> +    dev = qdev_create(NULL, TYPE_SD);
>> +    qdev_prop_set_drive(dev, "drive", blk, &err);
>> +    if (err) {
>> +        error_report("sd_init failed: %s", error_get_pretty(err));
>>          return NULL;
>>      }
>> -
>> -    sd = (SDState *) g_malloc0(sizeof(SDState));
>> -    sd->buf = blk_blockalign(blk, 512);
>> -    sd->spi = is_spi;
>> -    sd->enable = true;
>> -    sd->blk = blk;
>> -    sd_reset(sd);
>> -    if (sd->blk) {
>> -        /* Attach dev if not already attached.  (This call ignores an
>> -         * error return code if sd->blk is already attached.) */
>> -        /* FIXME ignoring blk_attach_dev() failure is dangerously brittle */
>> -        blk_attach_dev(sd->blk, sd);
>
> This functionality is removed with this patch. If the block is not
> already attached won't this cause errors?

The block backend is attached when we set the "drive" property
(which we do in this function in the new code just above).
[the actual call to blk_attach_dev() is in parse_drive() in
hw/core/qdev-properties-system.c.]

The blk_set_dev_ops() moves in this patch to sd_realize().

There is incidentally no longer any case where the block backend
could be already attached when we call sd_init(), because the
only case there was when it had been attached to the sdhci
controller device because of the x-drive property on that device,
and we removed the property in the previous patch. It's exactly
because setting a drive property does an implicit blk_attach_dev
that this code previously had to have special casing for
"attach of an already attached blk".

>> -        blk_set_dev_ops(sd->blk, &sd_block_ops, sd);
>> +    qdev_prop_set_bit(dev, "spi", is_spi);
>> +    object_property_set_bool(OBJECT(dev), true, "realized", &err);
>> +    if (err) {
>> +        error_report("sd_init failed: %s", error_get_pretty(err));
>> +        return NULL;
>>      }
>> -    vmstate_register(NULL, -1, &sd_vmstate, sd);
>> -    return sd;
>> +
>> +    return SD(dev);
>>  }
>>
>>  void sd_set_cb(SDState *sd, qemu_irq readonly, qemu_irq insert)
>> @@ -1765,3 +1761,62 @@ void sd_enable(SDState *sd, bool enable)
>>  {
>>      sd->enable = enable;
>>  }
>> +
>> +static void sd_instance_init(Object *obj)
>> +{
>> +    SDState *sd = SD(obj);
>> +
>> +    sd->enable = true;
>> +}
>> +
>> +static void sd_realize(DeviceState *dev, Error ** errp)
>
> You have one too many spaces here.

Yep, will fix.

thanks
-- PMM
Alistair Francis Dec. 18, 2015, 12:20 a.m. UTC | #3
On Thu, Dec 17, 2015 at 2:11 PM, Peter Maydell <peter.maydell@linaro.org> wrote:
> On 17 December 2015 at 21:45, Alistair Francis
> <alistair.francis@xilinx.com> wrote:
>> On Fri, Dec 11, 2015 at 8:37 AM, Peter Maydell <peter.maydell@linaro.org> wrote:
>>> Turn the SD card into a QOM device.
>>> This conversion only changes the device itself; the various
>>> functions which are effectively methods on the device are not
>>> touched at this point.
>>>
>>> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
>
>>> @@ -472,34 +476,26 @@ static const VMStateDescription sd_vmstate = {
>>>      }
>>>  };
>>>
>>> -/* We do not model the chip select pin, so allow the board to select
>>> -   whether card should be in SSI or MMC/SD mode.  It is also up to the
>>> -   board to ensure that ssi transfers only occur when the chip select
>>> -   is asserted.  */
>>> +/* Legacy initialization function for use by non-qdevified callers */
>>>  SDState *sd_init(BlockBackend *blk, bool is_spi)
>>>  {
>>> -    SDState *sd;
>>> +    DeviceState *dev;
>>> +    Error *err = NULL;
>>>
>>> -    if (blk && blk_is_read_only(blk)) {
>>> -        fprintf(stderr, "sd_init: Cannot use read-only drive\n");
>>> +    dev = qdev_create(NULL, TYPE_SD);
>>> +    qdev_prop_set_drive(dev, "drive", blk, &err);
>>> +    if (err) {
>>> +        error_report("sd_init failed: %s", error_get_pretty(err));
>>>          return NULL;
>>>      }
>>> -
>>> -    sd = (SDState *) g_malloc0(sizeof(SDState));
>>> -    sd->buf = blk_blockalign(blk, 512);
>>> -    sd->spi = is_spi;
>>> -    sd->enable = true;
>>> -    sd->blk = blk;
>>> -    sd_reset(sd);
>>> -    if (sd->blk) {
>>> -        /* Attach dev if not already attached.  (This call ignores an
>>> -         * error return code if sd->blk is already attached.) */
>>> -        /* FIXME ignoring blk_attach_dev() failure is dangerously brittle */
>>> -        blk_attach_dev(sd->blk, sd);
>>
>> This functionality is removed with this patch. If the block is not
>> already attached won't this cause errors?
>
> The block backend is attached when we set the "drive" property
> (which we do in this function in the new code just above).
> [the actual call to blk_attach_dev() is in parse_drive() in
> hw/core/qdev-properties-system.c.]
>
> The blk_set_dev_ops() moves in this patch to sd_realize().
>
> There is incidentally no longer any case where the block backend
> could be already attached when we call sd_init(), because the
> only case there was when it had been attached to the sdhci
> controller device because of the x-drive property on that device,
> and we removed the property in the previous patch. It's exactly
> because setting a drive property does an implicit blk_attach_dev
> that this code previously had to have special casing for
> "attach of an already attached blk".

Ok, fair enough, just thought I would check. The rest of the logic
looks good then.

Thanks,

Alistair

>
>>> -        blk_set_dev_ops(sd->blk, &sd_block_ops, sd);
>>> +    qdev_prop_set_bit(dev, "spi", is_spi);
>>> +    object_property_set_bool(OBJECT(dev), true, "realized", &err);
>>> +    if (err) {
>>> +        error_report("sd_init failed: %s", error_get_pretty(err));
>>> +        return NULL;
>>>      }
>>> -    vmstate_register(NULL, -1, &sd_vmstate, sd);
>>> -    return sd;
>>> +
>>> +    return SD(dev);
>>>  }
>>>
>>>  void sd_set_cb(SDState *sd, qemu_irq readonly, qemu_irq insert)
>>> @@ -1765,3 +1761,62 @@ void sd_enable(SDState *sd, bool enable)
>>>  {
>>>      sd->enable = enable;
>>>  }
>>> +
>>> +static void sd_instance_init(Object *obj)
>>> +{
>>> +    SDState *sd = SD(obj);
>>> +
>>> +    sd->enable = true;
>>> +}
>>> +
>>> +static void sd_realize(DeviceState *dev, Error ** errp)
>>
>> You have one too many spaces here.
>
> Yep, will fix.
>
> thanks
> -- PMM
>
Peter Crosthwaite Dec. 19, 2015, 9:36 p.m. UTC | #4
On Fri, Dec 11, 2015 at 04:37:03PM +0000, Peter Maydell wrote:
> Turn the SD card into a QOM device.
> This conversion only changes the device itself; the various
> functions which are effectively methods on the device are not
> touched at this point.
> 
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
> ---
>  hw/sd/sd.c         | 99 ++++++++++++++++++++++++++++++++++++++++++------------
>  include/hw/sd/sd.h |  3 ++
>  2 files changed, 80 insertions(+), 22 deletions(-)
> 
> diff --git a/hw/sd/sd.c b/hw/sd/sd.c
> index 1a9935c..7c79217 100644
> --- a/hw/sd/sd.c
> +++ b/hw/sd/sd.c
> @@ -33,6 +33,8 @@
>  #include "sysemu/block-backend.h"
>  #include "hw/sd/sd.h"
>  #include "qemu/bitmap.h"
> +#include "hw/qdev-properties.h"
> +#include "qemu/error-report.h"
>  
>  //#define DEBUG_SD 1
>  
> @@ -77,6 +79,8 @@ enum SDCardStates {
>  };
>  
>  struct SDState {
> +    DeviceState parent_obj;
> +
>      uint32_t mode;    /* current card mode, one of SDCardModes */
>      int32_t state;    /* current card state, one of SDCardStates */
>      uint32_t ocr;
> @@ -472,34 +476,26 @@ static const VMStateDescription sd_vmstate = {
>      }
>  };
>  
> -/* We do not model the chip select pin, so allow the board to select
> -   whether card should be in SSI or MMC/SD mode.  It is also up to the
> -   board to ensure that ssi transfers only occur when the chip select
> -   is asserted.  */
> +/* Legacy initialization function for use by non-qdevified callers */
>  SDState *sd_init(BlockBackend *blk, bool is_spi)
>  {
> -    SDState *sd;
> +    DeviceState *dev;
> +    Error *err = NULL;
>  
> -    if (blk && blk_is_read_only(blk)) {
> -        fprintf(stderr, "sd_init: Cannot use read-only drive\n");
> +    dev = qdev_create(NULL, TYPE_SD);
> +    qdev_prop_set_drive(dev, "drive", blk, &err);
> +    if (err) {
> +        error_report("sd_init failed: %s", error_get_pretty(err));
>          return NULL;
>      }
> -
> -    sd = (SDState *) g_malloc0(sizeof(SDState));
> -    sd->buf = blk_blockalign(blk, 512);
> -    sd->spi = is_spi;
> -    sd->enable = true;
> -    sd->blk = blk;
> -    sd_reset(sd);
> -    if (sd->blk) {
> -        /* Attach dev if not already attached.  (This call ignores an
> -         * error return code if sd->blk is already attached.) */
> -        /* FIXME ignoring blk_attach_dev() failure is dangerously brittle */
> -        blk_attach_dev(sd->blk, sd);
> -        blk_set_dev_ops(sd->blk, &sd_block_ops, sd);
> +    qdev_prop_set_bit(dev, "spi", is_spi);
> +    object_property_set_bool(OBJECT(dev), true, "realized", &err);
> +    if (err) {
> +        error_report("sd_init failed: %s", error_get_pretty(err));
> +        return NULL;
>      }
> -    vmstate_register(NULL, -1, &sd_vmstate, sd);
> -    return sd;
> +
> +    return SD(dev);
>  }
>  
>  void sd_set_cb(SDState *sd, qemu_irq readonly, qemu_irq insert)
> @@ -1765,3 +1761,62 @@ void sd_enable(SDState *sd, bool enable)
>  {
>      sd->enable = enable;
>  }
> +
> +static void sd_instance_init(Object *obj)
> +{
> +    SDState *sd = SD(obj);
> +
> +    sd->enable = true;
> +}
> +
> +static void sd_realize(DeviceState *dev, Error ** errp)
> +{
> +    SDState *sd = SD(dev);
> +
> +    if (sd->blk && blk_is_read_only(sd->blk)) {
> +        error_setg(errp, "Cannot use read-only drive as SD card");
> +        return;
> +    }
> +
> +    sd->buf = blk_blockalign(sd->blk, 512);
> +
> +    if (sd->blk) {
> +        blk_set_dev_ops(sd->blk, &sd_block_ops, sd);
> +    }
> +
> +    sd_reset(sd);
> +}
> +
> +static Property sd_properties[] = {
> +    DEFINE_PROP_DRIVE("drive", SDState, blk),
> +    /* We do not model the chip select pin, so allow the board to select
> +     * whether card should be in SSI or MMC/SD mode.  It is also up to the
> +     * board to ensure that ssi transfers only occur when the chip select
> +     * is asserted.  */
> +    DEFINE_PROP_BOOL("spi", SDState, spi, false),
> +    DEFINE_PROP_END_OF_LIST()
> +};
> +
> +static void sd_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    dc->realize = sd_realize;
> +    dc->props = sd_properties;
> +    dc->vmsd = &sd_vmstate;
> +}
> +
> +static const TypeInfo sd_info = {
> +    .name = TYPE_SD,
> +    .parent = TYPE_DEVICE,
> +    .instance_size = sizeof(SDState),
> +    .class_init = sd_class_init,
> +    .instance_init = sd_instance_init,
> +};
> +
> +static void sd_register_types(void)
> +{
> +    type_register_static(&sd_info);
> +}
> +
> +type_init(sd_register_types)
> diff --git a/include/hw/sd/sd.h b/include/hw/sd/sd.h
> index 79adb5b..6997dfc 100644
> --- a/include/hw/sd/sd.h
> +++ b/include/hw/sd/sd.h
> @@ -68,6 +68,9 @@ typedef struct {
>  
>  typedef struct SDState SDState;
>  
> +#define TYPE_SD "sd"

Can we get "card" in there? I think unqualified SD should be usable to refer
to the bus standard.

Otherwise,

Reviewed-by: Peter Crosthwaite <crosthwaite.peter@gmail.com>

Regards,
Peter

> +#define SD(obj) OBJECT_CHECK(SDState, (obj), TYPE_SD)
> +
>  SDState *sd_init(BlockBackend *bs, bool is_spi);
>  int sd_do_command(SDState *sd, SDRequest *req,
>                    uint8_t *response);
> -- 
> 1.9.1
>
Peter Maydell Dec. 20, 2015, 5:07 p.m. UTC | #5
On 19 December 2015 at 21:36, Peter Crosthwaite
<crosthwaitepeter@gmail.com> wrote:
> On Fri, Dec 11, 2015 at 04:37:03PM +0000, Peter Maydell wrote:
>> Turn the SD card into a QOM device.
>> This conversion only changes the device itself; the various
>> functions which are effectively methods on the device are not
>> touched at this point.

>> +#define TYPE_SD "sd"
>
> Can we get "card" in there? I think unqualified SD should be usable
> to refer to the bus standard.

Sure. I don't have strong opinions on any of the type names
in this series, and I was wondering about sticking 'card' in.
"sdcard" or "sd-card" ?

thanks
-- PMM
Peter Crosthwaite Dec. 20, 2015, 6:25 p.m. UTC | #6
On Sun, Dec 20, 2015 at 9:07 AM, Peter Maydell <peter.maydell@linaro.org> wrote:
> On 19 December 2015 at 21:36, Peter Crosthwaite
> <crosthwaitepeter@gmail.com> wrote:
>> On Fri, Dec 11, 2015 at 04:37:03PM +0000, Peter Maydell wrote:
>>> Turn the SD card into a QOM device.
>>> This conversion only changes the device itself; the various
>>> functions which are effectively methods on the device are not
>>> touched at this point.
>
>>> +#define TYPE_SD "sd"
>>
>> Can we get "card" in there? I think unqualified SD should be usable
>> to refer to the bus standard.
>
> Sure. I don't have strong opinions on any of the type names
> in this series, and I was wondering about sticking 'card' in.
> "sdcard" or "sd-card" ?
>

I think "sd-card"

Regards,
Peter

> thanks
> -- PMM
diff mbox

Patch

diff --git a/hw/sd/sd.c b/hw/sd/sd.c
index 1a9935c..7c79217 100644
--- a/hw/sd/sd.c
+++ b/hw/sd/sd.c
@@ -33,6 +33,8 @@ 
 #include "sysemu/block-backend.h"
 #include "hw/sd/sd.h"
 #include "qemu/bitmap.h"
+#include "hw/qdev-properties.h"
+#include "qemu/error-report.h"
 
 //#define DEBUG_SD 1
 
@@ -77,6 +79,8 @@  enum SDCardStates {
 };
 
 struct SDState {
+    DeviceState parent_obj;
+
     uint32_t mode;    /* current card mode, one of SDCardModes */
     int32_t state;    /* current card state, one of SDCardStates */
     uint32_t ocr;
@@ -472,34 +476,26 @@  static const VMStateDescription sd_vmstate = {
     }
 };
 
-/* We do not model the chip select pin, so allow the board to select
-   whether card should be in SSI or MMC/SD mode.  It is also up to the
-   board to ensure that ssi transfers only occur when the chip select
-   is asserted.  */
+/* Legacy initialization function for use by non-qdevified callers */
 SDState *sd_init(BlockBackend *blk, bool is_spi)
 {
-    SDState *sd;
+    DeviceState *dev;
+    Error *err = NULL;
 
-    if (blk && blk_is_read_only(blk)) {
-        fprintf(stderr, "sd_init: Cannot use read-only drive\n");
+    dev = qdev_create(NULL, TYPE_SD);
+    qdev_prop_set_drive(dev, "drive", blk, &err);
+    if (err) {
+        error_report("sd_init failed: %s", error_get_pretty(err));
         return NULL;
     }
-
-    sd = (SDState *) g_malloc0(sizeof(SDState));
-    sd->buf = blk_blockalign(blk, 512);
-    sd->spi = is_spi;
-    sd->enable = true;
-    sd->blk = blk;
-    sd_reset(sd);
-    if (sd->blk) {
-        /* Attach dev if not already attached.  (This call ignores an
-         * error return code if sd->blk is already attached.) */
-        /* FIXME ignoring blk_attach_dev() failure is dangerously brittle */
-        blk_attach_dev(sd->blk, sd);
-        blk_set_dev_ops(sd->blk, &sd_block_ops, sd);
+    qdev_prop_set_bit(dev, "spi", is_spi);
+    object_property_set_bool(OBJECT(dev), true, "realized", &err);
+    if (err) {
+        error_report("sd_init failed: %s", error_get_pretty(err));
+        return NULL;
     }
-    vmstate_register(NULL, -1, &sd_vmstate, sd);
-    return sd;
+
+    return SD(dev);
 }
 
 void sd_set_cb(SDState *sd, qemu_irq readonly, qemu_irq insert)
@@ -1765,3 +1761,62 @@  void sd_enable(SDState *sd, bool enable)
 {
     sd->enable = enable;
 }
+
+static void sd_instance_init(Object *obj)
+{
+    SDState *sd = SD(obj);
+
+    sd->enable = true;
+}
+
+static void sd_realize(DeviceState *dev, Error ** errp)
+{
+    SDState *sd = SD(dev);
+
+    if (sd->blk && blk_is_read_only(sd->blk)) {
+        error_setg(errp, "Cannot use read-only drive as SD card");
+        return;
+    }
+
+    sd->buf = blk_blockalign(sd->blk, 512);
+
+    if (sd->blk) {
+        blk_set_dev_ops(sd->blk, &sd_block_ops, sd);
+    }
+
+    sd_reset(sd);
+}
+
+static Property sd_properties[] = {
+    DEFINE_PROP_DRIVE("drive", SDState, blk),
+    /* We do not model the chip select pin, so allow the board to select
+     * whether card should be in SSI or MMC/SD mode.  It is also up to the
+     * board to ensure that ssi transfers only occur when the chip select
+     * is asserted.  */
+    DEFINE_PROP_BOOL("spi", SDState, spi, false),
+    DEFINE_PROP_END_OF_LIST()
+};
+
+static void sd_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->realize = sd_realize;
+    dc->props = sd_properties;
+    dc->vmsd = &sd_vmstate;
+}
+
+static const TypeInfo sd_info = {
+    .name = TYPE_SD,
+    .parent = TYPE_DEVICE,
+    .instance_size = sizeof(SDState),
+    .class_init = sd_class_init,
+    .instance_init = sd_instance_init,
+};
+
+static void sd_register_types(void)
+{
+    type_register_static(&sd_info);
+}
+
+type_init(sd_register_types)
diff --git a/include/hw/sd/sd.h b/include/hw/sd/sd.h
index 79adb5b..6997dfc 100644
--- a/include/hw/sd/sd.h
+++ b/include/hw/sd/sd.h
@@ -68,6 +68,9 @@  typedef struct {
 
 typedef struct SDState SDState;
 
+#define TYPE_SD "sd"
+#define SD(obj) OBJECT_CHECK(SDState, (obj), TYPE_SD)
+
 SDState *sd_init(BlockBackend *bs, bool is_spi);
 int sd_do_command(SDState *sd, SDRequest *req,
                   uint8_t *response);