root/sbin/efi/devpath.c
/* $NetBSD: devpath.c,v 1.2 2026/07/04 18:27:17 kre 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 <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: devpath.c,v 1.2 2026/07/04 18:27:17 kre Exp $");
#endif /* not lint */

#include <sys/queue.h>

#include <err.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <util.h>

#include "defs.h"
#include "devpath.h"
#include "devpath1.h"
#include "devpath2.h"
#include "devpath3.h"
#include "devpath4.h"
#include "devpath5.h"

#define easprintf       (size_t)easprintf

typedef SIMPLEQ_HEAD(devpath_head, devpath_blk) devpath_head_t;

typedef struct devpath_blk {
        devpath_elm_t   path;
        devpath_elm_t   dbg;
        SIMPLEQ_ENTRY(devpath_blk) entry;
} devpath_blk_t;

static void
devpath_end(devpath_t *dp, devpath_elm_t *path, devpath_elm_t *dbg)
{

        assert(dp->Type == 0x7f);
        assert(dp->Length == 4);

        switch (dp->SubType) {
        case 1:
                path->cp = estrdup(""); /* end of devpath instance */
                path->sz = 1;
                break;
        case 0xff:
                path->cp = NULL;        /* end of entire devpath */
                path->sz = 0;
                break;
        default:
                path->sz = easprintf(&path->cp,
                    "unknown device path end subtype: %u\n", dp->SubType);
                break;
        }

        if (dbg != NULL)
                devpath_hdr(dp, dbg);
}

static char *
collapse_list(devpath_head_t *head, size_t plen, char **dmsg, size_t dlen)
{
        devpath_blk_t *blk, *next;
        char *bp, *path;

        bp = path = emalloc(plen + 1);
        SIMPLEQ_FOREACH_SAFE(blk, head, entry, next) {
                if (blk->path.cp == NULL) {
                        if (next == NULL) {
                                *bp = '\0';
                                break;
                        }
                        // *bp++=';';   /* just nothing should be OK, or ??? */
                        continue;
                }
                else if (*blk->path.cp == '\0') {
                        *bp++ = ':';
                        next = SIMPLEQ_NEXT(blk, entry);
                }
                else {
                        bp = stpcpy(bp, blk->path.cp);
                        if (next->path.cp != NULL && *next->path.cp != '\0')
                                *bp++ = '/';
                }
                free(blk->path.cp);
        }
        if (dmsg) {
                bp = *dmsg = emalloc(dlen + 1);
                SIMPLEQ_FOREACH_SAFE(blk, head, entry, next) {
                        bp = stpcpy(bp, blk->dbg.cp);
                        free(blk->dbg.cp);
                }
        }
        return path;
}

static void
devpath_parse_core(devpath_t *dp, devpath_elm_t *path, devpath_elm_t *dbg)
{

        switch (dp->Type) {
        case DEVPATH_TYPE_HW:    devpath_hw(dp, path, dbg);     return; /* Type 1 */
        case DEVPATH_TYPE_ACPI:  devpath_acpi(dp, path, dbg);   return; /* Type 2 */
        case DEVPATH_TYPE_MSG:   devpath_msg(dp, path, dbg);    return; /* Type 3 */
        case DEVPATH_TYPE_MEDIA: devpath_media(dp, path, dbg);  return; /* Type 4 */
        case DEVPATH_TYPE_BIOS:  devpath_bios(dp, path, dbg);   return; /* Type 5 */
        case DEVPATH_TYPE_END:   devpath_end(dp, path, dbg);    return; /* Type 0x7F */
        default:                 devpath_unsupported(dp, path, dbg);    return;
        }
}

PUBLIC char *
devpath_parse(devpath_t *dp, size_t dplen, char **dmsg)
{
        devpath_head_t head = SIMPLEQ_HEAD_INITIALIZER(head);
        devpath_blk_t *blk;
        union {
                char *cp;
                devpath_t *dp;
        } u;
        size_t dlen = 0, plen = 0;
        char *ep;

        if (dmsg)
                *dmsg = NULL;

        u.dp = dp;
        ep = u.cp + dplen;
        for (/*EMPTY*/; u.cp < ep; u.cp += u.dp->Length) {
                blk = ecalloc(1, sizeof(*blk));
                devpath_parse_core(u.dp, &blk->path, dmsg ? &blk->dbg : NULL);
                plen += blk->path.sz;
                dlen += blk->dbg.sz;
                SIMPLEQ_INSERT_TAIL(&head, blk, entry);
        }

        return collapse_list(&head, plen, dmsg, dlen);
}