1 /*++
2 
3 Copyright (C) 2006 VorontSOFT
4 
5 Module Name:
6     id_badblock.cpp
7 
8 Abstract:
9     This is the artificial badblock simulation part of the
10     miniport driver for ATA/ATAPI IDE controllers with Busmaster DMA support
11 
12 Author:
13     Nikolai Vorontsov (NickViz)
14 
15 Environment:
16     kernel mode only
17 
18 Notes:
19 
20     THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21     IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22     OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23     IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25     NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26     DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27     THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28     (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29     THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 
31 Revision History:
32     2006/08/03 Initial implementation.
33     2006/08/06 Added registry work.
34     2007/03/27 Added device serial to registry value name instead of LUN.
35 
36 --*/
37 
38 #include "stdafx.h"
39 
40 //#define MAX_BADBLOCKS_ITEMS 512
41 
42 
43 
44 //SBadBlockRange    arrBadBlocks[MAX_BADBLOCKS_ITEMS];
45 //ULONG            nBadBlocks = 0;
46 
47 LIST_ENTRY BBList;
48 BOOLEAN BBListInited = FALSE;
49 
50 // RtlQueryRegistryValues callback function
51 static NTSTATUS __stdcall
52 BadBlockQueryRoutine(
53     IN PWSTR ValueName,
54     IN ULONG ValueType,
55     IN PVOID ValueData,
56     IN ULONG ValueLength,
57     IN PVOID Context,
58     IN PVOID EntryContext
59     )
60 {
61     PSBadBlockListItem cur;
62     PLIST_ENTRY      link;
63     ULONG i;
64     // The ValueType should be REG_SZ
65     // The ValueData is UNICODE string of the following format:
66     //  "badblocks_start_from_lba  badblocks_end_at_lba"
67 
68     KdPrint(( "BadBlockQueryRoutine: S/N:%S\n type %#x, len %#x\n", ValueName, ValueType, ValueLength));
69 
70     if(!BBListInited)
71         return STATUS_SUCCESS;
72 
73     if((ValueType == REG_BINARY)  &&            // STRING
74         ValueLength &&            // At least "0 0 0"
75         !(ValueLength % sizeof(SBadBlockRange)))    // There is free space for the record
76     {
77         cur = NULL;
78         link = BBList.Flink;
79         while(link != &BBList) {
80             cur = CONTAINING_RECORD( link, SBadBlockListItem, List);
81             link = link->Flink;
82             if(!wcscmp(cur->SerNumStr, ValueName)) {
83                 KdPrint(( "already loaded\n"));
84                 if(cur->LunExt) {
85                     cur->LunExt->nBadBlocks   = 0;
86                     cur->LunExt->arrBadBlocks = NULL;
87                     cur->LunExt->bbListDescr  = NULL;
88                     cur->LunExt = NULL;
89                 }
90                 break;
91             }
92         }
93 
94         if(!cur) {
95             cur = (PSBadBlockListItem)ExAllocatePool(NonPagedPool, sizeof(SBadBlockListItem));
96             if(!cur)
97                 return STATUS_SUCCESS;
98         } else {
99             if(cur->arrBadBlocks) {
100                 ExFreePool(cur->arrBadBlocks);
101                 cur->arrBadBlocks = NULL;
102             }
103         }
104         cur->arrBadBlocks = (SBadBlockRange*)ExAllocatePool(NonPagedPool, ValueLength);
105         if(!cur->arrBadBlocks) {
106             ExFreePool(cur);
107             return STATUS_SUCCESS;
108         }
109         RtlCopyMemory(cur->arrBadBlocks, ValueData, ValueLength);
110         wcsncpy(cur->SerNumStr, ValueName, 127);
111         cur->SerNumStr[127] = 0;
112         cur->nBadBlocks = ValueLength/sizeof(SBadBlockRange);
113         cur->LunExt = NULL;
114         InitializeListHead(&cur->List);
115         InsertTailList(&BBList, &(cur->List));
116         for(i=0; i<cur->nBadBlocks; i++) {
117             KdPrint(( "BB: %I64x - %I64x\n", cur->arrBadBlocks[i].m_lbaStart, cur->arrBadBlocks[i].m_lbaEnd-1));
118         }
119     }
120     return STATUS_SUCCESS;
121 } // end BadBlockQueryRoutine()
122 
123 
124 void
125 NTAPI
126 InitBadBlocks(
127     IN PHW_LU_EXTENSION LunExt
128     )
129 {
130     RTL_QUERY_REGISTRY_TABLE QueryTable[2];    // Main record and zero filled end of array marker
131     WCHAR DevSerial[128];
132 #ifdef _DEBUG
133     UCHAR cDevSerial[128];
134     ULONG i;
135 #endif
136     ULONG Length;
137     PLIST_ENTRY      link;
138     PSBadBlockListItem   cur;
139     // Read from the registry necessary badblock pairs and fill in arrBadBlocks array
140     // HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\UniATA\Parameters\BadBlocks
141 
142     if(!LunExt) {
143         // init
144         KdPrint(( "InitBadBlocks general\n"));
145         if(!BBListInited) {
146             InitializeListHead(&BBList);
147             BBListInited = TRUE;
148         }
149 
150         QueryTable[0].QueryRoutine    = BadBlockQueryRoutine;
151         QueryTable[0].Flags           = RTL_QUERY_REGISTRY_REQUIRED;
152         QueryTable[0].Name            = NULL;   // If Name is NULL, the QueryRoutine function
153                                                 //  specified for this table entry is called
154                                                 //  for all values associated with the current
155                                                 //  registry key.
156         QueryTable[0].EntryContext    = NULL;
157         QueryTable[0].DefaultType     = REG_NONE;
158         QueryTable[0].DefaultData     = 0;
159         QueryTable[0].DefaultLength   = 0;
160 
161         RtlZeroMemory(QueryTable + 1, sizeof(RTL_QUERY_REGISTRY_TABLE));    // EOF
162 
163         NTSTATUS status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES,
164                                                 L"UniATA\\Parameters\\BadBlocks",
165                                                 QueryTable, 0, 0);
166 
167 #ifdef _DEBUG
168         KdPrint(( "InitBadBlocks returned: %#x\n", status));
169 #else
170         UNREFERENCED_PARAMETER(status);
171 #endif
172     } else {
173 
174         KdPrint(( "InitBadBlocks local\n"));
175         Length = EncodeVendorStr(DevSerial, (PUCHAR)LunExt->IdentifyData.ModelNumber, sizeof(LunExt->IdentifyData.ModelNumber));
176         DevSerial[Length] = '-';
177         Length++;
178         Length += EncodeVendorStr(DevSerial+Length, LunExt->IdentifyData.SerialNumber, sizeof(LunExt->IdentifyData.SerialNumber));
179 
180 #ifdef _DEBUG
181         KdPrint(( "LunExt %#x\n", LunExt));
182         for(i=0; i<Length; i++) {
183             cDevSerial[i] = (UCHAR)(DevSerial[i]);
184         }
185         cDevSerial[i] = 0;
186         KdPrint(( "S/N:%s\n", cDevSerial));
187 #endif
188 
189         LunExt->nBadBlocks = 0;
190         LunExt->arrBadBlocks = NULL;
191 
192         link = BBList.Flink;
193         while(link != &BBList) {
194             cur = CONTAINING_RECORD( link, SBadBlockListItem, List);
195             link = link->Flink;
196             if(cur->LunExt == LunExt) {
197                 KdPrint(( "  deassociate BB list (by LunExt)\n"));
198                 cur->LunExt->nBadBlocks   = 0;
199                 cur->LunExt->arrBadBlocks = NULL;
200                 cur->LunExt->bbListDescr  = NULL;
201                 cur->LunExt = NULL;
202             } else
203             if(!wcscmp(cur->SerNumStr, DevSerial)) {
204                 KdPrint(( "  deassociate BB list (by Serial)\n"));
205                 if(cur->LunExt) {
206                     cur->LunExt->nBadBlocks   = 0;
207                     cur->LunExt->arrBadBlocks = NULL;
208                     cur->LunExt->bbListDescr  = NULL;
209                     cur->LunExt = NULL;
210                 }
211             }
212         }
213 
214         if(!(LunExt->DeviceFlags & DFLAGS_ATAPI_DEVICE)) {
215 
216             link = BBList.Flink;
217             while(link != &BBList) {
218                 cur = CONTAINING_RECORD( link, SBadBlockListItem, List);
219                 link = link->Flink;
220                 if(!wcscmp(cur->SerNumStr, DevSerial)) {
221                     KdPrint(( "found BB:List\n"));
222                     cur->LunExt = LunExt;
223                     LunExt->arrBadBlocks = cur->arrBadBlocks;
224                     LunExt->nBadBlocks   = cur->nBadBlocks;
225                     LunExt->bbListDescr  = cur;
226                     return;
227                 }
228             }
229         }
230     }
231     return;
232 } // end InitBadBlocks()
233 
234 
235 void
236 NTAPI
237 ForgetBadBlocks(
238     IN PHW_LU_EXTENSION LunExt
239     )
240 {
241     if(LunExt->bbListDescr) {
242         LunExt->bbListDescr->LunExt = NULL;
243         LunExt->nBadBlocks   = 0;
244         LunExt->arrBadBlocks = NULL;
245         LunExt->bbListDescr  = NULL;
246     }
247 } // end ForgetBadBlocks()
248 
249 bool
250 NTAPI
251 CheckIfBadBlock(
252     IN PHW_LU_EXTENSION LunExt,
253 //    IN UCHAR command,
254     IN ULONGLONG lba,
255     IN ULONG count
256     )
257 {
258     if (LunExt->nBadBlocks == 0)
259         return false;
260 /*
261     // this is checked by caller
262     if(!(AtaCommandFlags[command] & ATA_CMD_FLAG_LBAsupp)) {
263         return false;
264 */
265     ULONG nBadBlocks = LunExt->nBadBlocks;
266     SBadBlockRange*  arrBadBlocks = LunExt->arrBadBlocks;
267 
268     // back transform for possibly CHS'ed LBA
269     lba = UniAtaCalculateLBARegsBack(LunExt, lba);
270 
271     for (ULONG i = 0; i < nBadBlocks; i++)
272     {
273         if (lba + count > arrBadBlocks->m_lbaStart  &&
274                     lba < arrBadBlocks->m_lbaEnd) {
275             KdPrint(( "listed BB @ %I64x\n", lba));
276             return true;
277         }
278         arrBadBlocks++;
279     }
280 
281     return false;
282 
283 } // end CheckIfBadBlock()
284