Skip to contentSkip to conversation

cdrom: gdrom: fix block I/O and capacity setting

PATCH Superseded Florian Fuchs 3 participants 12 messages Apr 5 – Apr 6 SuperH

v1 of 4 Superseded Latest is v4 →

lore
Share & tools
lore
#0 lore raw

cover cdrom: gdrom: fix block I/O and capacity setting

Hi all,

This series fixes a gdrom driver Oops due to bad MMIO register access and fixes the missing updates of the block layer gendisk capacity that prevented ISO9660 mounts from working.

The change was tested on real Sega Dreamcast devices (PAL-E, NTSC-J) with physical CD-R discs and with GDEMU emulated discs. Before: Oops on mount and an unusable drive. After: Successfully able to mount and use the inserted medium.

Thanks,
Florian

Florian Fuchs (2):
cdrom: gdrom: replace port I/O with MMIO accessors
cdrom: gdrom: update gendisk capacity on open

 drivers/cdrom/gdrom.c | 39 +++++++++++++++++++++++++++++++++++----
 1 file changed, 35 insertions(+), 4 deletions(-)

base-commit: 6de23f81a5e08be8fbf5e8d7e9febc72a5b5f27f

Signature · 1 line
2.43.0
#1 lore raw

PATCH 1/2 cdrom: gdrom: replace port I/O with MMIO accessors

GDROM_DATA_REG is a memory-mapped data register, but the driver uses outsw() and insw() only for this register. Replace this with local helpers using MMIO accessors ioread16_rep() / iowrite16_rep().

Before, it oopsed accessing the data register, as the io_port_base P2SEG gets added to the argument in outsw() / insw(), which leads to an unusable drive:

        BUG: unable to handle kernel paging request at 405f7080
        PC: [<8c28d5b4>] gdrom_spicommand+0x6c/0xb0

---
The original Oops can be reproduced just by mounting a disc, like:
mount -t iso9660 -o ro /dev/gdrom /mnt

---
 drivers/cdrom/gdrom.c | 18 ++++++++++++++----
 1 file changed, 14 insertions(+), 4 deletions(-)
drivers/cdrom/gdrom.c +14 -4
index 4ba4dd06cbf4..dccf41fa5d0a 100644
@@ -171,6 +171,16 @@ static void gdrom_identifydevice(void *buf)
 		data[c] = __raw_readw(GDROM_DATA_REG);
 }
 
+static void gdrom_fifo_readw(void *buf, unsigned int words)
+{
+	ioread16_rep((void __iomem *)GDROM_DATA_REG, buf, words);
+}
+
+static void gdrom_fifo_writew(const void *buf, unsigned int words)
+{
+	iowrite16_rep((void __iomem *)GDROM_DATA_REG, buf, words);
+}
+
 static void gdrom_spicommand(void *spi_string, int buflen)
 {
 	short *cmd = spi_string;
@@ -198,7 +208,7 @@ static void gdrom_spicommand(void *spi_string, int buflen)
 		gdrom_getsense(NULL);
 		return;
 	}
-	outsw(GDROM_DATA_REG, cmd, 6);
+	gdrom_fifo_writew(cmd, 6);
 }
 
 
@@ -282,7 +292,7 @@ static int gdrom_readtoc_cmd(struct gdromtoc *toc, int session)
 		err = -EINVAL;
 		goto cleanup_readtoc;
 	}
-	insw(GDROM_DATA_REG, toc, tocsize/2);
+	gdrom_fifo_readw(toc, tocsize / 2);
 	if (gd.status & 0x01)
 		err = -EINVAL;
 
@@ -433,7 +443,7 @@ static int gdrom_getsense(short *bufstring)
 		GDROM_DEFAULT_TIMEOUT);
 	if (gd.pending)
 		goto cleanup_sense;
