cdrom: gdrom: fix block I/O and capacity setting
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
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
@@ -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
Re: [PATCH 1/2] cdrom: gdrom: replace port I/O with MMIO accessors
Quoted 42 lines · Florian Fuchs
GDROM_DATA_REGis a memory-mapped data register, but the driver usesoutsw()andinsw()only for this register. Replace this with local helpers using MMIO accessorsioread16_rep()/iowrite16_rep().Before, it oopsed accessing the data register, as the
io_port_baseP2SEG gets added to the argument inoutsw()/insw(), which leads to an unusable drive:BUG: unable to handle kernel paging request at 405f7080 PC: [<8c28d5b4>] gdrom_spicommand+0x6c/0xb0Signed-off-by Florian Fuchs <[email protected]>---
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@@ -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
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_REGis a memory-mapped data register, but the driver usesoutsw()andinsw()only for this register. Replace this with local helpers using MMIO accessorsioread16_rep()/iowrite16_rep().Before, it oopsed accessing the data register, as the
io_port_baseP2SEG gets added to the argument inoutsw()/insw(), which leads to an unusable drive:BUG: unable to handle kernel paging request at 405f7080 PC: [<8c28d5b4>] gdrom_spicommand+0x6c/0xb0Signed-off-by Florian Fuchs <[email protected]>---
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@@ -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
PATCH 2/2 cdrom: gdrom: update gendisk capacity on open
In reply to Florian FuchsUpdate 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
@@ -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
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)?
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
-0mutex_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
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 mutexgdrom_mutexors_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.
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 mutexgdrom_mutexors_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?
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 mutexgdrom_mutexors_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_changeand 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
John Paul Adrian Glaubitz
maintainerHi 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,
FlorianFlorian 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
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,
FlorianFlorian Fuchs (2):
cdrom: gdrom: replace port I/O with MMIO accessors
cdrom: gdrom: update gendisk capacity on opendrivers/cdrom/gdrom.c | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-)
Quoted 13 lines
Quoted 1 line
base-commit:
6de23f81a5e08be8fbf5e8d7e9febc72a5b5f27fThanks 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