@@ -2,72 +2,206 @@ package msc
2
2
3
3
import (
4
4
"encoding/binary"
5
+ "errors"
6
+ "fmt"
5
7
"machine"
8
+ "runtime/interrupt"
9
+ "time"
6
10
)
7
11
8
- var _ machine.BlockDevice = (* DefaultDisk )(nil )
12
+ var (
13
+ errWriteOutOfBounds = errors .New ("WriteAt offset out of bounds" )
14
+ )
15
+
16
+ // RegisterBlockDevice registers a BlockDevice provider with the MSC driver
17
+ func (m * msc ) RegisterBlockDevice (dev machine.BlockDevice ) {
18
+ m .dev = dev
19
+
20
+ m .blockRatio = 1
21
+ m .blockSize = uint32 (m .dev .WriteBlockSize ())
22
+ if m .blockSize < 512 {
23
+ // If the block size is less than 512 bytes, we'll scale it up to 512 for compatibility
24
+ m .blockRatio = 512 / m .blockSize
25
+ m .blockSize = 512
26
+ }
27
+ m .blockCount = uint32 (m .dev .Size ()) / m .blockSize
28
+ // FIXME: Figure out what to do if the emulated write block size is larger than the erase block size
29
+
30
+ // Set VPD UNMAP fields
31
+ for i := range vpdPages {
32
+ if vpdPages [i ].PageCode == 0xb0 {
33
+ // 0xb0 - 5.4.5 Block Limits VPD page (B0h)
34
+ if len (vpdPages [i ].Data ) >= 28 {
35
+ // Set the OPTIMAL UNMAP GRANULARITY (write blocks per erase block)
36
+ granularity := uint32 (dev .EraseBlockSize ()) / m .blockSize
37
+ binary .BigEndian .PutUint32 (vpdPages [i ].Data [24 :28 ], granularity )
38
+ }
39
+ if len (vpdPages [i ].Data ) >= 32 {
40
+ // Set the UNMAP GRANULARITY ALIGNMENT (first sector of first full erase block)
41
+ // The unmap granularity alignment is used to calculate an optimal unmap request starting LBA as follows:
42
+ // optimal unmap request starting LBA = (n * OPTIMAL UNMAP GRANULARITY) + UNMAP GRANULARITY ALIGNMENT
43
+ // where n is zero or any positive integer value
44
+ // https://www.seagate.com/files/staticfiles/support/docs/manual/Interface%20manuals/100293068j.pdf
45
+
46
+ // We assume the block device is aligned to the end of the underlying block device
47
+ blockOffset := uint32 (dev .EraseBlockSize ()) % m .blockSize
48
+ binary .BigEndian .PutUint32 (vpdPages [i ].Data [28 :32 ], blockOffset )
49
+ }
50
+ break
51
+ }
52
+ }
53
+ }
54
+
55
+ var _ machine.BlockDevice = (* RecorderDisk )(nil )
9
56
10
- // DefaultDisk is a placeholder disk implementation
11
- type DefaultDisk struct {
57
+ // RecorderDisk is a block device that records actions taken on it
58
+ type RecorderDisk struct {
59
+ data map [int64 ][]byte
60
+ log []RecorderRecord
61
+ last time.Time
62
+ time time.Time
12
63
}
13
64
14
- // NewDefaultDisk creates a new DefaultDisk instance
15
- func NewDefaultDisk () * DefaultDisk {
16
- return & DefaultDisk {}
65
+ type RecorderRecord struct {
66
+ OpCode RecorderOpCode
67
+ Offset int64
68
+ Length int
69
+ Data []byte
70
+ Time int64
71
+ valid bool
17
72
}
18
73
19
- func (d * DefaultDisk ) Size () int64 {
74
+ type RecorderOpCode uint8
75
+
76
+ const (
77
+ RecorderOpCodeRead RecorderOpCode = iota
78
+ RecorderOpCodeWrite
79
+ RecorderOpCodeEraseBlocks
80
+ )
81
+
82
+ // NewRecorderDisk creates a new RecorderDisk instance
83
+ func NewRecorderDisk (count int ) * RecorderDisk {
84
+ d := & RecorderDisk {
85
+ data : make (map [int64 ][]byte ),
86
+ log : make ([]RecorderRecord , 0 , count ),
87
+ last : time .Now (),
88
+ }
89
+ for i := 0 ; i < count ; i ++ {
90
+ d .log = append (d .log , RecorderRecord {
91
+ OpCode : RecorderOpCodeRead ,
92
+ Offset : 0 ,
93
+ Length : 0 ,
94
+ Data : make ([]byte , 0 , 64 ),
95
+ Time : 0 ,
96
+ })
97
+ }
98
+ return d
99
+ }
100
+
101
+ func (d * RecorderDisk ) Size () int64 {
20
102
return 4096 * int64 (d .WriteBlockSize ()) // 2MB
21
103
}
22
104
23
- func (d * DefaultDisk ) WriteBlockSize () int64 {
105
+ func (d * RecorderDisk ) WriteBlockSize () int64 {
24
106
return 512 // 512 bytes
25
107
}
26
108
27
- func (d * DefaultDisk ) EraseBlockSize () int64 {
109
+ func (d * RecorderDisk ) EraseBlockSize () int64 {
28
110
return 2048 // 4 blocks of 512 bytes
29
111
}
30
112
31
- func (d * DefaultDisk ) EraseBlocks (startBlock , numBlocks int64 ) error {
113
+ func (d * RecorderDisk ) EraseBlocks (startBlock , numBlocks int64 ) error {
114
+ d .Record (RecorderOpCodeEraseBlocks , startBlock , int (numBlocks ), []byte {})
115
+ if interrupt .In () {
116
+ // Flash erase commands are not allowed in interrupt context
117
+ panic ("EraseBlocks attempted in interrupt context" )
118
+ }
32
119
return nil
33
120
}
34
121
35
- func (d * DefaultDisk ) ReadAt (buffer []byte , offset int64 ) (int , error ) {
36
- n := uint8 (offset )
37
- for i := range buffer {
38
- n ++
39
- buffer [i ] = n
122
+ func (d * RecorderDisk ) ReadAt (buffer []byte , offset int64 ) (int , error ) {
123
+ d .Record (RecorderOpCodeRead , offset , len (buffer ), []byte {})
124
+ sector := offset / d .WriteBlockSize ()
125
+ if sector < 0 || offset + int64 (len (buffer )) > d .Size () {
126
+ return 0 , fmt .Errorf ("read out of bounds: %d" , offset )
127
+ }
128
+
129
+ sectorCount := int64 (len (buffer )) / d .WriteBlockSize ()
130
+ for i := int64 (0 ); i < sectorCount ; i ++ {
131
+ if _ , ok := d .data [sector + i ]; ok {
132
+ n := int (offset % d .WriteBlockSize ())
133
+ copy (buffer , d .data [sector + i ][n :])
134
+ }
40
135
}
136
+
41
137
return len (buffer ), nil
42
138
}
43
139
44
- func (d * DefaultDisk ) WriteAt (buffer []byte , offset int64 ) (int , error ) {
140
+ func (d * RecorderDisk ) WriteAt (buffer []byte , offset int64 ) (int , error ) {
141
+ if interrupt .In () {
142
+ // Flash writes aren't possible in interrupt context
143
+ panic ("WriteAt attempted in interrupt context" )
144
+ }
145
+ if offset < 0 || offset + int64 (len (buffer )) > d .Size () {
146
+ return 0 , errWriteOutOfBounds
147
+ }
148
+
149
+ d .Record (RecorderOpCodeWrite , offset , len (buffer ), buffer )
150
+
151
+ sector := offset / d .WriteBlockSize ()
152
+ sectorCount := int64 (len (buffer )) / d .WriteBlockSize ()
153
+ for i := int64 (0 ); i < sectorCount ; i ++ {
154
+ _ , ok := d .data [sector + i ]
155
+ if ! ok {
156
+ d .data [sector + i ] = make ([]byte , d .WriteBlockSize ())
157
+ }
158
+ n := int (offset % d .WriteBlockSize ())
159
+ copy (d .data [sector + i ][n :], buffer )
160
+ }
45
161
return len (buffer ), nil
46
162
}
47
163
48
- // RegisterBlockDevice registers a BlockDevice provider with the MSC driver
49
- func (m * msc ) RegisterBlockDevice (dev machine.BlockDevice ) {
50
- m .dev = dev
51
-
52
- // Set VPD UNMAP fields
53
- for i := range vpdPages {
54
- if vpdPages [i ].PageCode == 0xb0 {
55
- // 0xb0 - 5.4.5 Block Limits VPD page (B0h)
56
- if len (vpdPages [i ].Data ) >= 28 {
57
- // Set the OPTIMAL UNMAP GRANULARITY (write blocks per erase block)
58
- granularity := uint32 (dev .EraseBlockSize ()) / uint32 (dev .WriteBlockSize ())
59
- binary .BigEndian .PutUint32 (vpdPages [i ].Data [24 :28 ], granularity )
60
- }
61
- /* TODO: Add method for working out the optimal unmap granularity alignment
62
- if len(vpdPages[i].Data) >= 32 {
63
- // Set the UNMAP GRANULARITY ALIGNMENT (first sector of first full erase block)
64
- // The unmap granularity alignment is used to calculate an optimal unmap request starting LBA as follows:
65
- // optimal unmap request starting LBA = (n * OPTIMAL UNMAP GRANULARITY) + UNMAP GRANULARITY ALIGNMENT
66
- // where n is zero or any positive integer value
67
- // https://www.seagate.com/files/staticfiles/support/docs/manual/Interface%20manuals/100293068j.pdf
68
- }
69
- */
70
- break
164
+ func (d * RecorderDisk ) Record (opCode RecorderOpCode , offset int64 , length int , data []byte ) {
165
+ n := len (d .log )
166
+ if n == 0 {
167
+ return
168
+ } else if n == cap (d .log ) {
169
+ for i := 0 ; i < n - 1 ; i ++ {
170
+ d .log [i ] = d .log [i + 1 ]
71
171
}
72
172
}
173
+
174
+ // Append the new record
175
+ d .log [n - 1 ].OpCode = opCode
176
+ d .log [n - 1 ].Offset = offset
177
+ d .log [n - 1 ].Length = length
178
+ d .log [n - 1 ].Data = d .log [n - 1 ].Data [:len (data )]
179
+ copy (d .log [n - 1 ].Data , data )
180
+ d .log [n - 1 ].Time = time .Since (d .time ).Microseconds ()
181
+ d .time = d .time .Add (time .Since (d .time ))
182
+ d .log [n - 1 ].valid = true
183
+ }
184
+
185
+ func (d * RecorderDisk ) ClearLog () {
186
+ for i := range d .log {
187
+ d .log [i ].valid = false
188
+ }
189
+ d .time = time .Now ()
190
+ }
191
+
192
+ func (d * RecorderDisk ) GetLog () []RecorderRecord {
193
+ return d .log
194
+ }
195
+
196
+ func (r RecorderRecord ) String () (string , bool ) {
197
+ opCode := "Unknown"
198
+ switch r .OpCode {
199
+ case RecorderOpCodeRead :
200
+ opCode = "Read"
201
+ case RecorderOpCodeWrite :
202
+ opCode = "Write"
203
+ case RecorderOpCodeEraseBlocks :
204
+ opCode = "EraseBlocks"
205
+ }
206
+ return fmt .Sprintf ("%s: %05d+%02d t:%d| % 0x" , opCode , r .Offset , r .Length , r .Time , r .Data ), r .valid
73
207
}
0 commit comments