-	insw(GDROM_DATA_REG, &sense, sense_command->buflen/2);
+	gdrom_fifo_readw(sense, sense_command->buflen / 2);
 	if (sense[1] & 40) {
 		pr_info("Drive not ready - command aborted\n");
 		goto cleanup_sense;
@@ -612,7 +622,7 @@ static blk_status_t gdrom_readdisk_dma(struct request *req)
 		cpu_relax();
 	gd.pending = 1;
 	gd.transfer = 1;
-	outsw(GDROM_DATA_REG, &read_command->cmd, 6);
+	gdrom_fifo_writew(read_command->cmd, 6);
 	timeout = jiffies + HZ / 2;
 	/* Wait for any pending DMA to finish */
 	while (__raw_readb(GDROM_DMA_STATUS_REG) &&
Signature · 1 line
2.43.0
#2 lore raw

Re: [PATCH 1/2] cdrom: gdrom: replace port I/O with MMIO accessors

Quoted 42 lines · Florian Fuchs

GDROM_DATA_REG is a memory-mapped data register, but the driver uses outsw() and insw() only for this register. Replace this with local helpers using MMIO accessors ioread16_rep() / iowrite16_rep().

Before, it oopsed accessing the data register, as the io_port_base P2SEG gets added to the argument in outsw() / insw(), which leads to an unusable drive:

        BUG: unable to handle kernel paging request at 405f7080
        PC: [<8c28d5b4>] gdrom_spicommand+0x6c/0xb0

---
The original Oops can be reproduced just by mounting a disc, like:
mount -t iso9660 -o ro /dev/gdrom /mnt

---
 drivers/cdrom/gdrom.c | 18 ++++++++++++++----
 1 file changed, 14 insertions(+), 4 deletions(-)
drivers/cdrom/gdrom.c +10 -0
index 4ba4dd06cbf4..dccf41fa5d0a 100644
@@ -171,6 +171,16 @@ static void gdrom_identifydevice(void *buf)
                data[c] = __raw_readw(GDROM_DATA_REG);
 }
 
+static void gdrom_fifo_readw(void *buf, unsigned int words)
+{
+       ioread16_rep((void __iomem *)GDROM_DATA_REG, buf, words);
+}
+
+static void gdrom_fifo_writew(const void *buf, unsigned int words)
+{
+       iowrite16_rep((void __iomem *)GDROM_DATA_REG, buf, words);
+}
+
 static void gdrom_spicommand(void *spi_string, int buflen)
 {
        short *cmd = spi_string;

drivers/cdrom/gdrom.c static void gdrom_spicommand(void *spi_string, int buflen) · line 213

Quoted 7 lines
drivers/cdrom/gdrom.c +1 -1
@@ -198,7 +208,7 @@ static void gdrom_spicommand(void *spi_string, int buflen)
                gdrom_getsense(NULL);
                return;
        }
-       outsw(GDROM_DATA_REG, cmd, 6);
+       gdrom_fifo_writew(cmd, 6);
 }
 

This is one of those "how did this ever work to begin with" bugs when examined today - bur rather than introduce new local functions can we not just use either readsw(p, d, l)/writesw(p, d, l) or __raw_writew(p,d,l) and __raw_read(p,d,l) directly?

Adrian

#3 lore raw

Re: [PATCH 1/2] cdrom: gdrom: replace port I/O with MMIO accessors

Quoted 57 lines · Adrian McMenamin
Quoted 42 lines · Florian Fuchs

GDROM_DATA_REG is a memory-mapped data register, but the driver uses outsw() and insw() only for this register. Replace this with local helpers using MMIO accessors ioread16_rep() / iowrite16_rep().

Before, it oopsed accessing the data register, as the io_port_base P2SEG gets added to the argument in outsw() / insw(), which leads to an unusable drive:

        BUG: unable to handle kernel paging request at 405f7080
        PC: [<8c28d5b4>] gdrom_spicommand+0x6c/0xb0

---
The original Oops can be reproduced just by mounting a disc, like:
mount -t iso9660 -o ro /dev/gdrom /mnt

---
 drivers/cdrom/gdrom.c | 18 ++++++++++++++----
 1 file changed, 14 insertions(+), 4 deletions(-)
drivers/cdrom/gdrom.c +10 -0
index 4ba4dd06cbf4..dccf41fa5d0a 100644
@@ -171,6 +171,16 @@ static void gdrom_identifydevice(void *buf)
                data[c] = __raw_readw(GDROM_DATA_REG);
 }
 
