@@ -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