@@ -2,72 +2,206 @@ package msc
22
33import (
44 "encoding/binary"
5+ "errors"
6+ "fmt"
57 "machine"
8+ "runtime/interrupt"
9+ "time"
610)
711
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 )
956
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
1263}
1364
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
1772}
1873
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 {
20102 return 4096 * int64 (d .WriteBlockSize ()) // 2MB
21103}
22104
23- func (d * DefaultDisk ) WriteBlockSize () int64 {
105+ func (d * RecorderDisk ) WriteBlockSize () int64 {
24106 return 512 // 512 bytes
25107}
26108
27- func (d * DefaultDisk ) EraseBlockSize () int64 {
109+ func (d * RecorderDisk ) EraseBlockSize () int64 {
28110 return 2048 // 4 blocks of 512 bytes
29111}
30112
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+ }
32119 return nil
33120}
34121
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+ }
40135 }
136+
41137 return len (buffer ), nil
42138}
43139
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+ }
45161 return len (buffer ), nil
46162}
47163
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 ]
71171 }
72172 }
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
73207}
0 commit comments