]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/Network/IScsiDxe/IScsiIbft.c
update file header
[mirror_edk2.git] / MdeModulePkg / Universal / Network / IScsiDxe / IScsiIbft.c
1 /** @file
2 Implementation for iSCSI Boot Firmware Table publication.
3
4 Copyright (c) 2004 - 2008, Intel Corporation
5 All rights reserved. This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13 Module Name:
14
15 IScsiIbft.c
16
17 Abstract:
18
19 Implementation for iSCSI Boot Firmware Table publication.
20
21 **/
22
23 #include "IScsiImpl.h"
24
25 /**
26 Initialize the header of the iSCSI Boot Firmware Table.
27
28 @param Header[in] The header of the iSCSI Boot Firmware Table.
29
30 @retval None.
31
32 **/
33 VOID
34 IScsiInitIbfTableHeader (
35 IN EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_HEADER *Header
36 )
37 {
38 ZeroMem (Header, sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_HEADER));
39
40 Header->Signature = EFI_ACPI_3_0_ISCSI_BOOT_FIRMWARE_TABLE_SIGNATURE;
41 Header->Length = IBFT_HEAP_OFFSET;
42 Header->Revision = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_REVISION;
43 Header->Checksum = 0;
44
45 Header->OemId[0] = 'I';
46 Header->OemId[1] = 'N';
47 Header->OemId[2] = 'T';
48 Header->OemId[3] = 'E';
49 Header->OemId[4] = 'L';
50 }
51
52 /**
53 Initialize the control section of the iSCSI Boot Firmware Table.
54
55 @param Table[in] The ACPI table.
56
57 @param HandleCount[in] The number of the handles associated with iSCSI sessions, it's
58 equal to the number of iSCSI sessions.
59
60 @retval None.
61
62 **/
63 VOID
64 IScsiInitControlSection (
65 IN EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_HEADER *Table,
66 IN UINTN HandleCount
67 )
68 {
69 EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE *Control;
70 UINTN NumOffset;
71
72 Control = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE *) (Table + 1);
73
74 ZeroMem (Control, sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE));
75
76 Control->Header.StructureId = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE_ID;
77 Control->Header.Version = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE_VERSION;
78 Control->Header.Length = sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE);
79
80 //
81 // Each session occupies two offsets, one for the NIC section,
82 // the other for the Target section.
83 //
84 NumOffset = 2 * HandleCount;
85 if (NumOffset > 4) {
86 //
87 // Need expand the control section if more than 2 NIC/Target sections
88 // exist.
89 //
90 Control->Header.Length = (UINT16) (Control->Header.Length + (NumOffset - 4) * sizeof (UINT16));
91 }
92 }
93
94 /**
95 Add one item into the heap.
96
97 @param Heap[in][out] On input, the current address of the heap; On output, the address of
98 the heap after the item is added.
99
100 @param Data[in] The data to add into the heap.
101
102 @param Len[in] Length of the Data in byte.
103
104 @retval None.
105
106 **/
107 VOID
108 IScsiAddHeapItem (
109 IN OUT UINT8 **Heap,
110 IN VOID *Data,
111 IN UINTN Len
112 )
113 {
114 //
115 // Add one byte for the NULL delimiter.
116 //
117 *Heap -= Len + 1;
118
119 CopyMem (*Heap, Data, Len);
120 *(*Heap + Len) = 0;
121 }
122
123 /**
124 Fill the Initiator section of the iSCSI Boot Firmware Table.
125
126 @param Table[in] The ACPI table.
127
128 @param Heap[in][out] The heap.
129
130 @param Handle[in] The handle associated with the iSCSI session.
131
132 @retval None.
133
134 **/
135 VOID
136 IScsiFillInitiatorSection (
137 IN EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_HEADER *Table,
138 IN OUT UINT8 **Heap,
139 IN EFI_HANDLE Handle
140 )
141 {
142 EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE *Control;
143 EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE *Initiator;
144 ISCSI_DRIVER_DATA *DriverData;
145 ISCSI_SESSION *Session;
146 ISCSI_PRIVATE_PROTOCOL *IScsiIdentifier;
147 EFI_STATUS Status;
148
149 Control = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE *) (Table + 1);
150
151 //
152 // Initiator section immediately follows the control section.
153 //
154 Initiator = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE *) ((UINT8 *) Control + IBFT_ROUNDUP (Control->Header.Length));
155
156 Control->InitiatorOffset = (UINT16) ((UINTN) Initiator - (UINTN) Table);
157
158 ZeroMem (Initiator, sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE));
159
160 Initiator->Header.StructureId = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE_ID;
161 Initiator->Header.Version = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE_VERSION;
162 Initiator->Header.Length = sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE);
163 Initiator->Header.Flags = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE_FLAG_BLOCK_VALID | EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE_FLAG_BOOT_SELECTED;
164
165 //
166 // Get the identifier from the handle.
167 //
168 Status = gBS->HandleProtocol (Handle, &mIScsiPrivateGuid, (VOID **) &IScsiIdentifier);
169 if (EFI_ERROR (Status)) {
170 ASSERT (FALSE);
171 return ;
172 }
173
174 DriverData = ISCSI_DRIVER_DATA_FROM_IDENTIFIER (IScsiIdentifier);
175 Session = &DriverData->Session;
176
177 //
178 // Fill the iSCSI Initiator Name into the heap.
179 //
180 IScsiAddHeapItem (Heap, Session->InitiatorName, Session->InitiatorNameLength - 1);
181
182 Initiator->IScsiNameLength = (UINT16) (Session->InitiatorNameLength - 1);
183 Initiator->IScsiNameOffset = (UINT16) ((UINTN) *Heap - (UINTN) Table);
184 }
185
186 /**
187 Map the v4 IP address into v6 IP address.
188
189 @param V4 The v4 IP address.
190
191 @param V6 The v6 IP address.
192
193 @retval None.
194
195 **/
196 VOID
197 IScsiMapV4ToV6Addr (
198 IN EFI_IPv4_ADDRESS *V4,
199 OUT EFI_IPv6_ADDRESS *V6
200 )
201 {
202 UINTN Index;
203
204 ZeroMem (V6, sizeof (EFI_IPv6_ADDRESS));
205
206 V6->Addr[10] = 0xff;
207 V6->Addr[11] = 0xff;
208
209 for (Index = 0; Index < 4; Index++) {
210 V6->Addr[12 + Index] = V4->Addr[Index];
211 }
212 }
213
214 /**
215 Get the NIC's PCI location and return it accroding to the composited
216 format defined in iSCSI Boot Firmware Table.
217
218 @param Controller[in] The handle of the controller.
219
220 @retval UINT16 The composited representation of the NIC PCI location.
221
222 **/
223 UINT16
224 IScsiGetNICPciLocation (
225 IN EFI_HANDLE Controller
226 )
227 {
228 EFI_STATUS Status;
229 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
230 EFI_HANDLE PciIoHandle;
231 EFI_PCI_IO_PROTOCOL *PciIo;
232 UINTN Segment;
233 UINTN Bus;
234 UINTN Device;
235 UINTN Function;
236
237 Status = gBS->HandleProtocol (
238 Controller,
239 &gEfiDevicePathProtocolGuid,
240 (VOID **)&DevicePath
241 );
242 if (EFI_ERROR (Status)) {
243 return 0;
244 }
245
246 Status = gBS->LocateDevicePath (
247 &gEfiPciIoProtocolGuid,
248 &DevicePath,
249 &PciIoHandle
250 );
251 if (EFI_ERROR (Status)) {
252 return 0;
253 }
254
255 Status = gBS->HandleProtocol (PciIoHandle, &gEfiPciIoProtocolGuid, (VOID **)&PciIo);
256 if (EFI_ERROR (Status)) {
257 return 0;
258 }
259
260 Status = PciIo->GetLocation (PciIo, &Segment, &Bus, &Device, &Function);
261 if (EFI_ERROR (Status)) {
262 return 0;
263 }
264
265 return (UINT16) ((Bus << 8) | (Device << 3) | Function);
266 }
267
268 /**
269 Get the MAC address of the controller.
270
271 @param Controller[in] The handle of the controller.
272
273 @retval EFI_MAC_ADDRESS * The mac address.
274
275 **/
276 EFI_MAC_ADDRESS *
277 IScsiGetMacAddress (
278 IN EFI_HANDLE Controller
279 )
280 {
281 EFI_STATUS Status;
282 EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
283
284 Status = gBS->HandleProtocol (
285 Controller,
286 &gEfiSimpleNetworkProtocolGuid,
287 (VOID **) &Snp
288 );
289 ASSERT_EFI_ERROR (Status);
290
291 return &Snp->Mode->PermanentAddress;
292 }
293
294 /**
295 Fill the NIC and target sections in iSCSI Boot Firmware Table.
296
297 @param Table[in] The buffer of the ACPI table.
298
299 @param Heap[in][out] The heap buffer used to store the variable length parameters such as iSCSI name.
300
301 @param HandleCount[in] The number of handles having iSCSI private protocol installed.
302
303 @param Handles[in] The handle buffer.
304
305 @retval None.
306
307 **/
308 VOID
309 IScsiFillNICAndTargetSections (
310 IN EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_HEADER *Table,
311 IN OUT UINT8 **Heap,
312 IN UINTN HandleCount,
313 IN EFI_HANDLE *Handles
314 )
315 {
316 EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE *Control;
317 EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE *Nic;
318 EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE *Target;
319 ISCSI_DRIVER_DATA *DriverData;
320 ISCSI_SESSION_CONFIG_DATA *SessionConfigData;
321 ISCSI_CHAP_AUTH_CONFIG_NVDATA *AuthConfig;
322 UINT16 *SectionOffset;
323 UINTN Index;
324 UINT16 Length;
325 EFI_MAC_ADDRESS *Mac;
326 ISCSI_PRIVATE_PROTOCOL *IScsiIdentifier;
327 EFI_STATUS Status;
328
329 //
330 // Get the offset of the first Nic and Target section.
331 //
332 Control = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE *) (Table + 1);
333 Nic = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE *) ((UINTN) Table +
334 Control->InitiatorOffset + IBFT_ROUNDUP (sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE)));
335 Target = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE *) ((UINTN) Nic +
336 IBFT_ROUNDUP (sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE)));
337
338 SectionOffset = &Control->NIC0Offset;
339
340 for (Index = 0; Index < HandleCount; Index++) {
341 Status = gBS->HandleProtocol (Handles[Index], &mIScsiPrivateGuid, (VOID **)&IScsiIdentifier);
342 if (EFI_ERROR (Status)) {
343 ASSERT (FALSE);
344 return ;
345 }
346
347 DriverData = ISCSI_DRIVER_DATA_FROM_IDENTIFIER (IScsiIdentifier);
348 SessionConfigData = &DriverData->Session.ConfigData;
349 AuthConfig = &DriverData->Session.AuthData.AuthConfig;
350
351 //
352 // Fill the Nic section.
353 //
354 ZeroMem (Nic, sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE));
355
356 Nic->Header.StructureId = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE_ID;
357 Nic->Header.Version = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE_VERSION;
358 Nic->Header.Length = sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE);
359 Nic->Header.Index = (UINT8) Index;
360 Nic->Header.Flags = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE_FLAG_BLOCK_VALID |
361 EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE_FLAG_BOOT_SELECTED |
362 EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE_FLAG_GLOBAL;
363
364 //
365 // Get the subnet mask prefix length.
366 //
367 Nic->SubnetMaskPrefixLength = IScsiGetSubnetMaskPrefixLength (&SessionConfigData->NvData.SubnetMask);
368
369 if (SessionConfigData->NvData.InitiatorInfoFromDhcp) {
370 Nic->Origin = IpPrefixOriginDhcp;
371 } else {
372 Nic->Origin = IpPrefixOriginManual;
373 }
374 //
375 // Map the various v4 addresses into v6 addresses.
376 //
377 IScsiMapV4ToV6Addr (&SessionConfigData->NvData.LocalIp, &Nic->Ip);
378 IScsiMapV4ToV6Addr (&SessionConfigData->NvData.Gateway, &Nic->Gateway);
379 IScsiMapV4ToV6Addr (&SessionConfigData->PrimaryDns, &Nic->PrimaryDns);
380 IScsiMapV4ToV6Addr (&SessionConfigData->SecondaryDns, &Nic->SecondaryDns);
381 IScsiMapV4ToV6Addr (&SessionConfigData->DhcpServer, &Nic->DhcpServer);
382
383 Mac = IScsiGetMacAddress (DriverData->Controller);
384 CopyMem (Nic->Mac, Mac, sizeof (Nic->Mac));
385
386 //
387 // Get the PCI location of the Nic.
388 //
389 Nic->PciLocation = IScsiGetNICPciLocation (DriverData->Controller);
390
391 *SectionOffset = (UINT16) ((UINTN) Nic - (UINTN) Table);
392 SectionOffset++;
393
394 //
395 // Fill the Target section.
396 //
397 ZeroMem (Target, sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE));
398
399 Target->Header.StructureId = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE_ID;
400 Target->Header.Version = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE_VERSION;
401 Target->Header.Length = sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE);
402 Target->Header.Index = (UINT8) Index;
403 Target->Header.Flags = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE_FLAG_BLOCK_VALID | EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE_FLAG_BOOT_SELECTED;
404 Target->Port = SessionConfigData->NvData.TargetPort;
405 Target->CHAPType = AuthConfig->CHAPType;
406 Target->NicIndex = (UINT8) Index;
407
408 IScsiMapV4ToV6Addr (&SessionConfigData->NvData.TargetIp, &Target->Ip);
409 CopyMem (Target->BootLun, SessionConfigData->NvData.BootLun, sizeof (Target->BootLun));
410
411 //
412 // Target iSCSI Name, CHAP name/secret, reverse CHAP name/secret.
413 //
414 Length = (UINT16) AsciiStrLen (SessionConfigData->NvData.TargetName);
415 IScsiAddHeapItem (Heap, SessionConfigData->NvData.TargetName, Length);
416
417 Target->IScsiNameLength = Length;
418 Target->IScsiNameOffset = (UINT16) ((UINTN) *Heap - (UINTN) Table);
419
420 if (Target->CHAPType != ISCSI_CHAP_NONE) {
421 //
422 // CHAP Name
423 //
424 Length = (UINT16) AsciiStrLen (AuthConfig->CHAPName);
425 IScsiAddHeapItem (Heap, AuthConfig->CHAPName, Length);
426 Target->CHAPNameLength = Length;
427 Target->CHAPNameOffset = (UINT16) ((UINTN) *Heap - (UINTN) Table);
428
429 //
430 // CHAP Secret
431 //
432 Length = (UINT16) AsciiStrLen (AuthConfig->CHAPSecret);
433 IScsiAddHeapItem (Heap, AuthConfig->CHAPSecret, Length);
434 Target->CHAPSecretLength = Length;
435 Target->CHAPSecretOffset = (UINT16) ((UINTN) *Heap - (UINTN) Table);
436
437 if (Target->CHAPType == ISCSI_CHAP_MUTUAL) {
438 //
439 // Reverse CHAP Name
440 //
441 Length = (UINT16) AsciiStrLen (AuthConfig->ReverseCHAPName);
442 IScsiAddHeapItem (Heap, AuthConfig->ReverseCHAPName, Length);
443 Target->ReverseCHAPNameLength = Length;
444 Target->ReverseCHAPNameOffset = (UINT16) ((UINTN) *Heap - (UINTN) Table);
445
446 //
447 // Reverse CHAP Secret
448 //
449 Length = (UINT16) AsciiStrLen (AuthConfig->ReverseCHAPSecret);
450 IScsiAddHeapItem (Heap, AuthConfig->ReverseCHAPSecret, Length);
451 Target->ReverseCHAPSecretLength = Length;
452 Target->ReverseCHAPSecretOffset = (UINT16) ((UINTN) *Heap - (UINTN) Table);
453 }
454 }
455
456 *SectionOffset = (UINT16) ((UINTN) Target - (UINTN) Table);
457 SectionOffset++;
458
459 //
460 // Advance to the next NIC/Target pair
461 //
462 Nic = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE *) ((UINTN) Target +
463 IBFT_ROUNDUP (sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE)));
464 Target = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE *) ((UINTN) Nic +
465 IBFT_ROUNDUP (sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE)));
466 }
467 }
468
469 /**
470 Publish and remove the iSCSI Boot Firmware Table according to the iSCSI
471 session status.
472
473 @param None.
474
475 @retval None.
476
477 **/
478 VOID
479 IScsiPublishIbft (
480 IN VOID
481 )
482 {
483 EFI_STATUS Status;
484 UINTN TableHandle;
485 EFI_ACPI_SUPPORT_PROTOCOL *AcpiSupport;
486 EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_HEADER *Table;
487 UINTN HandleCount;
488 EFI_HANDLE *HandleBuffer;
489 UINT8 *Heap;
490 INTN Index;
491 EFI_ACPI_TABLE_VERSION Version;
492 UINT32 Signature;
493
494 Status = gBS->LocateProtocol (&gEfiAcpiSupportProtocolGuid, NULL, (VOID **)&AcpiSupport);
495 if (EFI_ERROR (Status)) {
496 return ;
497 }
498 //
499 // Try to remove the old iSCSI Boot Firmware Table.
500 //
501 for (Index = 0;; Index++) {
502 Status = AcpiSupport->GetAcpiTable (
503 AcpiSupport,
504 Index,
505 (VOID **)&Table,
506 &Version,
507 &TableHandle
508 );
509 if (EFI_ERROR (Status)) {
510 break;
511 }
512
513 Signature = Table->Signature;
514 gBS->FreePool (Table);
515
516 if (Signature == EFI_ACPI_3_0_ISCSI_BOOT_FIRMWARE_TABLE_SIGNATURE) {
517 //
518 // Remove the table.
519 //
520 Status = AcpiSupport->SetAcpiTable (
521 AcpiSupport,
522 NULL,
523 FALSE,
524 Version,
525 &TableHandle
526 );
527 if (EFI_ERROR (Status)) {
528 return ;
529 }
530
531 break;
532 }
533 }
534 //
535 // Get all iSCSI private protocols.
536 //
537 Status = gBS->LocateHandleBuffer (
538 ByProtocol,
539 &mIScsiPrivateGuid,
540 NULL,
541 &HandleCount,
542 &HandleBuffer
543 );
544 if (EFI_ERROR (Status)) {
545 return ;
546 }
547 //
548 // Allocate 4k bytes to hold the ACPI table.
549 //
550 Table = AllocatePool (IBFT_MAX_SIZE);
551 if (Table == NULL) {
552 return ;
553 }
554
555 Heap = (UINT8 *) Table + IBFT_HEAP_OFFSET;
556
557 //
558 // Fill in the various section of the iSCSI Boot Firmware Table.
559 //
560 IScsiInitIbfTableHeader (Table);
561 IScsiInitControlSection (Table, HandleCount);
562 IScsiFillInitiatorSection (Table, &Heap, HandleBuffer[0]);
563 IScsiFillNICAndTargetSections (Table, &Heap, HandleCount, HandleBuffer);
564
565 gBS->FreePool (HandleBuffer);
566
567 TableHandle = 0;
568
569 //
570 // Install or update the iBFT table.
571 //
572 Status = AcpiSupport->SetAcpiTable (
573 AcpiSupport,
574 Table,
575 TRUE,
576 EFI_ACPI_TABLE_VERSION_3_0,
577 &TableHandle
578 );
579 if (!EFI_ERROR (Status)) {
580 AcpiSupport->PublishTables (AcpiSupport, EFI_ACPI_TABLE_VERSION_3_0);
581 }
582
583 gBS->FreePool (Table);
584 }