Added PnP-BIOS detection.

svn path=/trunk/; revision=4651
This commit is contained in:
Eric Kohl 2003-05-06 16:36:42 +00:00
parent e09ba35e8c
commit df18b87eab
5 changed files with 700 additions and 97 deletions

View file

@ -196,6 +196,8 @@ ARCH_OBJS = fathelp.o \
arch.o \
i386idt.o \
i386trap.o \
i386cpu.o \
i386pnp.o \
boot.o \
linux.o \
mb.o \
@ -207,7 +209,6 @@ ARCH_OBJS = fathelp.o \
i386disk.o \
portio.o \
hardware.o \
i386cpu.o \
_alloca.o # For Mingw32 builds
RTL_OBJS = print.o \

View file

@ -27,6 +27,7 @@
#include <portio.h>
#include "../../reactos/registry.h"
#include "hardware.h"
#define MILLISEC (10)
@ -37,29 +38,6 @@
#define LATCH (CLOCK_TICK_RATE / HZ)
typedef enum
{
InterfaceTypeUndefined = -1,
Internal,
Isa,
Eisa,
MicroChannel,
TurboChannel,
PCIBus,
VMEBus,
NuBus,
PCMCIABus,
CBus,
MPIBus,
MPSABus,
ProcessorInternal,
InternalPowerBus,
PNPISABus,
MaximumInterfaceType
} INTERFACE_TYPE, *PINTERFACE_TYPE;
typedef U64 PHYSICAL_ADDRESS;
typedef struct _CM_INT13_DRIVE_PARAMETER
@ -81,70 +59,35 @@ typedef struct _CM_DISK_GEOMETRY_DEVICE_DATA
} CM_DISK_GEOMETRY_DEVICE_DATA, *PCM_DISK_GEOMETRY_DEVICE_DATA;
typedef struct
{
U8 Type;
U8 ShareDisposition;
U16 Flags;
union
{
struct
{
PHYSICAL_ADDRESS Start;
U32 Length;
} __attribute__((packed)) Port;
struct
{
U32 Level;
U32 Vector;
U32 Affinity;
} __attribute__((packed)) Interrupt;
struct
{
PHYSICAL_ADDRESS Start;
U32 Length;
} __attribute__((packed)) Memory;
struct
{
U32 Channel;
U32 Port;
U32 Reserved1;
} __attribute__((packed)) Dma;
struct
{
U32 DataSize;
U32 Reserved1;
U32 Reserved2;
} __attribute__((packed)) DeviceSpecificData;
} __attribute__((packed)) u;
} __attribute__((packed)) CM_PARTIAL_RESOURCE_DESCRIPTOR, *PCM_PARTIAL_RESOURCE_DESCRIPTOR;
typedef struct
{
U16 Version;
U16 Revision;
U32 Count;
CM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptors[1];
} __attribute__((packed))CM_PARTIAL_RESOURCE_LIST, *PCM_PARTIAL_RESOURCE_LIST;
typedef struct
{
INTERFACE_TYPE InterfaceType;
U32 BusNumber;
CM_PARTIAL_RESOURCE_LIST PartialResourceList;
} __attribute__((packed)) CM_FULL_RESOURCE_DESCRIPTOR, *PCM_FULL_RESOURCE_DESCRIPTOR;
typedef struct _DEVICE_NODE
typedef struct _CM_PNP_BIOS_DEVICE_NODE
{
U16 Size;
U8 Node;
U8 ProductId[4];
U32 ProductId;
U8 DeviceType[3];
U16 DeviceAttributes;
} PACKED DEVICE_NODE, *PDEVICE_NODE;
} __attribute__((packed)) CM_PNP_BIOS_DEVICE_NODE, *PCM_PNP_BIOS_DEVICE_NODE;
typedef struct _CM_PNP_BIOS_INSTALLATION_CHECK
{
U8 Signature[4];
U8 Revision;
U8 Length;
U16 ControlField;
U8 Checksum;
U32 EventFlagAddress;
U16 RealModeEntryOffset;
U16 RealModeEntrySegment;
U16 ProtectedModeEntryOffset;
U32 ProtectedModeCodeBaseAddress;
U32 OemDeviceId;
U16 RealModeDataBaseAddress;
U32 ProtectedModeDataBaseAddress;
} __attribute__((packed)) CM_PNP_BIOS_INSTALLATION_CHECK, *PCM_PNP_BIOS_INSTALLATION_CHECK;
typedef struct _MPS_CONFIG_TABLE_HEADER
{
@ -163,6 +106,7 @@ typedef struct _MPS_CONFIG_TABLE_HEADER
U8 Reserved;
} PACKED MPS_CONFIG_TABLE_HEADER, *PMPS_CONFIG_TABLE_HEADER;
typedef struct _MPS_PROCESSOR_ENTRY
{
U8 EntryType;
@ -175,20 +119,10 @@ typedef struct _MPS_PROCESSOR_ENTRY
U32 Reserved2;
} PACKED MPS_PROCESSOR_ENTRY, *PMPS_PROCESSOR_ENTRY;
static char Hex[] = "0123456789ABCDEF";
static unsigned int delay_count = 1;
/* PROTOTYPES ***************************************************************/
/* i386cpu.S */
U32 CpuidSupported(VOID);
VOID GetCpuid(U32 Level, U32 *eax, U32 *ebx, U32 *ecx, U32 *edx);
U32 MpsSupported(VOID);
U32 MpsGetDefaultConfiguration(VOID);
U32 MpsGetConfigurationTable(PVOID ConfigTable);
/* FUNCTIONS ****************************************************************/
@ -299,6 +233,184 @@ HalpCalibrateStallExecution(VOID)
}
VOID
SetComponentInformation(HKEY ComponentKey,
U32 Flags,
U32 Key,
U32 Affinity)
{
CM_COMPONENT_INFORMATION CompInfo;
S32 Error;
CompInfo.Flags = Flags;
CompInfo.Version = 0;
CompInfo.Key = Key;
CompInfo.Affinity = Affinity;
/* Set 'Component Information' value */
Error = RegSetValue(ComponentKey,
"Component Information",
REG_BINARY,
(PU8)&CompInfo,
sizeof(CM_COMPONENT_INFORMATION));
if (Error != ERROR_SUCCESS)
{
DbgPrint((DPRINT_HWDETECT, "RegSetValue() failed (Error %u)\n", (int)Error));
}
}
static VOID
DetectPnpBios(HKEY SystemKey, U32 *BusNumber)
{
PCM_FULL_RESOURCE_DESCRIPTOR FullResourceDescriptor;
PCM_PNP_BIOS_DEVICE_NODE DeviceNode;
char Buffer[80];
HKEY BusKey;
U32 x;
U32 NodeSize = 0;
U32 NodeCount = 0;
U8 NodeNumber;
U32 FoundNodeCount;
int i;
U32 PnpBufferSize;
U32 Size;
char *Ptr;
S32 Error;
x = PnpBiosSupported((PVOID)DISKREADBUFFER);
if (!x)
{
printf(" PnP-BIOS not supported\n");
return;
}
x = PnpBiosGetDeviceNodeCount(&NodeSize, &NodeCount);
if (x != 0 || NodeSize == 0 || NodeCount == 0)
{
DbgPrint((DPRINT_HWDETECT, "PnP-BIOS failed to enumerate device nodes\n");
return;
}
DbgPrint((DPRINT_HWDETECT,
"PnP-BIOS supported: found %u device nodes\n",
(unsigned int)NodeCount));
FoundNodeCount = 0;
PnpBufferSize = 0;
for (i = 0; i < 0x255; i++)
{
NodeNumber = (U8)i;
x = PnpBiosGetDeviceNode(&NodeNumber, (PVOID)DISKREADBUFFER);
if (x == 0)
{
DeviceNode = (PCM_PNP_BIOS_DEVICE_NODE)DISKREADBUFFER;
PnpBufferSize += DeviceNode->Size;
FoundNodeCount++;
if (FoundNodeCount >= NodeCount)
break;
}
}
PnpBufferSize += sizeof(CM_PNP_BIOS_INSTALLATION_CHECK);
DbgPrint((DPRINT_HWDETECT, "PnpBufferSize: %u\n", PnpBufferSize));
/* Create new bus key */
sprintf(Buffer,
"MultifunctionAdapter\\%u", *BusNumber);
Error = RegCreateKey(SystemKey,
Buffer,
&BusKey);
if (Error != ERROR_SUCCESS)
{
DbgPrint((DPRINT_HWDETECT, "RegCreateKey() failed (Error %u)\n", (int)Error));
return;
}
/* Increment bus number */
(*BusNumber)++;
/* Set 'Identifier' value */
Error = RegSetValue(BusKey,
"Identifier",
REG_SZ,
(PU8)"PNP BIOS",
9);
if (Error != ERROR_SUCCESS)
{
DbgPrint((DPRINT_HWDETECT, "RegSetValue() failed (Error %u)\n", (int)Error));
return;
}
/* Set 'Configuration Data' value */
Size = sizeof(CM_FULL_RESOURCE_DESCRIPTOR) + PnpBufferSize;
FullResourceDescriptor = MmAllocateMemory(Size);
if (FullResourceDescriptor == NULL)
{
DbgPrint((DPRINT_HWDETECT,
"Failed to allocate resource descriptor\n"));
return;
}
/* Initialize resource descriptor */
memset(FullResourceDescriptor, 0, Size);
FullResourceDescriptor->InterfaceType = Internal;
FullResourceDescriptor->BusNumber = 0;
FullResourceDescriptor->PartialResourceList.Count = 1;
FullResourceDescriptor->PartialResourceList.PartialDescriptors[0].Type =
CmResourceTypeDeviceSpecific;
FullResourceDescriptor->PartialResourceList.PartialDescriptors[0].ShareDisposition =
CmResourceShareUndetermined;
// FullResourceDescriptor->PartialResourceList.PartialDescriptors[0].Flags =
FullResourceDescriptor->PartialResourceList.PartialDescriptors[0].u.DeviceSpecificData.DataSize =
PnpBufferSize;
Ptr = (char *)((PVOID)FullResourceDescriptor) + sizeof(CM_FULL_RESOURCE_DESCRIPTOR);
/* Set instalation check data */
PnpBiosSupported((PVOID)DISKREADBUFFER);
memcpy (Ptr, (PVOID)DISKREADBUFFER, sizeof(CM_PNP_BIOS_INSTALLATION_CHECK));
Ptr = (char *)((PVOID)Ptr + sizeof(CM_PNP_BIOS_INSTALLATION_CHECK));
/* Copy device nodes */
for (i = 0; i < 0x255; i++)
{
NodeNumber = (U8)i;
x = PnpBiosGetDeviceNode(&NodeNumber, (PVOID)DISKREADBUFFER);
if (x == 0)
{
DeviceNode = (PCM_PNP_BIOS_DEVICE_NODE)DISKREADBUFFER;
memcpy (Ptr,
DeviceNode,
DeviceNode->Size);
Ptr = (char *)((U32)Ptr + DeviceNode->Size);
FoundNodeCount++;
if (FoundNodeCount >= NodeCount)
break;
}
}
/* Set 'Configuration Data' value */
Error = RegSetValue(BusKey,
"Configuration Data",
REG_FULL_RESOURCE_DESCRIPTOR,
(PU8) FullResourceDescriptor,
Size);
MmFreeMemory(FullResourceDescriptor);
if (Error != ERROR_SUCCESS)
{
DbgPrint((DPRINT_HWDETECT,
"RegSetValue(Configuration Data) failed (Error %u)\n",
(int)Error));
}
}
static VOID
DetectCPU(HKEY CpuKey,
HKEY FpuKey)
@ -372,7 +484,9 @@ DetectCPU(HKEY CpuKey,
FeatureSet = 0;
}
/* FIXME: Set 'Configuration Data' value (CPU and FPU) */
/* Set 'Conmponent Information' value (CPU and FPU) */
SetComponentInformation(CpuInstKey, 0, 0, 1);
SetComponentInformation(FpuInstKey, 0, 0, 1);
/* Set 'FeatureSet' value (CPU only) */
DbgPrint((DPRINT_HWDETECT, "FeatureSet: %x\n", FeatureSet));
@ -492,7 +606,16 @@ SetMpsProcessor(HKEY CpuKey,
/* Get FeatureSet */
FeatureSet = CpuEntry->FeatureFlags;
/* FIXME: Set 'Configuration Data' value (CPU and FPU) */
/* Set 'Configuration Data' value (CPU and FPU) */
SetComponentInformation(CpuInstKey,
0,
CpuEntry->LocalApicId,
1 << CpuEntry->LocalApicId);
SetComponentInformation(FpuInstKey,
0,
CpuEntry->LocalApicId,
1 << CpuEntry->LocalApicId);
/* Set 'FeatureSet' value (CPU only) */
DbgPrint((DPRINT_HWDETECT, "FeatureSet: %x\n", FeatureSet));
@ -660,7 +783,7 @@ DetectCPUs(HKEY SystemKey)
return;
}
/* Create the 'CentralProcessor' key */
/* Create the 'FloatingPointProcessor' key */
Error = RegCreateKey(SystemKey,
"FloatingPointProcessor",
&FpuKey);
@ -1044,14 +1167,14 @@ DetectHardware(VOID)
return;
}
// DetectBiosData ();
// DetectSystemData ();
DetectCPUs (SystemKey);
/* Detect buses */
// DetectPciBios (&BusNumber);
// DetectApmBios (&BusNumber);
// DetectPnpBios (&BusNumber);
DetectPnpBios (SystemKey, &BusNumber);
DetectIsaBios (SystemKey, &BusNumber);
// DetectAcpiBios (&BusNumber);