+static void gdrom_fifo_readw(void *buf, unsigned int words)
+{
+       ioread16_rep((void __iomem *)GDROM_DATA_REG, buf, words);
+}
+
+static void gdrom_fifo_writew(const void *buf, unsigned int words)
+{
+       iowrite16_rep((void __iomem *)GDROM_DATA_REG, buf, words);
+}
+
 static void gdrom_spicommand(void *spi_string, int buflen)
 {
        short *cmd = spi_string;

drivers/cdrom/gdrom.c static void gdrom_spicommand(void *spi_string, int buflen) · line 213

Quoted 7 lines
drivers/cdrom/gdrom.c +1 -1
@@ -198,7 +208,7 @@ static void gdrom_spicommand(void *spi_string, int buflen)
                gdrom_getsense(NULL);
                return;
        }
-       outsw(GDROM_DATA_REG, cmd, 6);
+       gdrom_fifo_writew(cmd, 6);
 }
 

This is one of those "how did this ever work to begin with" bugs when examined today - bur rather than introduce new local functions can we not just use either readsw(p, d, l)/writesw(p, d, l) or __raw_writew(p,d,l) and __raw_read(p,d,l) directly?

Quoted 1 line

Adrian

Yeah, totally, I put them inline. I first had a loop with the __raw.* functions, that made more sense to extract.

Will send it, Thanks!
Florian

#4 lore raw

PATCH 2/2 cdrom: gdrom: update gendisk capacity on open

In reply to Florian Fuchs

Update the gendisk capacity of the media. Without the capacity, the block reads fail before reaching the request queue, which prevented ISO9660 mounts. Refresh the capacity from the TOC leadout in gdrom_bdops_open() so it checks the inserted media.

---
 drivers/cdrom/gdrom.c | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)
drivers/cdrom/gdrom.c +21 -0
index dccf41fa5d0a..854a511e3466 100644
@@ -484,6 +484,25 @@ static const struct cdrom_device_ops gdrom_ops = {
 				  CDC_RESET | CDC_DRIVE_STATUS | CDC_CD_R,
 };
 
+static int gdrom_update_capacity(void)
+{
+	sector_t cap;
+	int ret;
+
+	if (gdrom_drivestatus(gd.cd_info, CDSL_CURRENT) != CDS_DISC_OK) {
+		set_capacity(gd.disk, 0);
+		return -ENOMEDIUM;
+	}
+	ret = gdrom_readtoc_cmd(gd.toc, 0);
+	if (ret) {
+		set_capacity(gd.disk, 0);
+		return ret;
+	}
+	cap = (sector_t)get_entry_lba(gd.toc->leadout) * GD_TO_BLK;
+	set_capacity(gd.disk, cap);
+	return 0;
+}
+
 static int gdrom_bdops_open(struct gendisk *disk, blk_mode_t mode)
 {
 	int ret;
@@ -492,6 +511,8 @@ static int gdrom_bdops_open(struct gendisk *disk, blk_mode_t mode)
 
 	mutex_lock(&gdrom_mutex);
 	ret = cdrom_open(gd.cd_info, mode);
+	if (!ret)
+		ret = gdrom_update_capacity();
 	mutex_unlock(&gdrom_mutex);
 	return ret;
 }
Signature · 1 line
2.43.0
#5 lore raw

Re: [PATCH 2/2] cdrom: gdrom: update gendisk capacity on open

Quoted 6 lines · Florian Fuchs

Update the gendisk capacity of the media. Without the capacity, the block reads fail before reaching the request queue, which prevented ISO9660 mounts. Refresh the capacity from the TOC leadout in gdrom_bdops_open() so it checks the inserted media.

...

drivers/cdrom/gdrom.c static int gdrom_bdops_open(struct gendisk *disk, blk_mode_t mode) · line 518

Quoted 13 lines
drivers/cdrom/gdrom.c +3 -0
+
 static int gdrom_bdops_open(struct gendisk *disk, blk_mode_t mode)
 {
        int ret;
@@ -492,6 +511,8 @@ static int gdrom_bdops_open(struct gendisk *disk, blk_mode_t mode)
 
        mutex_lock(&gdrom_mutex);
        ret = cdrom_open(gd.cd_info, mode);
+       if (!ret)
+               ret = gdrom_update_capacity();
        mutex_unlock(&gdrom_mutex);
        return ret;
 }

Will this not reintrocue the potential race condition bug that was eliminated here -
https://github.com/mcmenaminadrian/linux/commit/2bbea6e117357d17842114c65e9a9cf2d13ae8a3
(not by me, I add)?

#6 lore raw

Re: [PATCH 2/2] cdrom: gdrom: update gendisk capacity on open

Quoted 14 lines · Adrian McMenamin
Quoted 6 lines · Florian Fuchs

