]> git.proxmox.com Git - mirror_edk2.git/blob - QuarkPlatformPkg/Platform/Dxe/MemorySubClass/MemorySubClass.c
QuarkPlatformPkg: Add new package for Galileo boards
[mirror_edk2.git] / QuarkPlatformPkg / Platform / Dxe / MemorySubClass / MemorySubClass.c
1 /** @file
2 This is the driver that locates the MemoryConfigurationData Variable, if it
3 exists, and reports the data to the DataHub.
4
5 Copyright (c) 2013-2015 Intel Corporation.
6
7 This program and the accompanying materials
8 are licensed and made available under the terms and conditions of the BSD License
9 which accompanies this distribution. The full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php
11
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
14
15 **/
16
17 #include "MemorySubClass.h"
18
19 extern UINT8 MemorySubClassStrings[];
20
21 EFI_GUID gEfiMemorySubClassDriverGuid = EFI_MEMORY_SUBCLASS_DRIVER_GUID;
22
23 EFI_STATUS
24 MemorySubClassEntryPoint (
25 IN EFI_HANDLE ImageHandle,
26 IN EFI_SYSTEM_TABLE *SystemTable
27 )
28 /*++
29
30 Routine Description:
31 This is the standard EFI driver point that detects whether there is a
32 MemoryConfigurationData Variable and, if so, reports memory configuration info
33 to the DataHub.
34
35 Arguments:
36 ImageHandle - Handle for the image of this driver
37 SystemTable - Pointer to the EFI System Table
38
39 Returns:
40 EFI_SUCCESS if the data is successfully reported
41 EFI_NOT_FOUND if the HOB list could not be located.
42
43 --*/
44 {
45 // UINT8 Index;
46 UINTN DataSize;
47 UINT8 Dimm;
48 UINTN StringBufferSize;
49 UINT8 NumSlots;
50 UINTN DevLocStrLen;
51 UINTN BankLocStrLen;
52 UINTN ManuStrLen;
53 UINTN SerialNumStrLen;
54 UINTN AssertTagStrLen;
55 UINTN PartNumStrLen;
56 UINTN MemoryDeviceSize;
57 CHAR8* OptionalStrStart;
58 UINT16 ArrayInstance;
59 UINT64 DimmMemorySize;
60 UINT64 TotalMemorySize;
61 UINT32 Data;
62 UINT32 MemoryCapacity;
63 BOOLEAN MemoryDeviceSizeUnitMega;
64 EFI_STATUS Status;
65 EFI_STRING StringBuffer;
66 EFI_STRING DevLocStr;
67 EFI_STRING BankLocStr;
68 EFI_STRING ManuStr;
69 EFI_STRING SerialNumStr;
70 EFI_STRING AssertTagStr;
71 EFI_STRING PartNumStr;
72 EFI_HII_HANDLE HiiHandle;
73 EFI_SMBIOS_HANDLE MemArraySmbiosHandle;
74 EFI_SMBIOS_HANDLE MemArrayMappedAddrSmbiosHandle;
75 EFI_SMBIOS_HANDLE MemDevSmbiosHandle;
76 EFI_SMBIOS_HANDLE MemDevMappedAddrSmbiosHandle;
77 EFI_SMBIOS_HANDLE MemModuleInfoSmbiosHandle;
78 SMBIOS_TABLE_TYPE6 *Type6Record;
79 SMBIOS_TABLE_TYPE16 *Type16Record;
80 SMBIOS_TABLE_TYPE17 *Type17Record;
81 SMBIOS_TABLE_TYPE19 *Type19Record;
82 SMBIOS_TABLE_TYPE20 *Type20Record;
83 EFI_SMBIOS_PROTOCOL *Smbios;
84 EFI_MEMORY_ARRAY_LINK_DATA ArrayLink;
85 EFI_MEMORY_ARRAY_LOCATION_DATA ArrayLocationData;
86 EFI_MEMORY_DEVICE_START_ADDRESS_DATA DeviceStartAddress;
87
88
89 DataSize = 0;
90 Dimm = 0;
91
92
93 //
94 // Allocate Buffers
95 //
96 StringBufferSize = (sizeof (CHAR16)) * 100;
97 StringBuffer = AllocateZeroPool (StringBufferSize);
98 ASSERT (StringBuffer != NULL);
99
100 //
101 // Locate dependent protocols
102 //
103 Status = gBS->LocateProtocol (&gEfiSmbiosProtocolGuid, NULL, (VOID**)&Smbios);
104 ASSERT_EFI_ERROR (Status);
105
106
107 //
108 // Add our default strings to the HII database. They will be modified later.
109 //
110 HiiHandle = HiiAddPackages (
111 &gEfiMemorySubClassDriverGuid,
112 NULL,
113 MemorySubClassStrings,
114 NULL
115 );
116 ASSERT (HiiHandle != NULL);
117
118 //
119 // Create physical array and associated data for all mainboard memory
120 // This will translate into a Type 16 SMBIOS Record
121 //
122 ArrayInstance = 1;
123
124 McD0PciCfg32 (QNC_ACCESS_PORT_MCR) = MESSAGE_READ_DW (0x3, 0x8);
125 TotalMemorySize = McD0PciCfg32 (QNC_ACCESS_PORT_MDR);
126
127 ArrayLocationData.MemoryArrayLocation = EfiMemoryArrayLocationSystemBoard;
128 ArrayLocationData.MemoryArrayUse = EfiMemoryArrayUseSystemMemory;
129
130 ArrayLocationData.MemoryErrorCorrection = EfiMemoryErrorCorrectionNone;
131
132 Data = 0x40000000;//(UINT32) RShiftU64(MemConfigData->RowInfo.MaxMemory, 10);
133
134 ArrayLocationData.MaximumMemoryCapacity.Exponent = (UINT16) LowBitSet32 (Data);
135 ArrayLocationData.MaximumMemoryCapacity.Value = (UINT16) (Data >> ArrayLocationData.MaximumMemoryCapacity.Exponent);
136
137 NumSlots = 2;// (UINT8)(MemConfigData->RowInfo.MaxRows >> 1);
138 ArrayLocationData.NumberMemoryDevices = (UINT16)(NumSlots);
139
140 //
141 // Report top level physical array to Type 16 SMBIOS Record
142 //
143 Type16Record = AllocatePool(sizeof(SMBIOS_TABLE_TYPE16) + 1 + 1);
144 ZeroMem(Type16Record, sizeof(SMBIOS_TABLE_TYPE16) + 1 + 1);
145
146 Type16Record->Hdr.Type = EFI_SMBIOS_TYPE_PHYSICAL_MEMORY_ARRAY;
147 Type16Record->Hdr.Length = sizeof(SMBIOS_TABLE_TYPE16);
148 Type16Record->Hdr.Handle = 0;
149
150 Type16Record->Location = (UINT8)ArrayLocationData.MemoryArrayLocation;
151
152 Type16Record->Use = (UINT8)ArrayLocationData.MemoryArrayUse;
153
154 Type16Record->MemoryErrorCorrection = (UINT8)ArrayLocationData.MemoryErrorCorrection;
155
156 MemoryCapacity = (UINT32) ArrayLocationData.MaximumMemoryCapacity.Value * (1 << ((UINT32) ArrayLocationData.MaximumMemoryCapacity.Exponent - 10));
157 Type16Record->MaximumCapacity = MemoryCapacity;
158
159 Type16Record->MemoryErrorInformationHandle = 0xfffe;
160
161 Type16Record->NumberOfMemoryDevices = ArrayLocationData.NumberMemoryDevices;
162 //
163 // Don't change it. This handle will be referenced by type 17 records
164 //
165 MemArraySmbiosHandle = SMBIOS_HANDLE_PI_RESERVED;
166 Status = Smbios->Add (Smbios, NULL, &MemArraySmbiosHandle, (EFI_SMBIOS_TABLE_HEADER*) Type16Record);
167 FreePool(Type16Record);
168 ASSERT_EFI_ERROR (Status);
169
170 // Do associated data for each DIMM
171 //RowConfArray = &MemConfigData->RowConfArray;
172
173 //
174 // Get total memory size for the construction of smbios record type 19
175 //
176 //TotalMemorySize = 0;// MSG_BUS_READ(0x0208);
177
178 //
179 // Generate Memory Array Mapped Address info
180 //
181 Type19Record = AllocatePool(sizeof (SMBIOS_TABLE_TYPE19));
182 ZeroMem(Type19Record, sizeof(SMBIOS_TABLE_TYPE19));
183 Type19Record->Hdr.Type = EFI_SMBIOS_TYPE_MEMORY_ARRAY_MAPPED_ADDRESS;
184 Type19Record->Hdr.Length = sizeof(SMBIOS_TABLE_TYPE19);
185 Type19Record->Hdr.Handle = 0;
186 Type19Record->StartingAddress = 0;
187 Type19Record->EndingAddress = (UINT32)RShiftU64(TotalMemorySize, 10) - 1;
188 Type19Record->MemoryArrayHandle = MemArraySmbiosHandle;
189 Type19Record->PartitionWidth = (UINT8)(NumSlots);
190
191 //
192 // Generate Memory Array Mapped Address info (TYPE 19)
193 //
194 MemArrayMappedAddrSmbiosHandle = SMBIOS_HANDLE_PI_RESERVED;
195 Status = Smbios->Add (Smbios, NULL, &MemArrayMappedAddrSmbiosHandle, (EFI_SMBIOS_TABLE_HEADER*) Type19Record);
196 FreePool(Type19Record);
197 ASSERT_EFI_ERROR (Status);
198
199
200 // Use SPD data to generate Device Type info
201 ZeroMem (&ArrayLink, sizeof (EFI_MEMORY_ARRAY_LINK_DATA));
202 ArrayLink.MemoryDeviceLocator = STRING_TOKEN(STR_MEMORY_SUBCLASS_DEVICE_LOCATOR_0);
203 ArrayLink.MemoryBankLocator = STRING_TOKEN(STR_MEMORY_SUBCLASS_DEVICE_LOCATOR_0);
204 ArrayLink.MemoryAssetTag = STRING_TOKEN(STR_MEMORY_SUBCLASS_UNKNOWN);
205 ArrayLink.MemoryArrayLink.ProducerName = gEfiMemorySubClassDriverGuid;
206 ArrayLink.MemoryArrayLink.Instance = ArrayInstance;
207 ArrayLink.MemoryArrayLink.SubInstance = EFI_SUBCLASS_INSTANCE_NON_APPLICABLE;
208 ArrayLink.MemorySubArrayLink.ProducerName = gEfiMemorySubClassDriverGuid;
209 ArrayLink.MemorySubArrayLink.SubInstance = EFI_SUBCLASS_INSTANCE_NON_APPLICABLE;
210 ArrayLink.MemoryFormFactor = EfiMemoryFormFactorChip;
211 ArrayLink.MemoryType = EfiMemoryTypeDdr2;
212
213
214 StrCpy (StringBuffer, L"NO DIMM,MEMROY DOWN");
215 ArrayLink.MemoryManufacturer = HiiSetString (
216 HiiHandle,
217 0,
218 StringBuffer,
219 NULL
220 );
221 ArrayLink.MemorySerialNumber = HiiSetString (
222 HiiHandle,
223 0,
224 StringBuffer,
225 NULL
226 );
227
228 ArrayLink.MemoryPartNumber = HiiSetString (
229 HiiHandle,
230 0,
231 StringBuffer,
232 NULL
233 );
234
235 //
236 // Hardcode value. Need to revise for different configuration.
237 //
238 ArrayLink.MemoryTotalWidth = 64;
239 ArrayLink.MemoryDataWidth = 64;
240
241 DimmMemorySize = TotalMemorySize;// MSG_BUS_READ(0x0208);
242
243 ArrayLink.MemoryDeviceSize.Exponent = (UINT16) LowBitSet64 (DimmMemorySize);
244 ArrayLink.MemoryDeviceSize.Value = (UINT16) RShiftU64(DimmMemorySize, ArrayLink.MemoryDeviceSize.Exponent);
245 ArrayLink.MemoryTypeDetail.Synchronous = 1;
246 Data = 800;
247 ArrayLink.MemorySpeed = *((EFI_EXP_BASE10_DATA *) &Data);
248
249
250
251 DevLocStr = HiiGetPackageString(&gEfiMemorySubClassDriverGuid, ArrayLink.MemoryDeviceLocator, NULL);
252 DevLocStrLen = StrLen(DevLocStr);
253 ASSERT(DevLocStrLen <= SMBIOS_STRING_MAX_LENGTH);
254
255 BankLocStr = HiiGetPackageString(&gEfiMemorySubClassDriverGuid, ArrayLink.MemoryBankLocator, NULL);
256 BankLocStrLen = StrLen(BankLocStr);
257 ASSERT(BankLocStrLen <= SMBIOS_STRING_MAX_LENGTH);
258
259 ManuStr = HiiGetPackageString(&gEfiMemorySubClassDriverGuid, ArrayLink.MemoryManufacturer, NULL);
260 ManuStrLen = StrLen(ManuStr);
261 ASSERT(ManuStrLen <= SMBIOS_STRING_MAX_LENGTH);
262
263 SerialNumStr = HiiGetPackageString(&gEfiMemorySubClassDriverGuid, ArrayLink.MemorySerialNumber, NULL);
264 SerialNumStrLen = StrLen(SerialNumStr);
265 ASSERT(SerialNumStrLen <= SMBIOS_STRING_MAX_LENGTH);
266
267 AssertTagStr = HiiGetPackageString(&gEfiMemorySubClassDriverGuid, ArrayLink.MemoryAssetTag, NULL);
268 AssertTagStrLen = StrLen(AssertTagStr);
269 ASSERT(AssertTagStrLen <= SMBIOS_STRING_MAX_LENGTH);
270
271 PartNumStr = HiiGetPackageString(&gEfiMemorySubClassDriverGuid, ArrayLink.MemoryPartNumber, NULL);
272 PartNumStrLen = StrLen(PartNumStr);
273 ASSERT(PartNumStrLen <= SMBIOS_STRING_MAX_LENGTH);
274
275 //
276 // Report DIMM level memory module information to smbios (Type 6)
277 //
278 DataSize = sizeof(SMBIOS_TABLE_TYPE6) + DevLocStrLen + 1 + 1;
279 Type6Record = AllocatePool(DataSize);
280 ZeroMem(Type6Record, DataSize);
281 Type6Record->Hdr.Type = EFI_SMBIOS_TYPE_MEMORY_MODULE_INFORMATON;
282 Type6Record->Hdr.Length = sizeof (SMBIOS_TABLE_TYPE6);
283 Type6Record->Hdr.Handle = 0;
284 Type6Record->SocketDesignation = 1;
285 if (ArrayLink.MemorySpeed.Value == 0) {
286 Type6Record->CurrentSpeed = 0;
287 } else {
288 //
289 // Memory speed is in ns unit
290 //
291 Type6Record->CurrentSpeed = (UINT8)(1000 / (ArrayLink.MemorySpeed.Value));
292 }
293 //
294 // Device Size
295 //
296 MemoryDeviceSize = (UINTN)(ArrayLink.MemoryDeviceSize.Value) * (UINTN)(1 << ArrayLink.MemoryDeviceSize.Exponent);
297 if (MemoryDeviceSize == 0) {
298 *(UINT8*)&(Type6Record->InstalledSize) = 0x7F;
299 *(UINT8*)&(Type6Record->EnabledSize) = 0x7F;
300 } else {
301 MemoryDeviceSize = (UINTN) RShiftU64 ((UINT64) MemoryDeviceSize, 21);
302 while (MemoryDeviceSize != 0) {
303 (*(UINT8*)&(Type6Record->InstalledSize))++;
304 (*(UINT8*)&(Type6Record->EnabledSize))++;
305 MemoryDeviceSize = (UINTN) RShiftU64 ((UINT64) MemoryDeviceSize,1);
306 }
307 }
308
309 if (ArrayLink.MemoryFormFactor == EfiMemoryFormFactorDimm ||
310 ArrayLink.MemoryFormFactor == EfiMemoryFormFactorFbDimm) {
311 *(UINT16*)&Type6Record->CurrentMemoryType |= 1<<8;
312 }
313 if (ArrayLink.MemoryFormFactor == EfiMemoryFormFactorSimm) {
314 *(UINT16*)&Type6Record->CurrentMemoryType |= 1<<7;
315 }
316 if (ArrayLink.MemoryType == EfiMemoryTypeSdram) {
317 *(UINT16*)&Type6Record->CurrentMemoryType |= 1<<10;
318 }
319 if (ArrayLink.MemoryTypeDetail.Edo == 1) {
320 *(UINT16*)&Type6Record->CurrentMemoryType |= 1<<4;
321 }
322 if (ArrayLink.MemoryTypeDetail.FastPaged == 1) {
323 *(UINT16*)&Type6Record->CurrentMemoryType |= 1<<3;
324 }
325 OptionalStrStart = (CHAR8 *)(Type6Record + 1);
326 UnicodeStrToAsciiStr(DevLocStr, OptionalStrStart);
327 MemModuleInfoSmbiosHandle = SMBIOS_HANDLE_PI_RESERVED;
328 Status = Smbios->Add (Smbios, NULL, &MemModuleInfoSmbiosHandle, (EFI_SMBIOS_TABLE_HEADER*) Type6Record);
329 FreePool(Type6Record);
330 ASSERT_EFI_ERROR (Status);
331 //
332 // Report DIMM level Device Type to smbios (Type 17)
333 //
334 DataSize = sizeof (SMBIOS_TABLE_TYPE17) + DevLocStrLen + 1 + BankLocStrLen + 1 + ManuStrLen + 1 + SerialNumStrLen + 1 + AssertTagStrLen + 1 + PartNumStrLen + 1 + 1;
335 Type17Record = AllocatePool(DataSize);
336 ZeroMem(Type17Record, DataSize);
337 Type17Record->Hdr.Type = EFI_SMBIOS_TYPE_MEMORY_DEVICE;
338 Type17Record->Hdr.Length = sizeof (SMBIOS_TABLE_TYPE17);
339 Type17Record->Hdr.Handle = 0;
340
341 Type17Record->MemoryArrayHandle = MemArraySmbiosHandle;
342 Type17Record->MemoryErrorInformationHandle = 0xfffe;
343 Type17Record->TotalWidth = ArrayLink.MemoryTotalWidth;
344 Type17Record->DataWidth = ArrayLink.MemoryDataWidth;
345 //
346 // Device Size
347 //
348 MemoryDeviceSize = ((UINTN) ArrayLink.MemoryDeviceSize.Value) << (ArrayLink.MemoryDeviceSize.Exponent - 10);
349 MemoryDeviceSizeUnitMega = FALSE;
350 //
351 // kilo as unit
352 //
353 if (MemoryDeviceSize > 0xffff) {
354 MemoryDeviceSize = MemoryDeviceSize >> 10;
355 //
356 // Mega as unit
357 //
358 MemoryDeviceSizeUnitMega = TRUE;
359 }
360
361 MemoryDeviceSize = MemoryDeviceSize & 0x7fff;
362 if (MemoryDeviceSize != 0 && MemoryDeviceSizeUnitMega == FALSE) {
363 MemoryDeviceSize |= 0x8000;
364 }
365 Type17Record->Size = (UINT16)MemoryDeviceSize;
366
367 Type17Record->FormFactor = (UINT8)ArrayLink.MemoryFormFactor;
368 Type17Record->DeviceLocator = 1;
369 Type17Record->BankLocator = 2;
370 Type17Record->MemoryType = (UINT8)ArrayLink.MemoryType;
371 CopyMem (
372 (UINT8 *) &Type17Record->TypeDetail,
373 &ArrayLink.MemoryTypeDetail,
374 2
375 );
376
377 Type17Record->Speed = ArrayLink.MemorySpeed.Value;
378 Type17Record->Manufacturer = 3;
379 Type17Record->SerialNumber = 4;
380 Type17Record->AssetTag = 5;
381 Type17Record->PartNumber = 6;
382 //
383 // temporary solution for save device label information.
384 //
385 Type17Record->Attributes = (UINT8)(Dimm + 1);
386
387 OptionalStrStart = (CHAR8 *)(Type17Record + 1);
388 UnicodeStrToAsciiStr(DevLocStr, OptionalStrStart);
389 UnicodeStrToAsciiStr(BankLocStr, OptionalStrStart + DevLocStrLen + 1);
390 UnicodeStrToAsciiStr(ManuStr, OptionalStrStart + DevLocStrLen + 1 + BankLocStrLen + 1);
391 UnicodeStrToAsciiStr(SerialNumStr, OptionalStrStart + DevLocStrLen + 1 + BankLocStrLen + 1 + ManuStrLen + 1);
392 UnicodeStrToAsciiStr(AssertTagStr, OptionalStrStart + DevLocStrLen + 1 + BankLocStrLen + 1 + ManuStrLen + 1 + SerialNumStrLen + 1);
393 UnicodeStrToAsciiStr(PartNumStr, OptionalStrStart + DevLocStrLen + 1 + BankLocStrLen + 1 + ManuStrLen + 1 + SerialNumStrLen + 1 + AssertTagStrLen + 1);
394 MemDevSmbiosHandle = SMBIOS_HANDLE_PI_RESERVED;
395 Status = Smbios->Add (Smbios, NULL, &MemDevSmbiosHandle, (EFI_SMBIOS_TABLE_HEADER*) Type17Record);
396 FreePool(Type17Record);
397 ASSERT_EFI_ERROR (Status);
398
399 //
400 // Generate Memory Device Mapped Address info
401 //
402 ZeroMem(&DeviceStartAddress, sizeof(EFI_MEMORY_DEVICE_START_ADDRESS_DATA));
403 DeviceStartAddress.MemoryDeviceStartAddress = 0;
404 DeviceStartAddress.MemoryDeviceEndAddress = DeviceStartAddress.MemoryDeviceStartAddress + DimmMemorySize-1;
405 DeviceStartAddress.PhysicalMemoryDeviceLink.ProducerName = gEfiMemorySubClassDriverGuid;
406 DeviceStartAddress.PhysicalMemoryDeviceLink.Instance = ArrayInstance;
407 DeviceStartAddress.PhysicalMemoryDeviceLink.SubInstance = (UINT16)(Dimm + 1);
408 DeviceStartAddress.PhysicalMemoryArrayLink.ProducerName = gEfiMemorySubClassDriverGuid;
409 DeviceStartAddress.PhysicalMemoryArrayLink.Instance = ArrayInstance;
410 DeviceStartAddress.PhysicalMemoryArrayLink.SubInstance = EFI_SUBCLASS_INSTANCE_NON_APPLICABLE;
411
412 //
413 // Single channel mode
414 //
415 DeviceStartAddress.MemoryDevicePartitionRowPosition = 0x01;
416 DeviceStartAddress.MemoryDeviceInterleavePosition = 0x00;
417 DeviceStartAddress.MemoryDeviceInterleaveDataDepth = 0x00;
418
419 //
420 // Generate Memory Device Mapped Address info (TYPE 20)
421 //
422 Type20Record = AllocatePool(sizeof (SMBIOS_TABLE_TYPE20));
423 ZeroMem(Type20Record, sizeof (SMBIOS_TABLE_TYPE20));
424 Type20Record->Hdr.Type = EFI_SMBIOS_TYPE_MEMORY_DEVICE_MAPPED_ADDRESS;
425 Type20Record->Hdr.Length = sizeof(SMBIOS_TABLE_TYPE20);
426 Type20Record->Hdr.Handle = 0;
427
428 Type20Record->StartingAddress = (UINT32)RShiftU64 (DeviceStartAddress.MemoryDeviceStartAddress, 10);
429 Type20Record->EndingAddress = (UINT32)RShiftU64 (DeviceStartAddress.MemoryDeviceEndAddress, 10);
430 Type20Record->MemoryDeviceHandle = MemDevSmbiosHandle;
431 Type20Record->MemoryArrayMappedAddressHandle = MemArrayMappedAddrSmbiosHandle;
432 Type20Record->PartitionRowPosition = DeviceStartAddress.MemoryDevicePartitionRowPosition;
433 Type20Record->InterleavePosition = DeviceStartAddress.MemoryDeviceInterleavePosition;
434 Type20Record->InterleavedDataDepth = DeviceStartAddress.MemoryDeviceInterleaveDataDepth;
435 MemDevMappedAddrSmbiosHandle = SMBIOS_HANDLE_PI_RESERVED;
436 Status = Smbios->Add (Smbios, NULL, &MemDevMappedAddrSmbiosHandle, (EFI_SMBIOS_TABLE_HEADER*) Type20Record);
437 FreePool(Type20Record);
438 ASSERT_EFI_ERROR (Status);
439
440 return Status;
441 }