aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS1
-rw-r--r--docs/system/arm/orangepi.rst16
-rw-r--r--hw/sd/sd.c86
-rw-r--r--tests/acceptance/boot_linux_console.py34
4 files changed, 106 insertions, 31 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 80fa883..310082f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1674,6 +1674,7 @@ F: hw/ssi/xilinx_*
SD (Secure Card)
M: Philippe Mathieu-Daudé <f4bug@amsat.org>
+L: qemu-block@nongnu.org
S: Odd Fixes
F: include/hw/sd/sd*
F: hw/sd/core.c
diff --git a/docs/system/arm/orangepi.rst b/docs/system/arm/orangepi.rst
index c41adad..6f23907 100644
--- a/docs/system/arm/orangepi.rst
+++ b/docs/system/arm/orangepi.rst
@@ -127,6 +127,16 @@ can be downloaded from:
Alternatively, you can also choose to build you own image with buildroot
using the orangepi_pc_defconfig. Also see https://buildroot.org for more information.
+When using an image as an SD card, it must be resized to a power of two. This can be
+done with the qemu-img command. It is recommended to only increase the image size
+instead of shrinking it to a power of two, to avoid loss of data. For example,
+to prepare a downloaded Armbian image, first extract it and then increase
+its size to one gigabyte as follows:
+
+.. code-block:: bash
+
+ $ qemu-img resize Armbian_19.11.3_Orangepipc_bionic_current_5.3.9.img 1G
+
You can choose to attach the selected image either as an SD card or as USB mass storage.
For example, to boot using the Orange Pi PC Debian image on SD card, simply add the -sd
argument and provide the proper root= kernel parameter:
@@ -213,12 +223,12 @@ Next, unzip the NetBSD image and write the U-Boot binary including SPL using:
$ dd if=/path/to/u-boot-sunxi-with-spl.bin of=armv7.img bs=1024 seek=8 conv=notrunc
Finally, before starting the machine the SD image must be extended such
-that the NetBSD kernel will not conclude the NetBSD partition is larger than
-the emulated SD card:
+that the size of the SD image is a power of two and that the NetBSD kernel
+will not conclude the NetBSD partition is larger than the emulated SD card:
.. code-block:: bash
- $ dd if=/dev/zero bs=1M count=64 >> armv7.img
+ $ qemu-img resize armv7.img 2G
Start the machine using the following command:
diff --git a/hw/sd/sd.c b/hw/sd/sd.c
index 5137168..fad9cf1 100644
--- a/hw/sd/sd.c
+++ b/hw/sd/sd.c
@@ -32,6 +32,7 @@
#include "qemu/osdep.h"
#include "qemu/units.h"
+#include "qemu/cutils.h"
#include "hw/irq.h"
#include "hw/registerfields.h"
#include "sysemu/block-backend.h"
@@ -920,6 +921,11 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req)
sd->multi_blk_cnt = 0;
}
+ if (sd_cmd_class[req.cmd] == 6 && FIELD_EX32(sd->ocr, OCR, CARD_CAPACITY)) {
+ /* Only Standard Capacity cards support class 6 commands */
+ return sd_illegal;
+ }
+
switch (req.cmd) {
/* Basic commands (Class 0 and Class 1) */
case 0: /* CMD0: GO_IDLE_STATE */
@@ -1165,12 +1171,15 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req)
case 17: /* CMD17: READ_SINGLE_BLOCK */
switch (sd->state) {
case sd_transfer_state:
+
+ if (addr + sd->blk_len > sd->size) {
+ sd->card_status |= ADDRESS_ERROR;
+ return sd_r1;
+ }
+
sd->state = sd_sendingdata_state;
sd->data_start = addr;
sd->data_offset = 0;
-
- if (sd->data_start + sd->blk_len > sd->size)
- sd->card_status |= ADDRESS_ERROR;
return sd_r1;
default:
@@ -1181,12 +1190,15 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req)
case 18: /* CMD18: READ_MULTIPLE_BLOCK */
switch (sd->state) {
case sd_transfer_state:
+
+ if (addr + sd->blk_len > sd->size) {
+ sd->card_status |= ADDRESS_ERROR;
+ return sd_r1;
+ }
+
sd->state = sd_sendingdata_state;
sd->data_start = addr;
sd->data_offset = 0;
-
- if (sd->data_start + sd->blk_len > sd->size)
- sd->card_status |= ADDRESS_ERROR;
return sd_r1;
default:
@@ -1226,17 +1238,23 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req)
/* Writing in SPI mode not implemented. */
if (sd->spi)
break;
+
+ if (addr + sd->blk_len > sd->size) {
+ sd->card_status |= ADDRESS_ERROR;
+ return sd_r1;
+ }
+
sd->state = sd_receivingdata_state;
sd->data_start = addr;
sd->data_offset = 0;
sd->blk_written = 0;
- if (sd->data_start + sd->blk_len > sd->size)
- sd->card_status |= ADDRESS_ERROR;
- if (sd_wp_addr(sd, sd->data_start))
+ if (sd_wp_addr(sd, sd->data_start)) {
sd->card_status |= WP_VIOLATION;
- if (sd->csd[14] & 0x30)
+ }
+ if (sd->csd[14] & 0x30) {
sd->card_status |= WP_VIOLATION;
+ }
return sd_r1;
default:
@@ -1250,17 +1268,23 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req)
/* Writing in SPI mode not implemented. */
if (sd->spi)
break;
+
+ if (addr + sd->blk_len > sd->size) {
+ sd->card_status |= ADDRESS_ERROR;
+ return sd_r1;
+ }
+
sd->state = sd_receivingdata_state;
sd->data_start = addr;
sd->data_offset = 0;
sd->blk_written = 0;
- if (sd->data_start + sd->blk_len > sd->size)
- sd->card_status |= ADDRESS_ERROR;
- if (sd_wp_addr(sd, sd->data_start))
+ if (sd_wp_addr(sd, sd->data_start)) {
sd->card_status |= WP_VIOLATION;
- if (sd->csd[14] & 0x30)
+ }
+ if (sd->csd[14] & 0x30) {
sd->card_status |= WP_VIOLATION;
+ }
return sd_r1;
default:
@@ -2100,12 +2124,36 @@ static void sd_realize(DeviceState *dev, Error **errp)
return;
}
- if (sd->blk && blk_is_read_only(sd->blk)) {
- error_setg(errp, "Cannot use read-only drive as SD card");
- return;
- }
-
if (sd->blk) {
+ int64_t blk_size;
+
+ if (blk_is_read_only(sd->blk)) {
+ error_setg(errp, "Cannot use read-only drive as SD card");
+ return;
+ }
+
+ blk_size = blk_getlength(sd->blk);
+ if (blk_size > 0 && !is_power_of_2(blk_size)) {
+ int64_t blk_size_aligned = pow2ceil(blk_size);
+ char *blk_size_str;
+
+ blk_size_str = size_to_str(blk_size);
+ error_setg(errp, "Invalid SD card size: %s", blk_size_str);
+ g_free(blk_size_str);
+
+ blk_size_str = size_to_str(blk_size_aligned);
+ error_append_hint(errp,
+ "SD card size has to be a power of 2, e.g. %s.\n"
+ "You can resize disk images with"
+ " 'qemu-img resize <imagefile> <new-size>'\n"
+ "(note that this will lose data if you make the"
+ " image smaller than it currently is).\n",
+ blk_size_str);
+ g_free(blk_size_str);
+
+ return;
+ }
+
ret = blk_set_perm(sd->blk, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE,
BLK_PERM_ALL, errp);
if (ret < 0) {
diff --git a/tests/acceptance/boot_linux_console.py b/tests/acceptance/boot_linux_console.py
index 3d02519..67c3b2f 100644
--- a/tests/acceptance/boot_linux_console.py
+++ b/tests/acceptance/boot_linux_console.py
@@ -28,6 +28,22 @@ try:
except CmdNotFoundError:
P7ZIP_AVAILABLE = False
+"""
+Round up to next power of 2
+"""
+def pow2ceil(x):
+ return 1 if x == 0 else 2**(x - 1).bit_length()
+
+"""
+Expand file size to next power of 2
+"""
+def image_pow2ceil_expand(path):
+ size = os.path.getsize(path)
+ size_aligned = pow2ceil(size)
+ if size != size_aligned:
+ with open(path, 'ab+') as fd:
+ fd.truncate(size_aligned)
+
class LinuxKernelTest(Test):
KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 '
@@ -620,6 +636,7 @@ class BootLinuxConsole(LinuxKernelTest):
"""
:avocado: tags=arch:arm
:avocado: tags=machine:orangepi-pc
+ :avocado: tags=device:sd
"""
deb_url = ('https://apt.armbian.com/pool/main/l/'
'linux-4.20.7-sunxi/linux-image-dev-sunxi_5.75_armhf.deb')
@@ -635,6 +652,7 @@ class BootLinuxConsole(LinuxKernelTest):
rootfs_path_xz = self.fetch_asset(rootfs_url, asset_hash=rootfs_hash)
rootfs_path = os.path.join(self.workdir, 'rootfs.cpio')
archive.lzma_uncompress(rootfs_path_xz, rootfs_path)
+ image_pow2ceil_expand(rootfs_path)
self.vm.set_console()
kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
@@ -669,9 +687,10 @@ class BootLinuxConsole(LinuxKernelTest):
"""
:avocado: tags=arch:arm
:avocado: tags=machine:orangepi-pc
+ :avocado: tags=device:sd
"""
- # This test download a 196MB compressed image and expand it to 932MB...
+ # This test download a 196MB compressed image and expand it to 1GB
image_url = ('https://dl.armbian.com/orangepipc/archive/'
'Armbian_19.11.3_Orangepipc_bionic_current_5.3.9.7z')
image_hash = '196a8ffb72b0123d92cea4a070894813d305c71e'
@@ -679,6 +698,7 @@ class BootLinuxConsole(LinuxKernelTest):
image_name = 'Armbian_19.11.3_Orangepipc_bionic_current_5.3.9.img'
image_path = os.path.join(self.workdir, image_name)
process.run("7z e -o%s %s" % (self.workdir, image_path_7z))
+ image_pow2ceil_expand(image_path)
self.vm.set_console()
self.vm.add_args('-drive', 'file=' + image_path + ',if=sd,format=raw',
@@ -710,8 +730,9 @@ class BootLinuxConsole(LinuxKernelTest):
"""
:avocado: tags=arch:arm
:avocado: tags=machine:orangepi-pc
+ :avocado: tags=device:sd
"""
- # This test download a 304MB compressed image and expand it to 1.3GB...
+ # This test download a 304MB compressed image and expand it to 2GB
deb_url = ('http://snapshot.debian.org/archive/debian/'
'20200108T145233Z/pool/main/u/u-boot/'
'u-boot-sunxi_2020.01%2Bdfsg-1_armhf.deb')
@@ -728,8 +749,9 @@ class BootLinuxConsole(LinuxKernelTest):
image_hash = '2babb29d36d8360adcb39c09e31060945259917a'
image_path_gz = self.fetch_asset(image_url, asset_hash=image_hash)
image_path = os.path.join(self.workdir, 'armv7.img')
- image_drive_args = 'if=sd,format=raw,snapshot=on,file=' + image_path
archive.gzip_uncompress(image_path_gz, image_path)
+ image_pow2ceil_expand(image_path)
+ image_drive_args = 'if=sd,format=raw,snapshot=on,file=' + image_path
# dd if=u-boot-sunxi-with-spl.bin of=armv7.img bs=1K seek=8 conv=notrunc
with open(uboot_path, 'rb') as f_in:
@@ -737,12 +759,6 @@ class BootLinuxConsole(LinuxKernelTest):
f_out.seek(8 * 1024)
shutil.copyfileobj(f_in, f_out)
- # Extend image, to avoid that NetBSD thinks the partition
- # inside the image is larger than device size itself
- f_out.seek(0, 2)
- f_out.seek(64 * 1024 * 1024, 1)
- f_out.write(bytearray([0x00]))
-
self.vm.set_console()
self.vm.add_args('-nic', 'user',
'-drive', image_drive_args,