ACPI之 系统地址映射接口

本文介绍了UEFI规范中内存映射的相关概念和技术细节,包括通过不同方式将内存信息传递给OSPM的方法,ACPI中定义的内存范围类型,以及UEFI GetMemoryMap()函数的具体实现。

目前有三种方法将内存映射告诉OSPM,

1 . 通过 BIOS INT 15.

2. UEFI 通过GetMemoryMap()  boot service 告诉OS loader, 然后OS loader 告诉OSPM.

3. 如果内存资源被动态的增加或删除,我们可以通过定义在ACPI 名字空间里的内存设备去表达出来。


ACPI 定义了五种范围: AdddressRangeMemory, AddressRangeACPI, AddressRangeNVS, 

AddressRangeUnusable, AddressRangeReserved.


valueMnemonic  助记符Description
1AddressRangeMemory这一段内存是可以供操作系统使用的
2AddressRangeReserved这一段内存已经被用了,或者保留起来的,不被OS的memory manager去分配
3AddressRangeACPI当OS 读过ACPI 表之后,就可使用的一段范围
4AddressRangeNVSACPI NVS 内存,这一段内存已经被用了或者保留起来,是不能被OS去用的
5AddressRangeUnusuable这是一段不能有的地址,因为检测到了错误
6AddressRangeDisabled一段没有被enabled的地址, 也是ospm 不可以用的
其他Undefined未定义,保留或者将来用。

BIOS 用AddressRangeReserved 把某些地址屏蔽起来,不给可编程器件去用,出于下列这些原因:

 1  这段地址包含了system BIOS.

 2 The address range contains RAM in use by the ROM.

 3. 这段地址被memory-mapped device 使用了

  4. 出于其他任何一种原因,不适合给standard device 作为memory space 用

  5. 这一段地址范围位于NVRAM device里面,往这些设备去读写,不一定成功

   5. The address range is within an NVRAM device where reads and writes to memory locations are

        no longer successful, that is, the deice was worn out.



UEFI GetMemoryMap() Boot Service Function

EFI 使用 GetMemoryMap() boot services 这个函数将内存资源的情况告诉OS loader,  后续OS loader 一定要将这个信息传给OSPM.


GetMemoryMap 这个函数只存在于boot service 这段时间,boot time service 和 run time service 之间的界限在于ExitBootServices()  函数的执行。


GetMemoryMap() 返回一个数组,里面放着内存描述符。 这些内存描述符定义了所有安装在板子上的RAM.


每一个描述符,有这么一个成员,去标示(dictate) OS 应该怎么对待这段区域。