View file

@ -0,0 +1,176 @@
/*
* FreeLoader
*
* Copyright (C) 2003 Eric Kohl
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __I386_HARDWARE_H_
#define __I386_HARDWARE_H_
typedef enum
{
InterfaceTypeUndefined = -1,
Internal,
Isa,
Eisa,
MicroChannel,
TurboChannel,
PCIBus,
VMEBus,
NuBus,
PCMCIABus,
CBus,
MPIBus,
MPSABus,
ProcessorInternal,
InternalPowerBus,
PNPISABus,
MaximumInterfaceType
} INTERFACE_TYPE, *PINTERFACE_TYPE;
typedef enum _CM_RESOURCE_TYPE
{
CmResourceTypeNull = 0,
CmResourceTypePort,
CmResourceTypeInterrupt,
CmResourceTypeMemory,
CmResourceTypeDma,
CmResourceTypeDeviceSpecific,
CmResourceTypeMaximum
} CM_RESOURCE_TYPE;
typedef enum _CM_SHARE_DISPOSITION
{
CmResourceShareUndetermined = 0,
CmResourceShareDeviceExclusive,
CmResourceShareDriverExclusive,
CmResourceShareShared
} CM_SHARE_DISPOSITION;
typedef U64 PHYSICAL_ADDRESS;
typedef struct
{
U8 Type;
U8 ShareDisposition;
U16 Flags;
union
{
struct
{
PHYSICAL_ADDRESS Start;
U32 Length;
} __attribute__((packed)) Port;
struct
{
U32 Level;
U32 Vector;
U32 Affinity;
} __attribute__((packed)) Interrupt;
struct
{
PHYSICAL_ADDRESS Start;
U32 Length;
} __attribute__((packed)) Memory;
struct
{
U32 Channel;
U32 Port;
U32 Reserved1;
} __attribute__((packed)) Dma;
struct
{
U32 DataSize;
U32 Reserved1;
U32 Reserved2;
} __attribute__((packed)) DeviceSpecificData;
} __attribute__((packed)) u;
} __attribute__((packed)) CM_PARTIAL_RESOURCE_DESCRIPTOR, *PCM_PARTIAL_RESOURCE_DESCRIPTOR;
typedef struct
{
U16 Version;
U16 Revision;
U32 Count;
CM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptors[1];
} __attribute__((packed))CM_PARTIAL_RESOURCE_LIST, *PCM_PARTIAL_RESOURCE_LIST;
typedef struct
{
INTERFACE_TYPE InterfaceType;
U32 BusNumber;
CM_PARTIAL_RESOURCE_LIST PartialResourceList;
} __attribute__((packed)) CM_FULL_RESOURCE_DESCRIPTOR, *PCM_FULL_RESOURCE_DESCRIPTOR;
typedef struct _CM_COMPONENT_INFORMATION
{
U32 Flags;
U32 Version;
U32 Key;
U32 Affinity;
} __attribute__((packed)) CM_COMPONENT_INFORMATION, *PCM_COMPONENT_INFORMATION;
/* CM_COMPONENT_INFORMATION.Flags */
#define Failed 0x00000001
#define ReadOnly 0x00000002
#define Removable 0x00000004
#define ConsoleIn 0x00000008
#define ConsoleOut 0x00000010
#define Input 0x00000020
#define Output 0x00000040
/* PROTOTYPES ***************************************************************/
/* hardware.c */
VOID SetComponentInformation(HKEY ComponentKey,
U32 Flags,
U32 Key,
U32 Affinity);
/* hwcpu.c */
/* i386cpu.S */
U32 CpuidSupported(VOID);
VOID GetCpuid(U32 Level,
U32 *eax,
U32 *ebx,
U32 *ecx,
U32 *edx);
U32 MpsSupported(VOID);
U32 MpsGetDefaultConfiguration(VOID);
U32 MpsGetConfigurationTable(PVOID ConfigTable);
/* i386pnp.S */
//U32 PnpBiosSupported(VOID);
U32 PnpBiosSupported(PVOID InstallationCheck);
U32 PnpBiosGetDeviceNodeCount(U32 *NodeSize,
U32 *NodeCount);
U32 PnpBiosGetDeviceNode(U8 *NodeId,
U8 *NodeBuffer);
#endif /* __I386_HARDWARE_H_ */
/* EOF */

