/* $NetBSD: devpath2.c,v 1.1 2025/02/24 13:47:56 christos Exp $ */ /* * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #ifndef lint __RCSID("$NetBSD: devpath2.c,v 1.1 2025/02/24 13:47:56 christos Exp $"); #endif /* not lint */ #include #include #include #include #include "defs.h" #include "devpath.h" #include "devpath2.h" #include "utils.h" #define easprintf (size_t)easprintf /************************************************************************ * Type 2 - ACPI Device Path ************************************************************************/ /* * See 19.3.4 of ACPI Specification, Release 6.5 for compressed EISAID * algorithm. */ #define PNP0A03 0x0a0341d0 #define PNP0A08 0x0a0841d0 static const char * eisaid_to_str(uint32_t eisaid) { static const char hexdigits[] = "0123456789ABCDEF"; static char text[8]; union { uint32_t eisaid; uint8_t b[4]; } u; u.eisaid = eisaid; text[0] = (char)__SHIFTOUT(u.b[0], __BITS(4,0)); text[1] = (char)__SHIFTOUT(u.b[0], __BITS(7,5)); text[1] |= (char)__SHIFTOUT(u.b[1], __BITS(1,0)) << 3; text[2] = (char)__SHIFTOUT(u.b[1], __BITS(7,2)); text[0] += 0x40; /* '@' */ text[1] += 0x40; text[2] += 0x40; #define hi_nib(v) (((v) >> 4) & 0x0f) #define lo_nib(v) (((v) >> 0) & 0x0f) text[3] = hexdigits[hi_nib(u.b[3])]; text[4] = hexdigits[lo_nib(u.b[3])]; text[5] = hexdigits[hi_nib(u.b[2])]; text[6] = hexdigits[lo_nib(u.b[2])]; text[7] = 0; #undef lo_nib #undef hi_nib return text; } static inline const char * devpath_acpi_acpi_eisaid(uint32_t eisaid) { #define PNP(v) (0x000041d0 | ((v) << 16)) switch (eisaid) { // case PNP(0x0f03): return "PS2Mouse"; /* legacy? */ // case PNP(0x0303): return "FloppyPNP"; /* legacy? */ case PNP(0x0a03): return "PciRoot"; case PNP(0x0a08): return "PcieRoot"; case PNP(0x0604): return "Floppy"; case PNP(0x0301): return "Keyboard"; case PNP(0x0501): return "Serial"; case PNP(0x0401): return "ParallelPort"; default: return NULL; } #undef PNP } static inline void devpath_acpi_acpi(devpath_t *dp, devpath_elm_t *path, devpath_elm_t *dbg) { /* See 10.3.3 */ struct { /* Sub-Type 1 */ devpath_t hdr; /* Length = 12 */ uint32_t _HID; uint32_t _UID; } __packed *p = (void *)dp; __CTASSERT(sizeof(*p) == 12); const char *str; str = devpath_acpi_acpi_eisaid(p->_HID); if (str == NULL) path->sz = easprintf(&path->cp, "ACPI(%s,0x%x)", eisaid_to_str(p->_HID), p->_UID); else path->sz = easprintf(&path->cp, "%s(0x%x)", str, p->_UID); if (dbg != NULL) { dbg->sz = easprintf(&dbg->cp, DEVPATH_FMT_HDR DEVPATH_FMT(_HID: 0x%08x(%s)\n) DEVPATH_FMT(_UID: 0x%08x\n), DEVPATH_DAT_HDR(dp), p->_HID, eisaid_to_str(p->_HID), p->_UID); } } static inline void devpath_acpi_acpiex(devpath_t *dp, devpath_elm_t *path, devpath_elm_t *dbg) { /* See 10.3.3 */ struct { /* Sub-Type 2 */ devpath_t hdr; /* Length = 19 + n */ uint32_t _HID; uint32_t _UID; uint32_t _CID; char _HIDSTR[]; /* at least NUL byte */ // char _UIDSTR[]; /* at least NUL byte */ // char _CIDSTR[]; /* at least NUL byte */ } __packed *p = (void *)dp; __CTASSERT(sizeof(*p) == 16); char *hidstr = p->_HIDSTR; char *uidstr = hidstr + strlen(hidstr) + 1; char *cidstr = uidstr + strlen(uidstr) + 1; #if 0 There are 4 different subsubtypes depending on the ?ID, ?IDSTR values. "AcpiEx" "AcpiExp" "PciRoot" "PcipRoot" #endif if (p->_HID == PNP0A08 || p->_CID == PNP0A08) { // PcieRoot(UID|UIDSTR) path->sz = easprintf(&path->cp, "PcieRoot(%s)", *uidstr != '\0' ? uidstr : eisaid_to_str(p->_UID)); } else if (p->_HID == PNP0A03 || p->_CID == PNP0A03) { assert(p->_HID != PNP0A08); // PciRoot(UID|UIDSTR) path->sz = easprintf(&path->cp, "PciRoot(%s)", *uidstr != '\0' ? uidstr : eisaid_to_str(p->_UID)); } else if (hidstr[0] == '\0' && cidstr[0] == '\0' && uidstr[0] != '\0') { assert(p->_HID != 0); path->sz = easprintf(&path->cp, "AcpiExp(%s,%s,%s)", eisaid_to_str(p->_HID), eisaid_to_str(p->_CID), uidstr); } else { path->sz = easprintf(&path->cp, "ACPIEX(%s,%s,0x%x,%s,%s,%s)", eisaid_to_str(p->_HID), eisaid_to_str(p->_CID), p->_UID, hidstr, cidstr, uidstr); } if (dbg != NULL) { dbg->sz = easprintf(&dbg->cp, DEVPATH_FMT_HDR DEVPATH_FMT(_HID: 0x%08x(%s)\n) DEVPATH_FMT(_UID: 0x%08x\n) DEVPATH_FMT(_CID: 0x%08x(%s)\n) DEVPATH_FMT(_HIDSTR: %s\n) DEVPATH_FMT(_UIDSTR: %s\n) DEVPATH_FMT(_CIDSTR: %s\n), DEVPATH_DAT_HDR(dp), p->_HID, eisaid_to_str(p->_HID), p->_UID, p->_CID, eisaid_to_str(p->_CID), hidstr, uidstr, cidstr); } } static inline void devpath_acpi_adr(devpath_t *dp, devpath_elm_t *path, devpath_elm_t *dbg) { /* See 10.3.3.1 */ struct { /* Sub-Type 3 */ devpath_t hdr; /* Length = 8; at least one _ADR */ uint32_t _ADR[]; } __packed *p = (void *)dp; __CTASSERT(sizeof(*p) == 4); char *tp; size_t cnt; cnt = (p->hdr.Length - sizeof(p->hdr)) / sizeof(p->_ADR[0]); tp = estrdup("AcpiAdr("); for (size_t i = 0; i < cnt; i++) { path->sz = easprintf(&path->cp, "%s0x%08x%s", tp, p->_ADR[i], i + 1 < cnt ? "," : ""); free(tp); tp = path->cp; } path->sz = easprintf(&path->cp, "%s)", tp); free(tp); if (dbg != NULL) { dbg->sz = easprintf(&dbg->cp, DEVPATH_FMT_HDR, DEVPATH_DAT_HDR(dp)); tp = dbg->cp; for (size_t i = 0; i < cnt; i++) { dbg->sz = easprintf(&dbg->cp, "%s" DEVPATH_FMT(_ADR[%zu]: 0x%08x\n), tp, i, p->_ADR[i]); free(tp); tp = dbg->cp; } } } static inline void devpath_acpi_nvdimm(devpath_t *dp, devpath_elm_t *path, devpath_elm_t *dbg) { /* See 10.3.3.2 */ struct { /* Sub-Type 4 */ devpath_t hdr; /* Length = 8 */ uint32_t NFIT_DevHdl; } __packed *p = (void *)dp; __CTASSERT(sizeof(*p) == 8); path->sz = easprintf(&path->cp, "NvdimmAcpiAdr(0x%x)", p->NFIT_DevHdl); if (dbg != NULL) { dbg->sz = easprintf(&dbg->cp, DEVPATH_FMT_HDR DEVPATH_FMT(NFIT_DevHdl: 0x%08x\n), DEVPATH_DAT_HDR(dp), p->NFIT_DevHdl); } } static inline void devpath_acpi_unknown(devpath_t *dp, devpath_elm_t *path, devpath_elm_t *dbg) { path->sz = easprintf(&path->cp, "Msg(%d,%s)", dp->SubType, encode_data(dp->Data, dp->Length - 4)); if (dbg != NULL) { dbg->sz = easprintf(&dbg->cp, DEVPATH_FMT_HDR, DEVPATH_DAT_HDR(dp)); } } PUBLIC void devpath_acpi(devpath_t *dp, devpath_elm_t *path, devpath_elm_t *dbg) { assert(dp->Type = 2); switch (dp->SubType) { case 1: devpath_acpi_acpi(dp, path, dbg); return; case 2: devpath_acpi_acpiex(dp, path, dbg); return; case 3: devpath_acpi_adr(dp, path, dbg); return; case 4: devpath_acpi_nvdimm(dp, path, dbg); return; default: devpath_unsupported(dp, path, dbg); return; } }