1
v1:
1
v1:
2
1. Fix boundary_count overflow
2
1. Fix boundary_count overflow
3
2. Fix data transfer did not complete if data size is bigger then SDMA Buffer Boundary
3
2. Fix data transfer did not complete if data size is bigger then SDMA Buffer Boundary
4
4
5
v2:
6
1. fix typo
7
2. update to none RFC patch
8
3. check the most upper byte of SDMA System Address Register (0x00) is written,
9
then restarts SDMA data transfer.
10
5
Jamin Lin (2):
11
Jamin Lin (2):
6
RFC:sd:sdhci: Fix boundary_count overflow in
12
hw/sd/sdhci: Fix boundary_count overflow in
7
sdhci_sdma_transfer_multi_blocks
13
sdhci_sdma_transfer_multi_blocks
8
RFC:sd:sdhci: Fix data transfer did not complete if data size is
14
hw/sd/sdhci: Fix data transfer did not complete if data size is bigger
9
bigger then SDMA Buffer Boundary
15
than SDMA Buffer Boundary
10
16
11
hw/sd/sdhci.c | 27 +++++++++++++--------------
17
hw/sd/sdhci.c | 20 ++++++++++++++++----
12
1 file changed, 13 insertions(+), 14 deletions(-)
18
1 file changed, 16 insertions(+), 4 deletions(-)
13
19
14
--
20
--
15
2.34.1
21
2.34.1
diff view generated by jsdifflib
1
How to reproduce it:
1
How to reproduce it:
2
1. The value of "s->blksie" was 0x7200. The bits[14:12] was "111", so the buffer
2
1. The value of "s->blksie" was 0x7200. The bits[14:12] was "111", so the buffer
3
boundary was 0x80000.(512Kbytes). This SDMA buffer boundary the same as
3
boundary was 0x80000.(512Kbytes). This SDMA buffer boundary was the same as
4
u-boot default value.
4
u-boot default value.
5
The bit[11:0] is "001000000000", so the block size is 0x200.(512bytes)
5
The bit[11:0] was "001000000000", so the block size was 0x200.(512bytes)
6
2. The SDMA address was 0x83123456 which was not page aligned and
6
2. The SDMA address was 0x83123456 which was not page aligned and
7
"s->sdmasysad % boundary_chk" was 0x23456. The value of boundary_count was
7
"s->sdmasysad % boundary_chk" was 0x23456. The value of boundary_count was
8
0x5cbaa.("boundary_chk - (s->sdmasysad % boundary_chk)" -->
8
0x5cbaa.("boundary_chk - (s->sdmasysad % boundary_chk)" -->
9
"(0x80000 - 0x23456)")
9
"(0x80000 - 0x23456)")
10
10
11
However, boundary_count did not aligned the block size 512 bytes and the SDMA
11
However, boundary_count did not align the block size 512 bytes and the SDMA
12
address is not page aligned(0x80000), so the following if-statement never be true,
12
address was not page aligned(0x80000), so the following if-statement never be true,
13
```
13
```
14
if (((boundary_count + begin) < block_size) && page_aligned)
14
if (((boundary_count + begin) < block_size) && page_aligned)
15
````
15
````
16
16
17
Finally, it caused boundary_count overflow because its data type was uint32_t.
17
Finally, it caused boundary_count overflow because its data type was uint32_t.
18
Ex: the last boundary_count was 0x1aa and "0x1aa - 0x200" became "0xffffffaa".
18
Ex: the last boundary_count was 0x1aa and "0x1aa - 0x200" became "0xffffffaa".
19
It is the wrong behavior.
19
It is the wrong behavior.
20
20
21
To fix it, it seems we can directly check the "boundary_count < blocksize"
21
To fix it, change to check boundary_count smaller than block size if system
22
instead of "boundary_count < blocksize && page_aligned"
22
address did not page align
23
23
24
Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
24
Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
25
---
25
---
26
hw/sd/sdhci.c | 18 ++++--------------
26
hw/sd/sdhci.c | 8 ++++----
27
1 file changed, 4 insertions(+), 14 deletions(-)
27
1 file changed, 4 insertions(+), 4 deletions(-)
28
28
29
diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c
29
diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c
30
index XXXXXXX..XXXXXXX 100644
30
index XXXXXXX..XXXXXXX 100644
31
--- a/hw/sd/sdhci.c
31
--- a/hw/sd/sdhci.c
32
+++ b/hw/sd/sdhci.c
32
+++ b/hw/sd/sdhci.c
33
@@ -XXX,XX +XXX,XX @@ static void sdhci_write_dataport(SDHCIState *s, uint32_t value, unsigned size)
34
/* Multi block SDMA transfer */
35
static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s)
36
{
37
- bool page_aligned = false;
38
unsigned int begin;
39
const uint16_t block_size = s->blksize & BLOCK_SIZE_MASK;
40
uint32_t boundary_chk = 1 << (((s->blksize & ~BLOCK_SIZE_MASK) >> 12) + 12);
41
@@ -XXX,XX +XXX,XX @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s)
42
return;
43
}
44
45
- /*
46
- * XXX: Some sd/mmc drivers (for example, u-boot-slp) do not account for
47
- * possible stop at page boundary if initial address is not page aligned,
48
- * allow them to work properly
49
- */
50
- if ((s->sdmasysad % boundary_chk) == 0) {
51
- page_aligned = true;
52
- }
53
-
54
s->prnsts |= SDHC_DATA_INHIBIT | SDHC_DAT_LINE_ACTIVE;
55
if (s->trnmod & SDHC_TRNS_READ) {
56
s->prnsts |= SDHC_DOING_READ;
57
@@ -XXX,XX +XXX,XX @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s)
33
@@ -XXX,XX +XXX,XX @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s)
58
sdbus_read_data(&s->sdbus, s->fifo_buffer, block_size);
34
sdbus_read_data(&s->sdbus, s->fifo_buffer, block_size);
59
}
35
}
60
begin = s->data_count;
36
begin = s->data_count;
61
- if (((boundary_count + begin) < block_size) && page_aligned) {
37
- if (((boundary_count + begin) < block_size) && page_aligned) {
62
+ if (((boundary_count + begin) < block_size)) {
38
+ if (((boundary_count + begin) < block_size) && !page_aligned) {
63
s->data_count = boundary_count + begin;
39
s->data_count = boundary_count + begin;
64
boundary_count = 0;
40
boundary_count = 0;
65
} else {
41
} else {
66
@@ -XXX,XX +XXX,XX @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s)
42
@@ -XXX,XX +XXX,XX @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s)
67
if (s->data_count == block_size) {
43
if (s->data_count == block_size) {
...
...
75
@@ -XXX,XX +XXX,XX @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s)
51
@@ -XXX,XX +XXX,XX @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s)
76
s->prnsts |= SDHC_DOING_WRITE;
52
s->prnsts |= SDHC_DOING_WRITE;
77
while (s->blkcnt) {
53
while (s->blkcnt) {
78
begin = s->data_count;
54
begin = s->data_count;
79
- if (((boundary_count + begin) < block_size) && page_aligned) {
55
- if (((boundary_count + begin) < block_size) && page_aligned) {
80
+ if (((boundary_count + begin) < block_size)) {
56
+ if (((boundary_count + begin) < block_size) && !page_aligned) {
81
s->data_count = boundary_count + begin;
57
s->data_count = boundary_count + begin;
82
boundary_count = 0;
58
boundary_count = 0;
83
} else {
59
} else {
84
@@ -XXX,XX +XXX,XX @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s)
60
@@ -XXX,XX +XXX,XX @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s)
85
s->blkcnt--;
61
s->blkcnt--;
...
...
diff view generated by jsdifflib
1
According to the design of sdhci_sdma_transfer_multi_blocks, if the
1
According to the design of sdhci_sdma_transfer_multi_blocks, if the
2
"s->blkcnt * 512" was bigger than the SDMA Buffer boundary, it breaked the
2
"s->blkcnt * 512" was bigger than the SDMA Buffer boundary, it break the
3
while loop of data transfer and set SDHC_NISEN_DMA in the normal interreupt
3
while loop of data transfer and set SDHC_NISEN_DMA in the normal interrupt
4
status to notify the firmware that this SDMA boundary buffer Transfer Complete
4
status to notify the firmware that this SDMA boundary buffer Transfer Complete
5
and firmware should set the system address of the next SDMA boundary buffer
5
and firmware should set the system address of the next SDMA boundary buffer
6
for the remaining data transfer.
6
for the remained data transfer.
7
7
8
However, after firmware set the system address of the next SDMA boundary buffer
8
However, after firmware set the system address of the next SDMA boundary buffer
9
in the SDMA System Address Register(0x00), SDHCI modle did not start the data
9
in the SDMA System Address Register(0x00), SDHCI model did not restart the data
10
transfer, again. Finally, firmware breaked the data transfer because firmware
10
transfer, again. Finally, firmware break the data transfer because firmware
11
did not receive the DMA Interrupt and Tansfer Complete Interrupt from SDHCI
11
did not receive the either "DMA Interrupt" or "Transfer Complete Interrupt"
12
model.
12
from SDHCI model.
13
13
14
Error log from u-boot
14
Error log from u-boot
15
```
15
```
16
sdhci_transfer_data: Transfer data timeout
16
sdhci_transfer_data: Transfer data timeout
17
** fs_devread read error - block
17
** fs_devread read error - block
18
```
18
```
19
19
20
According to the following mention from SDMA System Address Refister of SDHCI
20
According to the following mention from SDMA System Address Register of SDHCI
21
spec,
21
spec,
22
'''
22
'''
23
This register contains the system memory address for an SDMA transfer in
23
This register contains the system memory address for an SDMA transfer in
24
32-bit addressing mode. When the Host Controller stops an SDMA transfer,
24
32-bit addressing mode. When the Host Controller stops an SDMA transfer,
25
this register shall point to the system address of the next contiguous data
25
this register shall point to the system address of the next contiguous data
...
...
37
Driver sets the next system address of the next data position to this register.
37
Driver sets the next system address of the next data position to this register.
38
When the most upper byte of this register (003h) is written, the Host Controller
38
When the most upper byte of this register (003h) is written, the Host Controller
39
restarts the SDMA transfer.
39
restarts the SDMA transfer.
40
''',
40
''',
41
41
42
restrat the data transfer if firmware set the SDMA System Address, s->blkcnt
42
restart the data transfer if firmware writes the most upper byte of SDMA System
43
is bigger than 0 and SDHCI is in the data transfer state.
43
Address, s->blkcnt is bigger than 0 and SDHCI is in the data transfer state.
44
44
45
Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
45
Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
46
---
46
---
47
hw/sd/sdhci.c | 9 +++++++++
47
hw/sd/sdhci.c | 12 ++++++++++++
48
1 file changed, 9 insertions(+)
48
1 file changed, 12 insertions(+)
49
49
50
diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c
50
diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c
51
index XXXXXXX..XXXXXXX 100644
51
index XXXXXXX..XXXXXXX 100644
52
--- a/hw/sd/sdhci.c
52
--- a/hw/sd/sdhci.c
53
+++ b/hw/sd/sdhci.c
53
+++ b/hw/sd/sdhci.c
54
@@ -XXX,XX +XXX,XX @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
54
@@ -XXX,XX +XXX,XX @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
55
sdhci_sdma_transfer_single_block(s);
55
sdhci_sdma_transfer_single_block(s);
56
}
56
}
57
}
57
}
58
+ } else if (TRANSFERRING_DATA(s->prnsts)) {
58
+ } else if (TRANSFERRING_DATA(s->prnsts)) {
59
+ /* restarts the SDMA transfer if blkcnt is not zero */
59
+ s->sdmasysad = (s->sdmasysad & mask) | value;
60
+ if (s->blkcnt && SDHC_DMA_TYPE(s->hostctl1) == SDHC_CTRL_SDMA) {
60
+ MASKED_WRITE(s->sdmasysad, mask, value);
61
+ /* restarts the SDMA transfer if the most upper byte is written */
62
+ if ((s->sdmasysad & 0xFF000000) && s->blkcnt &&
63
+ SDHC_DMA_TYPE(s->hostctl1) == SDHC_CTRL_SDMA) {
61
+ if (s->trnmod & SDHC_TRNS_MULTI) {
64
+ if (s->trnmod & SDHC_TRNS_MULTI) {
62
+ sdhci_sdma_transfer_multi_blocks(s);
65
+ sdhci_sdma_transfer_multi_blocks(s);
63
+ } else {
66
+ } else {
64
+ sdhci_sdma_transfer_single_block(s);
67
+ sdhci_sdma_transfer_single_block(s);
65
+ }
68
+ }
66
+ }
69
+ }
67
}
70
}
68
break;
71
break;
69
case SDHC_BLKSIZE:
72
case SDHC_BLKSIZE:
70
--
73
--
71
2.34.1
74
2.34.1
diff view generated by jsdifflib