View file

@ -122,6 +122,8 @@ EXTERN(_GetCpuid)
*
* RETURNS:
*/
.code16
_mps_supported:
.long 0
_mps_fp_table_offset:
@ -190,6 +192,7 @@ mps_not_found:
*
* RETURNS:
*/
.code16
_mps_default_configuration:
.long 0
@ -239,6 +242,7 @@ EXTERN(_MpsGetDefaultConfiguration)
*
* RETURNS:
*/
.code16
_mps_buffer_segment:
.word 0
_mps_buffer_offset:
@ -311,7 +315,9 @@ EXTERN(_MpsGetConfigurationTable)
movw _mps_config_offset,%si
/* init DS */
.code32
movw _mps_config_segment,%ax
.code16
pushw %ds
pushw %ax
popw %ds

View file

@ -0,0 +1,297 @@
/*
* FreeLoader
* Copyright (C) 2003 Eric Kohl <ekohl@rz-online.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
.text
.code16
#define ASM
#include <arch.h>
/*
* U32 PnpBiosSupported(PVOID InstallationCheck);
*
* RETURNS:
*/
_pnp_support_result:
.long 0
_pnp_entry_point:
.word 0
_pnp_code_segment:
.word 0
_pnp_data_segment:
.word 0
EXTERN(_PnpBiosSupported)
.code32
pushal
push %es
/* convert pointer to node buffer to segment/offset */
movl 0x08(%ebp),%eax
shrl $4,%eax
andl $0xf000,%eax
movw %ax,_pnp_buffer_segment
movl 0x08(%ebp),%eax
andl $0xffff,%eax
movw %ax,_pnp_buffer_offset
call switch_to_real
.code16
/* init ES */
pushw $0xF000
popw %es
/* init cx */
movw $0xFFF0,%si
pnp_again:
movw %es:(%si),%ax
cmp $0x5024,%ax /* "$P" */
jne pnp_next
push %si
add $2,%si
movw %es:(%si),%ax
cmp $0x506E,%ax /* "nP" */
pop %si
je pnp_found
pnp_next:
cmp $0,%si
je pnp_not_found
sub $0x10,%si
jmp pnp_again
pnp_found:
movzwl %si,%eax
movl %eax,_pnp_support_result
add $0x0D,%si
movw %es:(%si),%ax
movw %ax,_pnp_entry_point
add $0x02,%si
movw %es:(%si),%ax
movw %ax,_pnp_code_segment
add $0x0C,%si
movw %es:(%si),%ax
movw %ax,_pnp_data_segment
/* copy data to buffer */
push %ds
movw %es, %ax
movw %ax, %ds
movl _pnp_support_result, %esi
movw _pnp_buffer_segment, %es
movw _pnp_buffer_offset, %di
movw $0x21, %cx
rep movsb
pop %ds
pnp_not_found:
call switch_to_prot
.code32
pop %es
popal
movl _pnp_support_result,%eax
ret
/*
* U32 PnpBiosGetDeviceNodeCount(U32 *NodeSize, U32 *NodeCount);
*
* RETURNS:
*/
_entry_point:
.long 0
_pnp_node_size:
.word 0
_pnp_node_count:
.word 0
EXTERN(_PnpBiosGetDeviceNodeCount)
.code32
pushl %ebp
movl %esp,%ebp
pushal
push %es
call switch_to_real
.code16
movw _pnp_data_segment,%ax
pushw %ax
pushw %cs
movw $(_pnp_node_size),%ax
pushw %ax
pushw %cs
movw $(_pnp_node_count),%ax
pushw %ax
pushw $0
movw _pnp_code_segment,%ax
movzwl %ax,%ecx
shll $16,%ecx
movw _pnp_entry_point,%ax
movzwl %ax,%ebx
orl %ebx,%ecx
movl %ecx,_entry_point
lcall *_entry_point
addw $12,%sp
movzwl %ax,%ecx
movl %ecx,_pnp_support_result
call switch_to_prot
.code32
movl 0x08(%ebp),%esi
movw _pnp_node_size,%ax
movzwl %ax,%ecx
movl %ecx, (%esi)
movl 0x0C(%ebp),%esi
movw _pnp_node_count,%ax
movzwl %ax,%ecx
movl %eax, (%esi)
pop %es
popal
movl %ebp,%esp
popl %ebp
movl _pnp_support_result,%eax
ret
/*
* U32 PnpBiosGetDeviceNode(U8 *NodeId, U8 *NodeBuffer);
*
* RETURNS:
*/
_pnp_buffer_segment:
.word 0
_pnp_buffer_offset:
.word 0
_pnp_node_number:
.byte 0
EXTERN(_PnpBiosGetDeviceNode)
.code32
pushl %ebp
movl %esp,%ebp
pushal
push %es
/* get current node number */
movl 0x08(%ebp),%esi
movb (%esi),%al
movb %al,_pnp_node_number
/* convert pointer to node buffer to segment/offset */
movl 0x0C(%ebp),%eax
shrl $4,%eax
andl $0xf000,%eax
movw %ax,_pnp_buffer_segment
movl 0x0C(%ebp),%eax
andl $0xffff,%eax
movw %ax,_pnp_buffer_offset
call switch_to_real
.code16
/* push bios segment */
movw _pnp_data_segment,%ax
pushw %ax
/* push control flag */
pushw $0x0001
/* push pointer to node buffer (segment/offset) */
movw _pnp_buffer_segment,%ax
pushw %ax
movw _pnp_buffer_offset,%ax
pushw %ax
/* push pointer to node number (segment/offset) */
pushw %cs
movw $(_pnp_node_number),%ax
pushw %ax
/* push function number */
pushw $1
/* call entry point */
lcall *_entry_point
addw $14,%sp
movzwl %ax,%ecx
movl %ecx,_pnp_support_result
call switch_to_prot
.code32
/* update node number */
movl 0x08(%ebp),%esi
movb _pnp_node_number,%al
movb %al,(%esi)
pop %es
popal
movl %ebp,%esp
popl %ebp
movl _pnp_support_result,%eax
ret
/* EOF */