Michael Chang
2015-07-23 07:33:27 UTC
Hi,
I have problem in IPv6 PXE booting under OVMF that the process
terminated immediately right after selecting the UEFI IPv6 boot entry. I
have to change the IPv6 config policy from automatic to manual to get
things working again for me.
I looked a bit into this problem, it was terminated by the error in IaId
check
in NetworkPkg/Dhcp6Dxe/Dhcp6Impl.c::EfiDhcp6Configure function
//
// Make sure the (IaId, IaType) is unique over all the instances.
//
NET_LIST_FOR_EACH (Entry, &Service->Child) {
Other = NET_LIST_USER_STRUCT (Entry, DHCP6_INSTANCE, Link);
if (Other->IaCb.Ia != NULL &&
Other->IaCb.Ia->Descriptor.Type == Dhcp6CfgData->IaDescriptor.Type &&
Other->IaCb.Ia->Descriptor.IaId == Dhcp6CfgData->IaDescriptor.IaId
) {
return EFI_INVALID_PARAMETER;
}
}
Which would be saying clearly it was not "unique" among all the running
DHCPv6 instances. At that time there should be only two:
1. The IPv6 config instance, while the policy is set to automatic would
exchange information with server with the IaId initialized at
NetworkPkg/Ip6Dxe/Ip6ConfigImpl.c::Ip6ConfigInitInstance
Instance->IaId = NET_RANDOM (NetRandomInitSeed ());
for (Index = 0; Index < IpSb->SnpMode.HwAddressSize; Index++) {
Instance->IaId |= (IpSb->SnpMode.CurrentAddress.Addr[Index] << ((Index << 3) & 31));
}
2. The IPv6 PXE Base code driver would start another DHCPv6 session with
the IaId initialized at
NetworkPkg/UefiPxeBcDxe/PxeBcDriver.c::PxeBcCreateIp6Children
//
// Generate a random IAID for the Dhcp6 assigned address.
//
Private->IaId = NET_RANDOM (NetRandomInitSeed ());
if (Private->Snp != NULL) {
for (Index = 0; Index < Private->Snp->Mode->HwAddressSize; Index++) {
Private->IaId |= (Private->Snp->Mode->CurrentAddress.Addr[Index] << ((Index << 3) & 31));
}
}
Unfortunately both IaId collides, and quite surprisingly it's NET_RANDOM
(NetRandomInitSeed ()), or NetRandomInitSeed () generates the same
number for them.
UINT32
EFIAPI
NetRandomInitSeed (
VOID
)
{
EFI_TIME Time;
UINT32 Seed;
gRT->GetTime (&Time, NULL);
Seed = (~Time.Hour << 24 | Time.Day << 16 | Time.Minute << 8 | Time.Second);
Seed ^= Time.Nanosecond;
Seed ^= Time.Year << 7;
return Seed;
}
The returned Time.Nanosecond is always zero as the timer resolution is l
Hz for PC-AT CMOS RTC, that seems to be default real time clock driver
used by OVMF. At least from .dsc file it says so ..
PcAtChipsetPkg/PcatRealTimeClockRuntimeDxe/PcatRealTimeClockRuntimeDxe.inf
The NetRandomInitSeed is not random enough in this case, and the
NET_RANDOM marco is even more confusing as it's not a random number
generator either (so what is good for always feeding random "seed" to a
deterministic mathematic fucntion ??)
I don't have good idea how to fix the problem, but here are of my thoughs.
1. Use real timer clock driver which could provide higher resolution for
time service. Qemu is able to provide HPET timer but I don't know how to
configure OVMF to use it.
2. Use GetPerformanceCounter(), but that would increase image size by
linking TimerLib.
3. The IaId has to be "unique" thus not necessarily to be random,
probably it should be derived by some properties like MAC address and an
assgined index (staring from zero) for the interface to be confiured.
4. Use manual as default IPv6 policy, but that just hide the problem.
Hope that my information and findings helps. :)
Thanks,
Michael
------------------------------------------------------------------------------
I have problem in IPv6 PXE booting under OVMF that the process
terminated immediately right after selecting the UEFI IPv6 boot entry. I
have to change the IPv6 config policy from automatic to manual to get
things working again for me.
I looked a bit into this problem, it was terminated by the error in IaId
check
in NetworkPkg/Dhcp6Dxe/Dhcp6Impl.c::EfiDhcp6Configure function
//
// Make sure the (IaId, IaType) is unique over all the instances.
//
NET_LIST_FOR_EACH (Entry, &Service->Child) {
Other = NET_LIST_USER_STRUCT (Entry, DHCP6_INSTANCE, Link);
if (Other->IaCb.Ia != NULL &&
Other->IaCb.Ia->Descriptor.Type == Dhcp6CfgData->IaDescriptor.Type &&
Other->IaCb.Ia->Descriptor.IaId == Dhcp6CfgData->IaDescriptor.IaId
) {
return EFI_INVALID_PARAMETER;
}
}
Which would be saying clearly it was not "unique" among all the running
DHCPv6 instances. At that time there should be only two:
1. The IPv6 config instance, while the policy is set to automatic would
exchange information with server with the IaId initialized at
NetworkPkg/Ip6Dxe/Ip6ConfigImpl.c::Ip6ConfigInitInstance
Instance->IaId = NET_RANDOM (NetRandomInitSeed ());
for (Index = 0; Index < IpSb->SnpMode.HwAddressSize; Index++) {
Instance->IaId |= (IpSb->SnpMode.CurrentAddress.Addr[Index] << ((Index << 3) & 31));
}
2. The IPv6 PXE Base code driver would start another DHCPv6 session with
the IaId initialized at
NetworkPkg/UefiPxeBcDxe/PxeBcDriver.c::PxeBcCreateIp6Children
//
// Generate a random IAID for the Dhcp6 assigned address.
//
Private->IaId = NET_RANDOM (NetRandomInitSeed ());
if (Private->Snp != NULL) {
for (Index = 0; Index < Private->Snp->Mode->HwAddressSize; Index++) {
Private->IaId |= (Private->Snp->Mode->CurrentAddress.Addr[Index] << ((Index << 3) & 31));
}
}
Unfortunately both IaId collides, and quite surprisingly it's NET_RANDOM
(NetRandomInitSeed ()), or NetRandomInitSeed () generates the same
number for them.
UINT32
EFIAPI
NetRandomInitSeed (
VOID
)
{
EFI_TIME Time;
UINT32 Seed;
gRT->GetTime (&Time, NULL);
Seed = (~Time.Hour << 24 | Time.Day << 16 | Time.Minute << 8 | Time.Second);
Seed ^= Time.Nanosecond;
Seed ^= Time.Year << 7;
return Seed;
}
The returned Time.Nanosecond is always zero as the timer resolution is l
Hz for PC-AT CMOS RTC, that seems to be default real time clock driver
used by OVMF. At least from .dsc file it says so ..
PcAtChipsetPkg/PcatRealTimeClockRuntimeDxe/PcatRealTimeClockRuntimeDxe.inf
The NetRandomInitSeed is not random enough in this case, and the
NET_RANDOM marco is even more confusing as it's not a random number
generator either (so what is good for always feeding random "seed" to a
deterministic mathematic fucntion ??)
I don't have good idea how to fix the problem, but here are of my thoughs.
1. Use real timer clock driver which could provide higher resolution for
time service. Qemu is able to provide HPET timer but I don't know how to
configure OVMF to use it.
2. Use GetPerformanceCounter(), but that would increase image size by
linking TimerLib.
3. The IaId has to be "unique" thus not necessarily to be random,
probably it should be derived by some properties like MAC address and an
assgined index (staring from zero) for the interface to be confiured.
4. Use manual as default IPv6 policy, but that just hide the problem.
Hope that my information and findings helps. :)
Thanks,
Michael
------------------------------------------------------------------------------