Update the gendisk capacity of the media. Without the capacity, the block reads fail before reaching the request queue, which prevented ISO9660 mounts. Refresh the capacity from the TOC leadout in gdrom_bdops_open() so it checks the inserted media.

...

Quoted 6 lines
drivers/cdrom/gdrom.c +1 -0
+
 static int gdrom_bdops_open(struct gendisk *disk, blk_mode_t mode)
 {
        int ret;
@@ -492,6 +511,8 @@ static int gdrom_bdops_open(struct gendisk *disk, blk_mode_t mode)
 
Quoted 13 lines

drivers/cdrom/gdrom.c static int gdrom_bdops_open(struct gendisk *disk, blk_mode_t mode) · line 518

Quoted 7 lines
drivers/cdrom/gdrom.c +2 -0
        mutex_lock(&gdrom_mutex);
        ret = cdrom_open(gd.cd_info, mode);
+       if (!ret)
+               ret = gdrom_update_capacity();
        mutex_unlock(&gdrom_mutex);
        return ret;
 }

Will this not reintrocue the potential race condition bug that was eliminated here -
https://github.com/mcmenaminadrian/linux/commit/2bbea6e117357d17842114c65e9a9cf2d13ae8a3
(not by me, I add)?

You mean, because one of the functions in the call tree of gdrom_update_capacity() wants to get the mutex gdrom_mutex or s_umount? I checked the locally called functions, but don't see that one tries to aquire a lock.

I don't understand the exact potential of the race condition, maybe you can elaborate, where I should look.

Thank You!
Regards
Florian

#7 lore raw

Re: [PATCH 2/2] cdrom: gdrom: update gendisk capacity on open

Florian,

Quoted 9 lines · Florian Fuchs

You mean, because one of the functions in the call tree of gdrom_update_capacity() wants to get the mutex gdrom_mutex or s_umount? I checked the locally called functions, but don't see that one tries to aquire a lock.

I don't understand the exact potential of the race condition, maybe you can elaborate, where I should look.

Actually, having looked at the code path in detail too I agree that this won't recreate the problem the earlier patch addressed, so this looks good to me.

#8 lore raw

Re: [PATCH 2/2] cdrom: gdrom: update gendisk capacity on open

Quoted 12 lines · Adrian McMenamin

Florian,

Quoted 6 lines · Florian Fuchs

You mean, because one of the functions in the call tree of gdrom_update_capacity() wants to get the mutex gdrom_mutex or s_umount? I checked the locally called functions, but don't see that one tries to aquire a lock.

Quoted 8 lines
Quoted 3 lines

I don't understand the exact potential of the race condition, maybe you can elaborate, where I should look.

Actually, having looked at the code path in detail too I agree that this won't recreate the problem the earlier patch addressed, so this looks good to me.

Sorry - a further thought. This rechecking for capacity is only truly necessary if there has been a change of medium - is that right? So we can take the result of the earlier disk_check_media_change and if that returns true update the capacity? Is that not correct?

#9 lore raw

Re: [PATCH 2/2] cdrom: gdrom: update gendisk capacity on open

Quoted 23 lines · Adrian McMenamin
Quoted 12 lines · Adrian McMenamin

Florian,

Quoted 6 lines · Florian Fuchs

You mean, because one of the functions in the call tree of gdrom_update_capacity() wants to get the mutex gdrom_mutex or s_umount? I checked the locally called functions, but don't see that one tries to aquire a lock.

Quoted 8 lines
Quoted 3 lines

I don't understand the exact potential of the race condition, maybe you can elaborate, where I should look.

Actually, having looked at the code path in detail too I agree that this won't recreate the problem the earlier patch addressed, so this looks good to me.

Quoted 4 lines

Sorry - a further thought. This rechecking for capacity is only truly necessary if there has been a change of medium - is that right? So we can take the result of the earlier disk_check_media_change and if that returns true update the capacity? Is that not correct?

The problem is, that disk_check_media_change(disk) is false on initial gdrom_bdops_open() when the CD is already in it. It gets true there, if I swap the disc while running the DC.

So right now, it would check capacity on every gdrom_bdops_open() - which is mostly on every mount, instead of only if the disk was really changed in between two mounts.

Regards
Florian

#10 lore raw
In reply to Florian Fuchs

Hi Florian,

