2 * The PCI Library -- Initialization and related things
4 * Copyright (c) 1997--2024 Martin Mares <mj@ucw.cz>
6 * Can be freely distributed and used under the terms of the GNU GPL v2+.
8 * SPDX-License-Identifier: GPL-2.0-or-later
19 #include <crt0.h> /* for __dos_argv0 */
26 /* Force usage of ANSI (char*) variant of GetModuleFileName() function */
28 #ifdef GetModuleFileName
29 #undef GetModuleFileName
31 #define GetModuleFileName GetModuleFileNameA
34 /* Define __ImageBase for all linkers */
36 /* GNU LD provides __ImageBase symbol since 2.19, in previous versions it is
37 * under name _image_base__, so add weak alias for compatibility. */
39 asm(".weak\t" PCI_STRINGIFY(__MINGW_USYMBOL(__ImageBase
)) "\n\t"
40 ".set\t" PCI_STRINGIFY(__MINGW_USYMBOL(__ImageBase
)) "," PCI_STRINGIFY(__MINGW_USYMBOL(_image_base__
)));
43 * MSVC link.exe provides __ImageBase symbol since 12.00 (MSVC 6.0), for
44 * previous versions resolve it at runtime via GetModuleHandleA() which
45 * returns base for main executable or via VirtualQuery() for DLL builds.
47 #if defined(_MSC_VER) && _MSC_VER < 1200
49 get_current_module_handle(void)
52 MEMORY_BASIC_INFORMATION info
;
53 size_t len
= VirtualQuery(&get_current_module_handle
, &info
, sizeof(info
));
54 if (len
!= sizeof(info
))
56 return (HMODULE
)info
.AllocationBase
;
58 return GetModuleHandleA(NULL
);
61 #define __ImageBase (*(IMAGE_DOS_HEADER *)get_current_module_handle())
63 extern IMAGE_DOS_HEADER __ImageBase
;
68 extern HINSTANCE _hModule
;
69 #elif defined(_WINDOWS)
70 extern HINSTANCE _hInstance
;
75 static struct pci_methods
*pci_methods
[PCI_ACCESS_MAX
] = {
77 #ifdef PCI_HAVE_PM_LINUX_SYSFS
82 #ifdef PCI_HAVE_PM_LINUX_PROC
87 #ifdef PCI_HAVE_PM_INTEL_CONF
94 #ifdef PCI_HAVE_PM_FBSD_DEVICE
99 #ifdef PCI_HAVE_PM_AIX_DEVICE
104 #ifdef PCI_HAVE_PM_NBSD_LIBPCI
109 #ifdef PCI_HAVE_PM_OBSD_DEVICE
114 #ifdef PCI_HAVE_PM_DUMP
119 #ifdef PCI_HAVE_PM_DARWIN_DEVICE
124 #ifdef PCI_HAVE_PM_SYLIXOS_DEVICE
129 #ifdef PCI_HAVE_PM_HURD_CONF
134 #ifdef PCI_HAVE_PM_WIN32_CFGMGR32
139 #ifdef PCI_HAVE_PM_WIN32_KLDBG
144 #ifdef PCI_HAVE_PM_WIN32_SYSDBG
149 #ifdef PCI_HAVE_PM_MMIO_CONF
156 #if defined(PCI_HAVE_PM_ECAM)
161 #if defined(PCI_HAVE_PM_AOS_EXPANSION)
166 #ifdef PCI_HAVE_PM_RT_THREAD_SMART_DM
167 &pm_rt_thread_smart_dm
,
173 // If PCI_ACCESS_AUTO is selected, we probe the access methods in this order
174 static int probe_sequence
[] = {
175 // System-specific methods
176 PCI_ACCESS_SYS_BUS_PCI
,
177 PCI_ACCESS_PROC_BUS_PCI
,
178 PCI_ACCESS_FBSD_DEVICE
,
179 PCI_ACCESS_AIX_DEVICE
,
180 PCI_ACCESS_NBSD_LIBPCI
,
181 PCI_ACCESS_OBSD_DEVICE
,
183 PCI_ACCESS_SYLIXOS_DEVICE
,
185 PCI_ACCESS_WIN32_CFGMGR32
,
186 PCI_ACCESS_WIN32_KLDBG
,
187 PCI_ACCESS_WIN32_SYSDBG
,
188 PCI_ACCESS_AOS_EXPANSION
,
189 PCI_ACCESS_RT_THREAD_SMART_DM
,
190 // Low-level methods poking the hardware directly
192 PCI_ACCESS_I386_TYPE1
,
193 PCI_ACCESS_I386_TYPE2
,
194 PCI_ACCESS_MMIO_TYPE1_EXT
,
195 PCI_ACCESS_MMIO_TYPE1
,
199 static void PCI_NONRET
200 pci_generic_error(char *msg
, ...)
205 fputs("pcilib: ", stderr
);
206 vfprintf(stderr
, msg
, args
);
213 pci_generic_warn(char *msg
, ...)
218 fputs("pcilib: ", stderr
);
219 vfprintf(stderr
, msg
, args
);
225 pci_generic_debug(char *msg
, ...)
230 vfprintf(stdout
, msg
, args
);
235 pci_null_debug(char *msg UNUSED
, ...)
239 // Memory allocation functions are safe to call if pci_access is not fully initalized or even NULL
242 pci_malloc(struct pci_access
*a
, int size
)
244 void *x
= malloc(size
);
247 (a
&& a
->error
? a
->error
: pci_generic_error
)("Out of memory (allocation of %d bytes failed)", size
);
259 pci_strdup(struct pci_access
*a
, const char *s
)
261 int len
= strlen(s
) + 1;
262 char *t
= pci_malloc(a
, len
);
268 pci_lookup_method(char *name
)
272 for (i
=0; i
<PCI_ACCESS_MAX
; i
++)
273 if (pci_methods
[i
] && !strcmp(pci_methods
[i
]->name
, name
))
279 pci_get_method_name(int index
)
281 if (index
< 0 || index
>= PCI_ACCESS_MAX
)
283 else if (!pci_methods
[index
])
286 return pci_methods
[index
]->name
;
289 #if defined(PCI_OS_WINDOWS) || defined(PCI_OS_DJGPP)
292 pci_init_name_list_path(struct pci_access
*a
)
294 if ((PCI_PATH_IDS_DIR
)[0])
295 pci_set_name_list_path(a
, PCI_PATH_IDS_DIR
"\\" PCI_IDS
, 0);
301 #if defined(PCI_OS_WINDOWS) && (defined(_WIN32) || defined(_WINDLL) || defined(_WINDOWS))
307 module
= (HINSTANCE
)&__ImageBase
;
308 #elif defined(_WINDLL)
310 #elif defined(_WINDOWS)
315 * Module file name can have arbitrary length despite all MS examples say
316 * about MAX_PATH upper limit. This limit does not apply for example when
317 * executable is running from network disk with very long UNC paths or
318 * when using "\\??\\" prefix for specifying executable binary path.
319 * Function GetModuleFileName() returns passed size argument when passed
320 * buffer is too small and does not signal any error. In this case retry
321 * again with larger buffer.
323 size
= 256; /* initial buffer size (more than sizeof(PCI_IDS)-4) */
325 path
= pci_malloc(a
, size
);
326 len
= GetModuleFileName(module
, path
, size
-sizeof(PCI_IDS
)-4); /* 4 for "\\\\?\\" */
327 if (len
>= size
-sizeof(PCI_IDS
)-4)
337 * GetModuleFileName() has bugs. On Windows 10 it prepends current drive
338 * letter if path is just pure NT namespace (with "\\??\\" prefix). Such
339 * extra drive letter makes path fully invalid and unusable. So remove
340 * extra drive letter to make path valid again.
341 * Reproduce: CreateProcessW("\\??\\C:\\lspci.exe", ...)
343 if (((path
[0] >= 'a' && path
[0] <= 'z') ||
344 (path
[0] >= 'A' && path
[0] <= 'Z')) &&
345 strncmp(path
+1, ":\\??\\", 5) == 0)
347 memmove(path
, path
+2, len
-2);
353 * GetModuleFileName() has bugs. On Windows 10 it does not add "\\\\?\\"
354 * prefix when path is in native NT UNC namespace. Such path is treated by
355 * WinAPI/DOS functions as standard DOS path relative to the current
356 * directory, hence something completely different. So prepend missing
357 * "\\\\?\\" prefix to make path valid again.
358 * Reproduce: CreateProcessW("\\??\\UNC\\10.0.2.4\\qemu\\lspci.exe", ...)
360 * If path starts with DOS drive letter and with appended PCI_IDS is
361 * longer than 260 bytes and is without "\\\\?\\" prefix then append it.
362 * This prefix is required for paths and file names with DOS drive letter
363 * longer than 260 bytes.
365 if (strncmp(path
, "\\UNC\\", 5) == 0 ||
366 strncmp(path
, "UNC\\", 4) == 0 ||
367 (((path
[0] >= 'a' && path
[0] <= 'z') || (path
[0] >= 'A' && path
[0] <= 'Z')) &&
368 len
+ sizeof(PCI_IDS
) >= 260))
370 memmove(path
+4, path
, len
);
371 memcpy(path
, "\\\\?\\", 4);
376 #elif defined(PCI_OS_DJGPP) || defined(PCI_OS_WINDOWS)
378 const char *exe_path
;
381 exe_path
= __dos_argv0
;
386 len
= strlen(exe_path
);
387 path
= pci_malloc(a
, len
+sizeof(PCI_IDS
));
388 memcpy(path
, exe_path
, len
+1);
392 sep
= strrchr(path
, '\\');
396 * If current module path (current executable for static builds or
397 * current DLL library for shared build) cannot be determined then
398 * fallback to the current directory.
401 pci_set_name_list_path(a
, PCI_IDS
, 0);
405 memcpy(sep
+1, PCI_IDS
, sizeof(PCI_IDS
));
406 pci_set_name_list_path(a
, path
, 1);
411 #elif defined PCI_OS_AMIGAOS
414 pci_init_name_list_path(struct pci_access
*a
)
416 int len
= strlen(PCI_PATH_IDS_DIR
);
419 pci_set_name_list_path(a
, PCI_IDS
, 0);
422 char last_char
= PCI_PATH_IDS_DIR
[len
- 1];
423 if (last_char
== ':' || last_char
== '/') // root or parent char
424 pci_set_name_list_path(a
, PCI_PATH_IDS_DIR PCI_IDS
, 0);
426 pci_set_name_list_path(a
, PCI_PATH_IDS_DIR
"/" PCI_IDS
, 0);
433 pci_init_name_list_path(struct pci_access
*a
)
435 pci_set_name_list_path(a
, PCI_PATH_IDS_DIR
"/" PCI_IDS
, 0);
443 pci_init_dns(struct pci_access
*a
)
445 pci_define_param(a
, "net.domain", PCI_ID_DOMAIN
, "DNS domain used for resolving of ID's");
446 a
->id_lookup_mode
= PCI_LOOKUP_CACHE
;
448 char *cache_dir
= getenv("XDG_CACHE_HOME");
450 cache_dir
= "~/.cache";
452 int name_len
= strlen(cache_dir
) + 32;
453 char *cache_name
= pci_malloc(NULL
, name_len
);
454 snprintf(cache_name
, name_len
, "%s/pci-ids", cache_dir
);
455 struct pci_param
*param
= pci_define_param(a
, "net.cache_name", cache_name
, "Name of the ID cache file");
456 param
->value_malloced
= 1;
464 struct pci_access
*a
= pci_malloc(NULL
, sizeof(struct pci_access
));
467 memset(a
, 0, sizeof(*a
));
468 pci_init_name_list_path(a
);
473 pci_define_param(a
, "hwdb.disable", "0", "Do not look up names in UDEV's HWDB if non-zero");
475 for (i
=0; i
<PCI_ACCESS_MAX
; i
++)
476 if (pci_methods
[i
] && pci_methods
[i
]->config
)
477 pci_methods
[i
]->config(a
);
482 pci_init_internal(struct pci_access
*a
, int skip_method
)
485 a
->error
= pci_generic_error
;
487 a
->warning
= pci_generic_warn
;
489 a
->debug
= pci_generic_debug
;
491 a
->debug
= pci_null_debug
;
493 if (a
->method
!= PCI_ACCESS_AUTO
)
495 if (a
->method
>= PCI_ACCESS_MAX
|| !pci_methods
[a
->method
])
496 a
->error("This access method is not supported.");
497 a
->methods
= pci_methods
[a
->method
];
502 for (i
=0; probe_sequence
[i
] >= 0; i
++)
504 struct pci_methods
*m
= pci_methods
[probe_sequence
[i
]];
507 if (skip_method
== probe_sequence
[i
])
509 a
->debug("Trying method %s...", m
->name
);
514 a
->method
= probe_sequence
[i
];
517 a
->debug("...No.\n");
522 a
->debug("Decided to use %s\n", a
->methods
->name
);
528 pci_init_v35(struct pci_access
*a
)
530 if (!pci_init_internal(a
, -1))
531 a
->error("Cannot find any working access method.");
534 STATIC_ALIAS(void pci_init(struct pci_access
*a
), pci_init_v35(a
));
535 DEFINE_ALIAS(void pci_init_v30(struct pci_access
*a
), pci_init_v35
);
536 SYMBOL_VERSION(pci_init_v30
, pci_init@LIBPCI_3
.0
);
537 SYMBOL_VERSION(pci_init_v35
, pci_init@@LIBPCI_3
.5
);
540 pci_clone_access(struct pci_access
*a
)
542 struct pci_access
*b
= pci_alloc();
544 b
->writeable
= a
->writeable
;
545 b
->buscentric
= a
->buscentric
;
546 b
->debugging
= a
->debugging
;
548 b
->warning
= a
->warning
;
555 pci_cleanup(struct pci_access
*a
)
557 struct pci_dev
*d
, *e
;
559 for (d
=a
->devices
; d
; d
=e
)
565 a
->methods
->cleanup(a
);
566 pci_free_name_list(a
);
568 pci_set_name_list_path(a
, NULL
, 0);