/**
  This function returns a copy of the current memory map. The map is an array of
  memory descriptors, each of which describes a contiguous block of memory.

  @param  MemoryMapSize          A pointer to the size, in bytes, of the
                                 MemoryMap buffer. On input, this is the size of
                                 the buffer allocated by the caller.  On output,
                                 it is the size of the buffer returned by the
                                 firmware  if the buffer was large enough, or the
                                 size of the buffer needed  to contain the map if
                                 the buffer was too small.
  @param  MemoryMap              A pointer to the buffer in which firmware places
                                 the current memory map.
  @param  MapKey                 A pointer to the location in which firmware
                                 returns the key for the current memory map.
  @param  DescriptorSize         A pointer to the location in which firmware
                                 returns the size, in bytes, of an individual
                                 EFI_MEMORY_DESCRIPTOR.
  @param  DescriptorVersion      A pointer to the location in which firmware
                                 returns the version number associated with the
                                 EFI_MEMORY_DESCRIPTOR.

  @retval EFI_SUCCESS            The memory map was returned in the MemoryMap
                                 buffer.
  @retval EFI_BUFFER_TOO_SMALL   The MemoryMap buffer was too small. The current
                                 buffer size needed to hold the memory map is
                                 returned in MemoryMapSize.
  @retval EFI_INVALID_PARAMETER  One of the parameters has an invalid value.

**/
EFI_STATUS
EFIAPI
CoreGetMemoryMap (
  IN OUT UINTN                  *MemoryMapSize,
  IN OUT EFI_MEMORY_DESCRIPTOR  *MemoryMap,
  OUT UINTN                     *MapKey,
  OUT UINTN                     *DescriptorSize,
  OUT UINT32                    *DescriptorVersion
  )
{
  EFI_STATUS                        Status;
  UINTN                             Size;
  UINTN                             BufferSize;
  UINTN                             NumberOfEntries;
  LIST_ENTRY                        *Link;
  MEMORY_MAP                        *Entry;
  EFI_GCD_MAP_ENTRY                 *GcdMapEntry;
  EFI_MEMORY_TYPE                   Type;
  EFI_MEMORY_DESCRIPTOR             *MemoryMapStart;

  //
  // Make sure the parameters are valid
  //
  if (MemoryMapSize == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  CoreAcquireGcdMemoryLock ();

  //
  // Count the number of Reserved and runtime MMIO entries
  // And, count the number of Persistent entries.
  //
  NumberOfEntries = 0;
  for (Link = mGcdMemorySpaceMap.ForwardLink; Link != &mGcdMemorySpaceMap; Link = Link->ForwardLink) {
    GcdMapEntry = CR (Link, EFI_GCD_MAP_ENTRY, Link, EFI_GCD_MAP_SIGNATURE);
    if ((GcdMapEntry->GcdMemoryType == EfiGcdMemoryTypePersistentMemory) || 
        (GcdMapEntry->GcdMemoryType == EfiGcdMemoryTypeReserved) ||
        ((GcdMapEntry->GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo) &&
        ((GcdMapEntry->Attributes & EFI_MEMORY_RUNTIME) == EFI_MEMORY_RUNTIME))) {
      NumberOfEntries ++;
    }
  }

  Size = sizeof (EFI_MEMORY_DESCRIPTOR);

  //
  // Make sure Size != sizeof(EFI_MEMORY_DESCRIPTOR). This will
  // prevent people from having pointer math bugs in their code.
  // now you have to use *DescriptorSize to make things work.
  //
  Size += sizeof(UINT64) - (Size % sizeof (UINT64));

  if (DescriptorSize != NULL) {
    *DescriptorSize = Size;
  }

  if (DescriptorVersion != NULL) {
    *DescriptorVersion = EFI_MEMORY_DESCRIPTOR_VERSION;
  }

  CoreAcquireMemoryLock ();

  //
  // Compute the buffer size needed to fit the entire map
  //
  BufferSize = Size * NumberOfEntries;
  for (Link = gMemoryMap.ForwardLink; Link != &gMemoryMap; Link = Link->ForwardLink) {
    BufferSize += Size;
  }

  if (*MemoryMapSize < BufferSize) {
    Status = EFI_BUFFER_TOO_SMALL;
    goto Done;
  }

  if (MemoryMap == NULL) {
    Status = EFI_INVALID_PARAMETER;
    goto Done;
  }

  //
  // Build the map
  //
  ZeroMem (MemoryMap, BufferSize);
  MemoryMapStart = MemoryMap;
  for (Link = gMemoryMap.ForwardLink; Link != &gMemoryMap; Link = Link->ForwardLink) {
    Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);
    ASSERT (Entry->VirtualStart == 0);

    //
    // Convert internal map into an EFI_MEMORY_DESCRIPTOR
    //
    MemoryMap->Type           = Entry->Type;
    MemoryMap->PhysicalStart  = Entry->Start;
    MemoryMap->VirtualStart   = Entry->VirtualStart;
    MemoryMap->NumberOfPages  = RShiftU64 (Entry->End - Entry->Start + 1, EFI_PAGE_SHIFT);
    //
    // If the memory type is EfiConventionalMemory, then determine if the range is part of a
    // memory type bin and needs to be converted to the same memory type as the rest of the
    // memory type bin in order to minimize EFI Memory Map changes across reboots.  This
    // improves the chances for a successful S4 resume in the presence of minor page allocation
    // differences across reboots.
    //
    if (MemoryMap->Type == EfiConventionalMemory) {
      for (Type = (EFI_MEMORY_TYPE) 0; Type < EfiMaxMemoryType; Type++) {
        if (mMemoryTypeStatistics[Type].Special                        &&
            mMemoryTypeStatistics[Type].NumberOfPages > 0              &&
            Entry->Start >= mMemoryTypeStatistics[Type].BaseAddress    &&
            Entry->End   <= mMemoryTypeStatistics[Type].MaximumAddress) {
          MemoryMap->Type = Type;
        }
      }
    }
    MemoryMap->Attribute = Entry->Attribute;
    if (MemoryMap->Type < EfiMaxMemoryType) {
      if (mMemoryTypeStatistics[MemoryMap->Type].Runtime) {
        MemoryMap->Attribute |= EFI_MEMORY_RUNTIME;
      }
    }

    //
    // Check to see if the new Memory Map Descriptor can be merged with an 
    // existing descriptor if they are adjacent and have the same attributes
    //
    MemoryMap = MergeMemoryMapDescriptor (MemoryMapStart, MemoryMap, Size);
  }

  for (Link = mGcdMemorySpaceMap.ForwardLink; Link != &mGcdMemorySpaceMap; Link = Link->ForwardLink) {
    GcdMapEntry = CR (Link, EFI_GCD_MAP_ENTRY, Link, EFI_GCD_MAP_SIGNATURE);
    if ((GcdMapEntry->GcdMemoryType == EfiGcdMemoryTypeReserved) ||
        ((GcdMapEntry->GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo) &&
        ((GcdMapEntry->Attributes & EFI_MEMORY_RUNTIME) == EFI_MEMORY_RUNTIME))) {
      // 
      // Create EFI_MEMORY_DESCRIPTOR for every Reserved and runtime MMIO GCD entries
      //
      MemoryMap->PhysicalStart = GcdMapEntry->BaseAddress;
      MemoryMap->VirtualStart  = 0;
      MemoryMap->NumberOfPages = RShiftU64 ((GcdMapEntry->EndAddress - GcdMapEntry->BaseAddress + 1), EFI_PAGE_SHIFT);
      MemoryMap->Attribute     = GcdMapEntry->Attributes & ~EFI_MEMORY_PORT_IO;

      if (GcdMapEntry->GcdMemoryType == EfiGcdMemoryTypeReserved) {
        MemoryMap->Type = EfiReservedMemoryType;
      } else if (GcdMapEntry->GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo) {
        if ((GcdMapEntry->Attributes & EFI_MEMORY_PORT_IO) == EFI_MEMORY_PORT_IO) {
          MemoryMap->Type = EfiMemoryMappedIOPortSpace;
        } else {
          MemoryMap->Type = EfiMemoryMappedIO;
        }
      }

      //
      // Check to see if the new Memory Map Descriptor can be merged with an 
      // existing descriptor if they are adjacent and have the same attributes
      //
      MemoryMap = MergeMemoryMapDescriptor (MemoryMapStart, MemoryMap, Size);
    }
    
    if (GcdMapEntry->GcdMemoryType == EfiGcdMemoryTypePersistentMemory) {
      // 
      // Create EFI_MEMORY_DESCRIPTOR for every Persistent GCD entries
      //
      MemoryMap->PhysicalStart = GcdMapEntry->BaseAddress;
      MemoryMap->VirtualStart  = 0;
      MemoryMap->NumberOfPages = RShiftU64 ((GcdMapEntry->EndAddress - GcdMapEntry->BaseAddress + 1), EFI_PAGE_SHIFT);
      MemoryMap->Attribute     = GcdMapEntry->Attributes | EFI_MEMORY_NV;
      MemoryMap->Type          = EfiPersistentMemory;
      
      //
      // Check to see if the new Memory Map Descriptor can be merged with an 
      // existing descriptor if they are adjacent and have the same attributes
      //
      MemoryMap = MergeMemoryMapDescriptor (MemoryMapStart, MemoryMap, Size);
    }
  }

  //
  // Compute the size of the buffer actually used after all memory map descriptor merge operations
  //
  BufferSize = ((UINT8 *)MemoryMap - (UINT8 *)MemoryMapStart);

  Status = EFI_SUCCESS;

Done:
  //
  // Update the map key finally
  //
  if (MapKey != NULL) {
    *MapKey = mMemoryMapKey;
  }

  CoreReleaseMemoryLock ();

  CoreReleaseGcdMemoryLock ();

  *MemoryMapSize = BufferSize;

  return Status;
}



评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值