/*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2023-2025 Ruslan Bukin * * This work was supported by Innovate UK project 105694, "Digital Security * by Design (DSbD) Technology Platform Prototype". * * 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 AND CONTRIBUTORS ``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 #include #include #include #include #include #include #include /* linker_hwpmc_list_objects */ #include #include #include #include #include #include #include #include #define HWT_RECORD_DEBUG #undef HWT_RECORD_DEBUG #ifdef HWT_RECORD_DEBUG #define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__) #else #define dprintf(fmt, ...) #endif static MALLOC_DEFINE(M_HWT_RECORD, "hwt_record", "Hardware Trace"); static uma_zone_t record_zone = NULL; static struct hwt_record_entry * hwt_record_clone(struct hwt_record_entry *ent, int flags) { struct hwt_record_entry *entry; entry = uma_zalloc(record_zone, flags); if (entry == NULL) return (NULL); memcpy(entry, ent, sizeof(struct hwt_record_entry)); switch (ent->record_type) { case HWT_RECORD_MMAP: case HWT_RECORD_EXECUTABLE: case HWT_RECORD_KERNEL: entry->fullpath = strdup(ent->fullpath, M_HWT_RECORD); break; default: break; } return (entry); } static void hwt_record_to_user(struct hwt_record_entry *ent, struct hwt_record_user_entry *usr) { usr->record_type = ent->record_type; switch (ent->record_type) { case HWT_RECORD_MMAP: case HWT_RECORD_EXECUTABLE: case HWT_RECORD_KERNEL: usr->addr = ent->addr; usr->baseaddr = ent->baseaddr; strncpy(usr->fullpath, ent->fullpath, MAXPATHLEN); break; case HWT_RECORD_BUFFER: usr->buf_id = ent->buf_id; usr->curpage = ent->curpage; usr->offset = ent->offset; break; case HWT_RECORD_THREAD_CREATE: case HWT_RECORD_THREAD_SET_NAME: usr->thread_id = ent->thread_id; break; default: break; } } void hwt_record_load(void) { record_zone = uma_zcreate("HWT records", sizeof(struct hwt_record_entry), NULL, NULL, NULL, NULL, 0, 0); } void hwt_record_unload(void) { uma_zdestroy(record_zone); } void hwt_record_ctx(struct hwt_context *ctx, struct hwt_record_entry *ent, int flags) { struct hwt_record_entry *entry; KASSERT(ent != NULL, ("ent is NULL")); entry = hwt_record_clone(ent, flags); if (entry == NULL) { /* XXX: Not sure what to do here other than logging an error. */ return; } HWT_CTX_LOCK(ctx); TAILQ_INSERT_TAIL(&ctx->records, entry, next); HWT_CTX_UNLOCK(ctx); hwt_record_wakeup(ctx); } void hwt_record_td(struct thread *td, struct hwt_record_entry *ent, int flags) { struct hwt_record_entry *entry; struct hwt_context *ctx; struct proc *p; p = td->td_proc; KASSERT(ent != NULL, ("ent is NULL")); entry = hwt_record_clone(ent, flags); if (entry == NULL) { /* XXX: Not sure what to do here other than logging an error. */ return; } ctx = hwt_contexthash_lookup(p); if (ctx == NULL) { hwt_record_entry_free(entry); return; } HWT_CTX_LOCK(ctx); TAILQ_INSERT_TAIL(&ctx->records, entry, next); HWT_CTX_UNLOCK(ctx); hwt_record_wakeup(ctx); hwt_ctx_put(ctx); } struct hwt_record_entry * hwt_record_entry_alloc(void) { return (uma_zalloc(record_zone, M_WAITOK | M_ZERO)); } void hwt_record_entry_free(struct hwt_record_entry *entry) { switch (entry->record_type) { case HWT_RECORD_MMAP: case HWT_RECORD_EXECUTABLE: case HWT_RECORD_KERNEL: free(entry->fullpath, M_HWT_RECORD); break; default: break; } uma_zfree(record_zone, entry); } static int hwt_record_grab(struct hwt_context *ctx, struct hwt_record_user_entry *user_entry, int nitems_req, int wait) { struct hwt_record_entry *entry; int i; if (wait) { mtx_lock(&ctx->rec_mtx); if (TAILQ_FIRST(&ctx->records) == NULL) { /* Wait until we have new records. */ msleep(ctx, &ctx->rec_mtx, PCATCH, "recsnd", 0); } mtx_unlock(&ctx->rec_mtx); } for (i = 0; i < nitems_req; i++) { HWT_CTX_LOCK(ctx); entry = TAILQ_FIRST(&ctx->records); if (entry) TAILQ_REMOVE_HEAD(&ctx->records, next); HWT_CTX_UNLOCK(ctx); if (entry == NULL) break; hwt_record_to_user(entry, &user_entry[i]); hwt_record_entry_free(entry); } return (i); } void hwt_record_free_all(struct hwt_context *ctx) { struct hwt_record_entry *entry; while (1) { HWT_CTX_LOCK(ctx); entry = TAILQ_FIRST(&ctx->records); if (entry) TAILQ_REMOVE_HEAD(&ctx->records, next); HWT_CTX_UNLOCK(ctx); if (entry == NULL) break; hwt_record_entry_free(entry); } } int hwt_record_send(struct hwt_context *ctx, struct hwt_record_get *record_get) { struct hwt_record_user_entry *user_entry; int nitems_req; int error; int i; nitems_req = 0; error = copyin(record_get->nentries, &nitems_req, sizeof(int)); if (error) return (error); if (nitems_req < 1 || nitems_req > 1024) return (ENXIO); user_entry = malloc(sizeof(struct hwt_record_user_entry) * nitems_req, M_HWT_RECORD, M_WAITOK | M_ZERO); i = hwt_record_grab(ctx, user_entry, nitems_req, record_get->wait); if (i > 0) error = copyout(user_entry, record_get->records, sizeof(struct hwt_record_user_entry) * i); if (error == 0) error = copyout(&i, record_get->nentries, sizeof(int)); free(user_entry, M_HWT_RECORD); return (error); } void hwt_record_kernel_objects(struct hwt_context *ctx) { struct hwt_record_entry *entry; struct pmckern_map_in *kobase; int i; kobase = linker_hwpmc_list_objects(); for (i = 0; kobase[i].pm_file != NULL; i++) { entry = hwt_record_entry_alloc(); entry->record_type = HWT_RECORD_KERNEL; entry->fullpath = strdup(kobase[i].pm_file, M_HWT_RECORD); entry->addr = kobase[i].pm_address; HWT_CTX_LOCK(ctx); TAILQ_INSERT_HEAD(&ctx->records, entry, next); HWT_CTX_UNLOCK(ctx); } free(kobase, M_LINKER); } void hwt_record_wakeup(struct hwt_context *ctx) { wakeup(ctx); }