]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - MdeModulePkg/Core/Dxe/FwVolBlock/FwVolBlock.c
Change field Handle to type IHANDLE to remove the type casting.
[mirror_edk2.git] / MdeModulePkg / Core / Dxe / FwVolBlock / FwVolBlock.c
... / ...
CommitLineData
1/** @file\r
2 Implementations for Firmware Volume Block protocol.\r
3\r
4 It consumes FV HOBs and creates read-lonly Firmare Volume Block protocol\r
5 instances for each of them.\r
6\r
7Copyright (c) 2006 - 2008, Intel Corporation. <BR>\r
8All rights reserved. This program and the accompanying materials\r
9are licensed and made available under the terms and conditions of the BSD License\r
10which accompanies this distribution. The full text of the license may be found at\r
11http://opensource.org/licenses/bsd-license.php\r
12\r
13THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
14WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
15\r
16**/\r
17\r
18#include "DxeMain.h"\r
19\r
20\r
21EFI_FW_VOL_BLOCK_DEVICE mFwVolBlock = {\r
22 FVB_DEVICE_SIGNATURE,\r
23 NULL,\r
24 {\r
25 {\r
26 {\r
27 HARDWARE_DEVICE_PATH,\r
28 HW_MEMMAP_DP,\r
29 {\r
30 (UINT8)(sizeof (MEMMAP_DEVICE_PATH)),\r
31 (UINT8)(sizeof (MEMMAP_DEVICE_PATH) >> 8)\r
32 }\r
33 },\r
34 EfiMemoryMappedIO,\r
35 (EFI_PHYSICAL_ADDRESS) 0,\r
36 (EFI_PHYSICAL_ADDRESS) 0,\r
37 },\r
38 {\r
39 END_DEVICE_PATH_TYPE,\r
40 END_ENTIRE_DEVICE_PATH_SUBTYPE,\r
41 {\r
42 END_DEVICE_PATH_LENGTH,\r
43 0\r
44 }\r
45 },\r
46 },\r
47 {\r
48 FwVolBlockGetAttributes,\r
49 (EFI_FVB_SET_ATTRIBUTES)FwVolBlockSetAttributes,\r
50 FwVolBlockGetPhysicalAddress,\r
51 FwVolBlockGetBlockSize,\r
52 FwVolBlockReadBlock,\r
53 (EFI_FVB_WRITE)FwVolBlockWriteBlock,\r
54 (EFI_FVB_ERASE_BLOCKS)FwVolBlockEraseBlock,\r
55 NULL\r
56 },\r
57 0,\r
58 NULL,\r
59 0,\r
60 0\r
61};\r
62\r
63\r
64\r
65/**\r
66 Retrieves Volume attributes. No polarity translations are done.\r
67\r
68 @param This Calling context\r
69 @param Attributes output buffer which contains attributes\r
70\r
71 @retval EFI_SUCCESS The firmware volume attributes were returned.\r
72\r
73**/\r
74EFI_STATUS\r
75EFIAPI\r
76FwVolBlockGetAttributes (\r
77 IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,\r
78 OUT EFI_FVB_ATTRIBUTES_2 *Attributes\r
79 )\r
80{\r
81 EFI_FW_VOL_BLOCK_DEVICE *FvbDevice;\r
82\r
83 FvbDevice = FVB_DEVICE_FROM_THIS (This);\r
84\r
85 //\r
86 // Since we are read only, it's safe to get attributes data from our in-memory copy.\r
87 //\r
88 *Attributes = FvbDevice->FvbAttributes;\r
89\r
90 return EFI_SUCCESS;\r
91}\r
92\r
93\r
94\r
95/**\r
96 Modifies the current settings of the firmware volume according to the input parameter.\r
97\r
98 @param This Calling context\r
99 @param Attributes input buffer which contains attributes\r
100\r
101 @retval EFI_SUCCESS The firmware volume attributes were returned.\r
102 @retval EFI_INVALID_PARAMETER The attributes requested are in conflict with\r
103 the capabilities as declared in the firmware\r
104 volume header.\r
105 @retval EFI_UNSUPPORTED Not supported.\r
106\r
107**/\r
108EFI_STATUS\r
109EFIAPI\r
110FwVolBlockSetAttributes (\r
111 IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,\r
112 IN CONST EFI_FVB_ATTRIBUTES_2 *Attributes\r
113 )\r
114{\r
115 return EFI_UNSUPPORTED;\r
116}\r
117\r
118\r
119\r
120/**\r
121 The EraseBlock() function erases one or more blocks as denoted by the\r
122 variable argument list. The entire parameter list of blocks must be verified\r
123 prior to erasing any blocks. If a block is requested that does not exist\r
124 within the associated firmware volume (it has a larger index than the last\r
125 block of the firmware volume), the EraseBlock() function must return\r
126 EFI_INVALID_PARAMETER without modifying the contents of the firmware volume.\r
127\r
128 @param This Calling context\r
129 @param ... Starting LBA followed by Number of Lba to erase.\r
130 a -1 to terminate the list.\r
131\r
132 @retval EFI_SUCCESS The erase request was successfully completed.\r
133 @retval EFI_ACCESS_DENIED The firmware volume is in the WriteDisabled\r
134 state.\r
135 @retval EFI_DEVICE_ERROR The block device is not functioning correctly\r
136 and could not be written. The firmware device\r
137 may have been partially erased.\r
138 @retval EFI_INVALID_PARAMETER One or more of the LBAs listed in the variable\r
139 argument list do\r
140 @retval EFI_UNSUPPORTED Not supported.\r
141\r
142**/\r
143EFI_STATUS\r
144EFIAPI\r
145FwVolBlockEraseBlock (\r
146 IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,\r
147 ...\r
148 )\r
149{\r
150 return EFI_UNSUPPORTED;\r
151}\r
152\r
153\r
154\r
155/**\r
156 Read the specified number of bytes from the block to the input buffer.\r
157\r
158 @param This Indicates the calling context.\r
159 @param Lba The starting logical block index to read.\r
160 @param Offset Offset into the block at which to begin reading.\r
161 @param NumBytes Pointer to a UINT32. At entry, *NumBytes\r
162 contains the total size of the buffer. At exit,\r
163 *NumBytes contains the total number of bytes\r
164 actually read.\r
165 @param Buffer Pinter to a caller-allocated buffer that\r
166 contains the destine for the read.\r
167\r
168 @retval EFI_SUCCESS The firmware volume was read successfully.\r
169 @retval EFI_BAD_BUFFER_SIZE The read was attempted across an LBA boundary.\r
170 @retval EFI_ACCESS_DENIED Access denied.\r
171 @retval EFI_DEVICE_ERROR The block device is malfunctioning and could not\r
172 be read.\r
173\r
174**/\r
175EFI_STATUS\r
176EFIAPI\r
177FwVolBlockReadBlock (\r
178 IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,\r
179 IN CONST EFI_LBA Lba,\r
180 IN CONST UINTN Offset,\r
181 IN OUT UINTN *NumBytes,\r
182 IN OUT UINT8 *Buffer\r
183 )\r
184{\r
185 EFI_FW_VOL_BLOCK_DEVICE *FvbDevice;\r
186 EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader;\r
187 UINT8 *LbaOffset;\r
188 UINTN LbaStart;\r
189 UINTN NumOfBytesRead;\r
190 UINTN LbaIndex;\r
191\r
192 FvbDevice = FVB_DEVICE_FROM_THIS (This);\r
193\r
194 //\r
195 // Check if This FW can be read\r
196 //\r
197 if ((FvbDevice->FvbAttributes & EFI_FVB2_READ_STATUS) == 0) {\r
198 return EFI_ACCESS_DENIED;\r
199 }\r
200\r
201 LbaIndex = (UINTN) Lba;\r
202 if (LbaIndex >= FvbDevice->NumBlocks) {\r
203 //\r
204 // Invalid Lba, read nothing.\r
205 //\r
206 *NumBytes = 0;\r
207 return EFI_BAD_BUFFER_SIZE;\r
208 }\r
209\r
210 if (Offset > FvbDevice->LbaCache[LbaIndex].Length) {\r
211 //\r
212 // all exceed boundry, read nothing.\r
213 //\r
214 *NumBytes = 0;\r
215 return EFI_BAD_BUFFER_SIZE;\r
216 }\r
217\r
218 NumOfBytesRead = *NumBytes;\r
219 if (Offset + NumOfBytesRead > FvbDevice->LbaCache[LbaIndex].Length) {\r
220 //\r
221 // partial exceed boundry, read data from current postion to end.\r
222 //\r
223 NumOfBytesRead = FvbDevice->LbaCache[LbaIndex].Length - Offset;\r
224 }\r
225\r
226 LbaStart = FvbDevice->LbaCache[LbaIndex].Base;\r
227 FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *)((UINTN) FvbDevice->BaseAddress);\r
228 LbaOffset = (UINT8 *) FwVolHeader + LbaStart + Offset;\r
229\r
230 //\r
231 // Perform read operation\r
232 //\r
233 CopyMem (Buffer, LbaOffset, NumOfBytesRead);\r
234\r
235 if (NumOfBytesRead == *NumBytes) {\r
236 return EFI_SUCCESS;\r
237 }\r
238\r
239 *NumBytes = NumOfBytesRead;\r
240 return EFI_BAD_BUFFER_SIZE;\r
241}\r
242\r
243\r
244\r
245/**\r
246 Writes the specified number of bytes from the input buffer to the block.\r
247\r
248 @param This Indicates the calling context.\r
249 @param Lba The starting logical block index to write to.\r
250 @param Offset Offset into the block at which to begin writing.\r
251 @param NumBytes Pointer to a UINT32. At entry, *NumBytes\r
252 contains the total size of the buffer. At exit,\r
253 *NumBytes contains the total number of bytes\r
254 actually written.\r
255 @param Buffer Pinter to a caller-allocated buffer that\r
256 contains the source for the write.\r
257\r
258 @retval EFI_SUCCESS The firmware volume was written successfully.\r
259 @retval EFI_BAD_BUFFER_SIZE The write was attempted across an LBA boundary.\r
260 On output, NumBytes contains the total number of\r
261 bytes actually written.\r
262 @retval EFI_ACCESS_DENIED The firmware volume is in the WriteDisabled\r
263 state.\r
264 @retval EFI_DEVICE_ERROR The block device is malfunctioning and could not\r
265 be written.\r
266 @retval EFI_UNSUPPORTED Not supported.\r
267\r
268**/\r
269EFI_STATUS\r
270EFIAPI\r
271FwVolBlockWriteBlock (\r
272 IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,\r
273 IN EFI_LBA Lba,\r
274 IN UINTN Offset,\r
275 IN OUT UINTN *NumBytes,\r
276 IN UINT8 *Buffer\r
277 )\r
278{\r
279 return EFI_UNSUPPORTED;\r
280}\r
281\r
282\r
283\r
284/**\r
285 Get Fvb's base address.\r
286\r
287 @param This Indicates the calling context.\r
288 @param Address Fvb device base address.\r
289\r
290 @retval EFI_SUCCESS Successfully got Fvb's base address.\r
291 @retval EFI_UNSUPPORTED Not supported.\r
292\r
293**/\r
294EFI_STATUS\r
295EFIAPI\r
296FwVolBlockGetPhysicalAddress (\r
297 IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,\r
298 OUT EFI_PHYSICAL_ADDRESS *Address\r
299 )\r
300{\r
301 EFI_FW_VOL_BLOCK_DEVICE *FvbDevice;\r
302\r
303 FvbDevice = FVB_DEVICE_FROM_THIS (This);\r
304\r
305 if (FvbDevice->FvbAttributes & EFI_FVB2_MEMORY_MAPPED) {\r
306 *Address = FvbDevice->BaseAddress;\r
307 return EFI_SUCCESS;\r
308 }\r
309\r
310 return EFI_UNSUPPORTED;\r
311}\r
312\r
313\r
314\r
315/**\r
316 Retrieves the size in bytes of a specific block within a firmware volume.\r
317\r
318 @param This Indicates the calling context.\r
319 @param Lba Indicates the block for which to return the\r
320 size.\r
321 @param BlockSize Pointer to a caller-allocated UINTN in which the\r
322 size of the block is returned.\r
323 @param NumberOfBlocks Pointer to a caller-allocated UINTN in which the\r
324 number of consecutive blocks starting with Lba\r
325 is returned. All blocks in this range have a\r
326 size of BlockSize.\r
327\r
328 @retval EFI_SUCCESS The firmware volume base address is returned.\r
329 @retval EFI_INVALID_PARAMETER The requested LBA is out of range.\r
330\r
331**/\r
332EFI_STATUS\r
333EFIAPI\r
334FwVolBlockGetBlockSize (\r
335 IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,\r
336 IN CONST EFI_LBA Lba,\r
337 IN OUT UINTN *BlockSize,\r
338 IN OUT UINTN *NumberOfBlocks\r
339 )\r
340{\r
341 UINTN TotalBlocks;\r
342 EFI_FW_VOL_BLOCK_DEVICE *FvbDevice;\r
343 EFI_FV_BLOCK_MAP_ENTRY *PtrBlockMapEntry;\r
344 EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader;\r
345\r
346 FvbDevice = FVB_DEVICE_FROM_THIS (This);\r
347\r
348 //\r
349 // Do parameter checking\r
350 //\r
351 if (Lba >= FvbDevice->NumBlocks) {\r
352 return EFI_INVALID_PARAMETER;\r
353 }\r
354\r
355 FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *)((UINTN)FvbDevice->BaseAddress);\r
356\r
357 PtrBlockMapEntry = FwVolHeader->BlockMap;\r
358\r
359 //\r
360 // Search the block map for the given block\r
361 //\r
362 TotalBlocks = 0;\r
363 while ((PtrBlockMapEntry->NumBlocks != 0) || (PtrBlockMapEntry->Length !=0 )) {\r
364 TotalBlocks += PtrBlockMapEntry->NumBlocks;\r
365 if (Lba < TotalBlocks) {\r
366 //\r
367 // We find the range\r
368 //\r
369 break;\r
370 }\r
371\r
372 PtrBlockMapEntry++;\r
373 }\r
374\r
375 *BlockSize = PtrBlockMapEntry->Length;\r
376 *NumberOfBlocks = TotalBlocks - (UINTN)Lba;\r
377\r
378 return EFI_SUCCESS;\r
379}\r
380\r
381\r
382\r
383/**\r
384 This routine produces a firmware volume block protocol on a given\r
385 buffer.\r
386\r
387 @param BaseAddress base address of the firmware volume image\r
388 @param Length length of the firmware volume image\r
389 @param ParentHandle handle of parent firmware volume, if this image\r
390 came from an FV image file in another firmware\r
391 volume (ala capsules)\r
392 @param FvProtocol Firmware volume block protocol produced.\r
393\r
394 @retval EFI_VOLUME_CORRUPTED Volume corrupted.\r
395 @retval EFI_OUT_OF_RESOURCES No enough buffer to be allocated.\r
396 @retval EFI_SUCCESS Successfully produced a FVB protocol on given\r
397 buffer.\r
398\r
399**/\r
400EFI_STATUS\r
401ProduceFVBProtocolOnBuffer (\r
402 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
403 IN UINT64 Length,\r
404 IN EFI_HANDLE ParentHandle,\r
405 OUT EFI_HANDLE *FvProtocol OPTIONAL\r
406 )\r
407{\r
408 EFI_STATUS Status;\r
409 EFI_FW_VOL_BLOCK_DEVICE *FvbDev;\r
410 EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader;\r
411 UINTN BlockIndex;\r
412 UINTN BlockIndex2;\r
413 UINTN LinearOffset;\r
414 UINT32 FvAlignment;\r
415 EFI_FV_BLOCK_MAP_ENTRY *PtrBlockMapEntry;\r
416\r
417 FvAlignment = 0;\r
418 FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN) BaseAddress;\r
419 //\r
420 // Validate FV Header, if not as expected, return\r
421 //\r
422 if (FwVolHeader->Signature != EFI_FVH_SIGNATURE) {\r
423 return EFI_VOLUME_CORRUPTED;\r
424 }\r
425 //\r
426 // Get FvHeader alignment\r
427 //\r
428 FvAlignment = 1 << ((FwVolHeader->Attributes & EFI_FVB2_ALIGNMENT) >> 16);\r
429 if (FvAlignment < 8) {\r
430 FvAlignment = 8;\r
431 }\r
432 if ((UINTN)BaseAddress % FvAlignment != 0) {\r
433 //\r
434 // FvImage buffer is not at its required alignment.\r
435 //\r
436 return EFI_VOLUME_CORRUPTED;\r
437 }\r
438 //\r
439 // Allocate EFI_FW_VOL_BLOCK_DEVICE\r
440 //\r
441 FvbDev = AllocateCopyPool (sizeof (EFI_FW_VOL_BLOCK_DEVICE), &mFwVolBlock);\r
442 if (FvbDev == NULL) {\r
443 return EFI_OUT_OF_RESOURCES;\r
444 }\r
445\r
446 FvbDev->BaseAddress = BaseAddress;\r
447 FvbDev->FvbAttributes = FwVolHeader->Attributes;\r
448 FvbDev->FwVolBlockInstance.ParentHandle = ParentHandle;\r
449\r
450 //\r
451 // Init the block caching fields of the device\r
452 // First, count the number of blocks\r
453 //\r
454 FvbDev->NumBlocks = 0;\r
455 for (PtrBlockMapEntry = FwVolHeader->BlockMap;\r
456 PtrBlockMapEntry->NumBlocks != 0;\r
457 PtrBlockMapEntry++) {\r
458 FvbDev->NumBlocks += PtrBlockMapEntry->NumBlocks;\r
459 }\r
460 //\r
461 // Second, allocate the cache\r
462 //\r
463 FvbDev->LbaCache = AllocatePool (FvbDev->NumBlocks * sizeof (LBA_CACHE));\r
464 if (FvbDev->LbaCache == NULL) {\r
465 CoreFreePool (FvbDev);\r
466 return EFI_OUT_OF_RESOURCES;\r
467 }\r
468 //\r
469 // Last, fill in the cache with the linear address of the blocks\r
470 //\r
471 BlockIndex = 0;\r
472 LinearOffset = 0;\r
473 for (PtrBlockMapEntry = FwVolHeader->BlockMap;\r
474 PtrBlockMapEntry->NumBlocks != 0; PtrBlockMapEntry++) {\r
475 for (BlockIndex2 = 0; BlockIndex2 < PtrBlockMapEntry->NumBlocks; BlockIndex2++) {\r
476 FvbDev->LbaCache[BlockIndex].Base = LinearOffset;\r
477 FvbDev->LbaCache[BlockIndex].Length = PtrBlockMapEntry->Length;\r
478 LinearOffset += PtrBlockMapEntry->Length;\r
479 BlockIndex++;\r
480 }\r
481 }\r
482\r
483 //\r
484 // Set up the devicepath\r
485 //\r
486 FvbDev->DevicePath.MemMapDevPath.StartingAddress = BaseAddress;\r
487 FvbDev->DevicePath.MemMapDevPath.EndingAddress = BaseAddress + FwVolHeader->FvLength - 1;\r
488\r
489 //\r
490 //\r
491 // Attach FvVolBlock Protocol to new handle\r
492 //\r
493 Status = CoreInstallMultipleProtocolInterfaces (\r
494 &FvbDev->Handle,\r
495 &gEfiFirmwareVolumeBlockProtocolGuid, &FvbDev->FwVolBlockInstance,\r
496 &gEfiDevicePathProtocolGuid, &FvbDev->DevicePath,\r
497 &gEfiFirmwareVolumeDispatchProtocolGuid, NULL,\r
498 NULL\r
499 );\r
500\r
501 //\r
502 // If they want the handle back, set it.\r
503 //\r
504 if (FvProtocol != NULL) {\r
505 *FvProtocol = FvbDev->Handle;\r
506 }\r
507\r
508 return Status;\r
509}\r
510\r
511\r
512\r
513/**\r
514 This routine is the driver initialization entry point. It initializes the\r
515 libraries, consumes FV hobs and NT_NON_MM_FV environment variable and\r
516 produces instances of FW_VOL_BLOCK_PROTOCOL as appropriate.\r
517\r
518 @param ImageHandle The image handle.\r
519 @param SystemTable The system table.\r
520\r
521 @retval EFI_SUCCESS Successfully initialized firmware volume block\r
522 driver.\r
523\r
524**/\r
525EFI_STATUS\r
526EFIAPI\r
527FwVolBlockDriverInit (\r
528 IN EFI_HANDLE ImageHandle,\r
529 IN EFI_SYSTEM_TABLE *SystemTable\r
530 )\r
531{\r
532 EFI_PEI_HOB_POINTERS FvHob;\r
533\r
534 //\r
535 // Core Needs Firmware Volumes to function\r
536 //\r
537 FvHob.Raw = GetHobList ();\r
538 while ((FvHob.Raw = GetNextHob (EFI_HOB_TYPE_FV, FvHob.Raw)) != NULL) {\r
539 //\r
540 // Produce an FVB protocol for it\r
541 //\r
542 ProduceFVBProtocolOnBuffer (FvHob.FirmwareVolume->BaseAddress, FvHob.FirmwareVolume->Length, NULL, NULL);\r
543 FvHob.Raw = GET_NEXT_HOB (FvHob);\r
544 }\r
545\r
546 return EFI_SUCCESS;\r
547}\r
548\r
549\r
550\r
551/**\r
552 This DXE service routine is used to process a firmware volume. In\r
553 particular, it can be called by BDS to process a single firmware\r
554 volume found in a capsule.\r
555\r
556 @param FvHeader pointer to a firmware volume header\r
557 @param Size the size of the buffer pointed to by FvHeader\r
558 @param FVProtocolHandle the handle on which a firmware volume protocol\r
559 was produced for the firmware volume passed in.\r
560\r
561 @retval EFI_OUT_OF_RESOURCES if an FVB could not be produced due to lack of\r
562 system resources\r
563 @retval EFI_VOLUME_CORRUPTED if the volume was corrupted\r
564 @retval EFI_SUCCESS a firmware volume protocol was produced for the\r
565 firmware volume\r
566\r
567**/\r
568EFI_STATUS\r
569CoreProcessFirmwareVolume (\r
570 IN VOID *FvHeader,\r
571 IN UINTN Size,\r
572 OUT EFI_HANDLE *FVProtocolHandle\r
573 )\r
574{\r
575 VOID *Ptr;\r
576 EFI_STATUS Status;\r
577\r
578 *FVProtocolHandle = NULL;\r
579 Status = ProduceFVBProtocolOnBuffer (\r
580 (EFI_PHYSICAL_ADDRESS) (UINTN) FvHeader,\r
581 (UINT64)Size,\r
582 NULL,\r
583 FVProtocolHandle\r
584 );\r
585 //\r
586 // Since in our implementation we use register-protocol-notify to put a\r
587 // FV protocol on the FVB protocol handle, we can't directly verify that\r
588 // the FV protocol was produced. Therefore here we will check the handle\r
589 // and make sure an FV protocol is on it. This indicates that all went\r
590 // well. Otherwise we have to assume that the volume was corrupted\r
591 // somehow.\r
592 //\r
593 if (!EFI_ERROR(Status)) {\r
594 Ptr = NULL;\r
595 Status = CoreHandleProtocol (*FVProtocolHandle, &gEfiFirmwareVolume2ProtocolGuid, (VOID **) &Ptr);\r
596 if (EFI_ERROR(Status) || (Ptr == NULL)) {\r
597 return EFI_VOLUME_CORRUPTED;\r
598 }\r
599 return EFI_SUCCESS;\r
600 }\r
601 return Status;\r
602}\r
603\r
604\r
605\r