Quoted 18 lines · Florian Fuchs

Hi all,

This series fixes a gdrom driver Oops due to bad MMIO register access and fixes the missing updates of the block layer gendisk capacity that prevented ISO9660 mounts from working.

The change was tested on real Sega Dreamcast devices (PAL-E, NTSC-J) with physical CD-R discs and with GDEMU emulated discs. Before: Oops on mount and an unusable drive. After: Successfully able to mount and use the inserted medium.

Thanks,
Florian

Florian Fuchs (2):
cdrom: gdrom: replace port I/O with MMIO accessors
cdrom: gdrom: update gendisk capacity on open

Quoted 5 lines
 drivers/cdrom/gdrom.c | 39 +++++++++++++++++++++++++++++++++++----
 1 file changed, 35 insertions(+), 4 deletions(-)

base-commit: 6de23f81a5e08be8fbf5e8d7e9febc72a5b5f27f

Thanks a lot for the series! Can you give me any hints on what's the best method these days to boot a Linux kernel on Dreamcast these days? I have the hardware, but I never tried to boot Linux on it. An emulator might be even better.

Adrian

Signature · 4 lines
 .''`.  John Paul Adrian Glaubitz
: :' :  Debian Developer
`. `'   Physicist
  `-    GPG: 62FF 8A75 84E0 2956 9546  0006 7426 3B37 F5B5 F913
#11 lore raw

Hi Adrian,

Quoted 25 lines · John Paul Adrian Glaubitz

Hi Florian,

Quoted 22 lines · Florian Fuchs

Hi all,

This series fixes a gdrom driver Oops due to bad MMIO register access and fixes the missing updates of the block layer gendisk capacity that prevented ISO9660 mounts from working.

The change was tested on real Sega Dreamcast devices (PAL-E, NTSC-J) with physical CD-R discs and with GDEMU emulated discs. Before: Oops on mount and an unusable drive. After: Successfully able to mount and use the inserted medium.

Thanks,
Florian

Florian Fuchs (2):
cdrom: gdrom: replace port I/O with MMIO accessors
cdrom: gdrom: update gendisk capacity on open

 drivers/cdrom/gdrom.c | 39 +++++++++++++++++++++++++++++++++++----
 1 file changed, 35 insertions(+), 4 deletions(-)
Quoted 13 lines
Quoted 1 line

base-commit: 6de23f81a5e08be8fbf5e8d7e9febc72a5b5f27f

Thanks a lot for the series! Can you give me any hints on what's the best method these days to boot a Linux kernel on Dreamcast these days? I have the hardware, but I never tried to boot Linux on it. An emulator might be even better.

Adrian

Signature · 4 lines
 .''`.  John Paul Adrian Glaubitz
: :' :  Debian Developer
`. `'   Physicist
  `-    GPG: 62FF 8A75 84E0 2956 9546  0006 7426 3B37 F5B5 F913

I haven't found any DC emulator that can boot linux currently, sadly. I analyzed a bit with gxemul/lxdream, with the goal of booting it in the future, but, currently it traps in the pcpu setup. (might be some kconfig related, missing features of the emulators or sth.)

On the hardware:
You can either burn CDs, emulate the GDROM with a hardware modification kit "GDEMU" with sdcards or push the executable over serial cable (or via network).

- fastest test-iteration is via gdemu/sd-card or via serial-cable, that may take just a few minutes to transfer
- via serial, requires a serial console "coders" cable for the DC/USB and needs a CDROM with dcload-serial [1] CDI burned on it, then the executable can be pushed over serial to the DC. I currently use sh-boot [2] as the very thin bootloader which might not be necessary, but I haven't got it done otherwise, yet.
- to boot an already built CDI (Dreamcast selfboot image), you can burn it on a CD, either with helper binaries [3] or manual using wodim, it needs special crafted CD-layout [4], but there are tools for it [5].

There are different tools from the DC homebrew community, that help with creating bootable CDs/Images, please ping me anytime if I can give you any further information or a test CDI.

Florian

[1] https://github.com/KallistiOS/dcload-serial
[2] https://github.com/foxdrodd/sh-boot/blob/main/tools/dreamcast/Makefile
[3] https://github.com/alex-free/dreamcast-cdi-burner/
[4] https://mc.pp.se/dc/cdr.html
[5] https://github.com/KallistiOS/KallistiOS/tree/master/utils