609f56cef69315a4edd016f7c954d510f8695860
[mirror_edk2.git] / MdeModulePkg / Universal / Disk / PartitionDxe / Udf.c
1 /** @file
2 Scan for an UDF file system on a formatted media.
3
4 Copyright (C) 2014-2017 Paulo Alcantara <pcacjr@zytor.com>
5
6 This program and the accompanying materials are licensed and made available
7 under the terms and conditions of the BSD License which accompanies this
8 distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
10
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
12 WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13 **/
14
15 #include "Partition.h"
16
17 //
18 // C5BD4D42-1A76-4996-8956-73CDA326CD0A
19 //
20 #define EFI_UDF_DEVICE_PATH_GUID \
21 { 0xC5BD4D42, 0x1A76, 0x4996, \
22 { 0x89, 0x56, 0x73, 0xCD, 0xA3, 0x26, 0xCD, 0x0A } \
23 }
24
25 typedef struct {
26 VENDOR_DEVICE_PATH DevicePath;
27 EFI_DEVICE_PATH_PROTOCOL End;
28 } UDF_DEVICE_PATH;
29
30 //
31 // Vendor-Defined Device Path GUID for UDF file system
32 //
33 EFI_GUID gUdfDevPathGuid = EFI_UDF_DEVICE_PATH_GUID;
34
35 //
36 // Vendor-Defined Media Device Path for UDF file system
37 //
38 UDF_DEVICE_PATH gUdfDevicePath = {
39 { { MEDIA_DEVICE_PATH, MEDIA_VENDOR_DP,
40 { sizeof (VENDOR_DEVICE_PATH), 0 } },
41 EFI_UDF_DEVICE_PATH_GUID
42 },
43 { END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE,
44 { sizeof (EFI_DEVICE_PATH_PROTOCOL), 0 }
45 }
46 };
47
48 /**
49 Find the anchor volume descriptor pointer.
50
51 @param[in] BlockIo BlockIo interface.
52 @param[in] DiskIo DiskIo interface.
53 @param[out] AnchorPoint Anchor volume descriptor pointer.
54
55 @retval EFI_SUCCESS Anchor volume descriptor pointer found.
56 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
57 @retval other Anchor volume descriptor pointer not found.
58
59 **/
60 EFI_STATUS
61 FindAnchorVolumeDescriptorPointer (
62 IN EFI_BLOCK_IO_PROTOCOL *BlockIo,
63 IN EFI_DISK_IO_PROTOCOL *DiskIo,
64 OUT UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER *AnchorPoint
65 )
66 {
67 EFI_STATUS Status;
68 UINT32 BlockSize;
69 EFI_LBA EndLBA;
70 EFI_LBA DescriptorLBAs[4];
71 UINTN Index;
72
73 BlockSize = BlockIo->Media->BlockSize;
74 EndLBA = BlockIo->Media->LastBlock;
75 DescriptorLBAs[0] = 256;
76 DescriptorLBAs[1] = EndLBA - 256;
77 DescriptorLBAs[2] = EndLBA;
78 DescriptorLBAs[3] = 512;
79
80 for (Index = 0; Index < ARRAY_SIZE (DescriptorLBAs); Index++) {
81 Status = DiskIo->ReadDisk (
82 DiskIo,
83 BlockIo->Media->MediaId,
84 MultU64x32 (DescriptorLBAs[Index], BlockSize),
85 sizeof (UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER),
86 (VOID *)AnchorPoint
87 );
88 if (EFI_ERROR (Status)) {
89 return Status;
90 }
91 //
92 // Check if read LBA has a valid AVDP descriptor.
93 //
94 if (IS_AVDP (AnchorPoint)) {
95 return EFI_SUCCESS;
96 }
97 }
98 //
99 // No AVDP found.
100 //
101 return EFI_VOLUME_CORRUPTED;
102 }
103
104 /**
105 Check if block device supports a valid UDF file system as specified by OSTA
106 Universal Disk Format Specification 2.60.
107
108 @param[in] BlockIo BlockIo interface.
109 @param[in] DiskIo DiskIo interface.
110
111 @retval EFI_SUCCESS UDF file system found.
112 @retval EFI_UNSUPPORTED UDF file system not found.
113 @retval EFI_NO_MEDIA The device has no media.
114 @retval EFI_DEVICE_ERROR The device reported an error.
115 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
116 @retval EFI_OUT_OF_RESOURCES The scan was not successful due to lack of
117 resources.
118
119 **/
120 EFI_STATUS
121 SupportUdfFileSystem (
122 IN EFI_BLOCK_IO_PROTOCOL *BlockIo,
123 IN EFI_DISK_IO_PROTOCOL *DiskIo
124 )
125 {
126 EFI_STATUS Status;
127 UINT64 Offset;
128 UINT64 EndDiskOffset;
129 CDROM_VOLUME_DESCRIPTOR VolDescriptor;
130 CDROM_VOLUME_DESCRIPTOR TerminatingVolDescriptor;
131 UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER AnchorPoint;
132
133 ZeroMem ((VOID *)&TerminatingVolDescriptor, sizeof (CDROM_VOLUME_DESCRIPTOR));
134
135 //
136 // Start Volume Recognition Sequence
137 //
138 EndDiskOffset = MultU64x32 (BlockIo->Media->LastBlock,
139 BlockIo->Media->BlockSize);
140
141 for (Offset = UDF_VRS_START_OFFSET; Offset < EndDiskOffset;
142 Offset += UDF_LOGICAL_SECTOR_SIZE) {
143 //
144 // Check if block device has a Volume Structure Descriptor and an Extended
145 // Area.
146 //
147 Status = DiskIo->ReadDisk (
148 DiskIo,
149 BlockIo->Media->MediaId,
150 Offset,
151 sizeof (CDROM_VOLUME_DESCRIPTOR),
152 (VOID *)&VolDescriptor
153 );
154 if (EFI_ERROR (Status)) {
155 return Status;
156 }
157
158 if (CompareMem ((VOID *)VolDescriptor.Unknown.Id,
159 (VOID *)UDF_BEA_IDENTIFIER,
160 sizeof (VolDescriptor.Unknown.Id)) == 0) {
161 break;
162 }
163
164 if ((CompareMem ((VOID *)VolDescriptor.Unknown.Id,
165 (VOID *)CDVOL_ID,
166 sizeof (VolDescriptor.Unknown.Id)) != 0) ||
167 (CompareMem ((VOID *)&VolDescriptor,
168 (VOID *)&TerminatingVolDescriptor,
169 sizeof (CDROM_VOLUME_DESCRIPTOR)) == 0)) {
170 return EFI_UNSUPPORTED;
171 }
172 }
173
174 //
175 // Look for "NSR0{2,3}" identifiers in the Extended Area.
176 //
177 Offset += UDF_LOGICAL_SECTOR_SIZE;
178 if (Offset >= EndDiskOffset) {
179 return EFI_UNSUPPORTED;
180 }
181
182 Status = DiskIo->ReadDisk (
183 DiskIo,
184 BlockIo->Media->MediaId,
185 Offset,
186 sizeof (CDROM_VOLUME_DESCRIPTOR),
187 (VOID *)&VolDescriptor
188 );
189 if (EFI_ERROR (Status)) {
190 return Status;
191 }
192
193 if ((CompareMem ((VOID *)VolDescriptor.Unknown.Id,
194 (VOID *)UDF_NSR2_IDENTIFIER,
195 sizeof (VolDescriptor.Unknown.Id)) != 0) &&
196 (CompareMem ((VOID *)VolDescriptor.Unknown.Id,
197 (VOID *)UDF_NSR3_IDENTIFIER,
198 sizeof (VolDescriptor.Unknown.Id)) != 0)) {
199 return EFI_UNSUPPORTED;
200 }
201
202 //
203 // Look for "TEA01" identifier in the Extended Area
204 //
205 Offset += UDF_LOGICAL_SECTOR_SIZE;
206 if (Offset >= EndDiskOffset) {
207 return EFI_UNSUPPORTED;
208 }
209
210 Status = DiskIo->ReadDisk (
211 DiskIo,
212 BlockIo->Media->MediaId,
213 Offset,
214 sizeof (CDROM_VOLUME_DESCRIPTOR),
215 (VOID *)&VolDescriptor
216 );
217 if (EFI_ERROR (Status)) {
218 return Status;
219 }
220
221 if (CompareMem ((VOID *)VolDescriptor.Unknown.Id,
222 (VOID *)UDF_TEA_IDENTIFIER,
223 sizeof (VolDescriptor.Unknown.Id)) != 0) {
224 return EFI_UNSUPPORTED;
225 }
226
227 Status = FindAnchorVolumeDescriptorPointer (BlockIo, DiskIo, &AnchorPoint);
228 if (EFI_ERROR (Status)) {
229 return EFI_UNSUPPORTED;
230 }
231
232 return EFI_SUCCESS;
233 }
234
235 /**
236 Install child handles if the Handle supports UDF/ECMA-167 volume format.
237
238 @param[in] This Calling context.
239 @param[in] Handle Parent Handle.
240 @param[in] DiskIo Parent DiskIo interface.
241 @param[in] DiskIo2 Parent DiskIo2 interface.
242 @param[in] BlockIo Parent BlockIo interface.
243 @param[in] BlockIo2 Parent BlockIo2 interface.
244 @param[in] DevicePath Parent Device Path
245
246
247 @retval EFI_SUCCESS Child handle(s) was added.
248 @retval EFI_MEDIA_CHANGED Media changed Detected.
249 @retval other no child handle was added.
250
251 **/
252 EFI_STATUS
253 PartitionInstallUdfChildHandles (
254 IN EFI_DRIVER_BINDING_PROTOCOL *This,
255 IN EFI_HANDLE Handle,
256 IN EFI_DISK_IO_PROTOCOL *DiskIo,
257 IN EFI_DISK_IO2_PROTOCOL *DiskIo2,
258 IN EFI_BLOCK_IO_PROTOCOL *BlockIo,
259 IN EFI_BLOCK_IO2_PROTOCOL *BlockIo2,
260 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
261 )
262 {
263 UINT32 RemainderByMediaBlockSize;
264 EFI_STATUS Status;
265 EFI_BLOCK_IO_MEDIA *Media;
266 EFI_DEVICE_PATH_PROTOCOL *DevicePathNode;
267 EFI_GUID *VendorDefinedGuid;
268 EFI_PARTITION_INFO_PROTOCOL PartitionInfo;
269
270 Media = BlockIo->Media;
271
272 //
273 // Check if UDF logical block size is multiple of underlying device block size
274 //
275 DivU64x32Remainder (
276 UDF_LOGICAL_SECTOR_SIZE, // Dividend
277 Media->BlockSize, // Divisor
278 &RemainderByMediaBlockSize // Remainder
279 );
280 if (RemainderByMediaBlockSize != 0) {
281 return EFI_NOT_FOUND;
282 }
283
284 DevicePathNode = DevicePath;
285 while (!IsDevicePathEnd (DevicePathNode)) {
286 //
287 // Do not allow checking for UDF file systems in CDROM "El Torito"
288 // partitions, and skip duplicate installation of UDF file system child
289 // nodes.
290 //
291 if (DevicePathType (DevicePathNode) == MEDIA_DEVICE_PATH) {
292 if (DevicePathSubType (DevicePathNode) == MEDIA_CDROM_DP) {
293 return EFI_NOT_FOUND;
294 }
295 if (DevicePathSubType (DevicePathNode) == MEDIA_VENDOR_DP) {
296 VendorDefinedGuid = (EFI_GUID *)((UINTN)DevicePathNode +
297 OFFSET_OF (VENDOR_DEVICE_PATH, Guid));
298 if (CompareGuid (VendorDefinedGuid, &gUdfDevPathGuid)) {
299 return EFI_NOT_FOUND;
300 }
301 }
302 }
303 //
304 // Try next device path node
305 //
306 DevicePathNode = NextDevicePathNode (DevicePathNode);
307 }
308
309 //
310 // Check if block device supports an UDF file system
311 //
312 Status = SupportUdfFileSystem (BlockIo, DiskIo);
313 if (EFI_ERROR (Status)) {
314 return EFI_NOT_FOUND;
315 }
316
317 //
318 // Create Partition Info protocol for UDF file system
319 //
320 ZeroMem (&PartitionInfo, sizeof (EFI_PARTITION_INFO_PROTOCOL));
321 PartitionInfo.Revision = EFI_PARTITION_INFO_PROTOCOL_REVISION;
322 PartitionInfo.Type = PARTITION_TYPE_OTHER;
323
324 //
325 // Install partition child handle for UDF file system
326 //
327 Status = PartitionInstallChildHandle (
328 This,
329 Handle,
330 DiskIo,
331 DiskIo2,
332 BlockIo,
333 BlockIo2,
334 DevicePath,
335 (EFI_DEVICE_PATH_PROTOCOL *)&gUdfDevicePath,
336 &PartitionInfo,
337 0,
338 Media->LastBlock,
339 Media->BlockSize
340 );
341 if (!EFI_ERROR (Status)) {
342 Status = EFI_NOT_FOUND;
343 }
344
345 return Status;
346 }