#include "nvdebug.h" #include // For seq_* functions and types #include // For copy_to_user() // Generic register printing function, used for PTOP_*_NUM registers (+more) // @param f File being read from. `data` field is register offset to read. // @param buf User buffer for result // @param size Length of user buffer // @param off Requested offset. Updated by number of characters written. // @return -errno on error, otherwise number of bytes written to *buf // Note: Parent `data` field MUST be the GPU index static ssize_t nvdebug_reg32_read(struct file *f, char __user *buf, size_t size, loff_t *off) { char out[16]; int chars_written; struct nvdebug_state *g = &g_nvdebug_state[file2parentgpuidx(f)]; if (size < 16 || *off != 0) return 0; // 32 bit register will always take less than 16 characters to print chars_written = scnprintf(out, 16, "%#0x\n", nvdebug_readl(g, (uintptr_t)PDE_DATA(file_inode(f)))); if (copy_to_user(buf, out, chars_written)) printk(KERN_WARNING "Unable to copy all data for %s\n", file_dentry(f)->d_name.name); *off += chars_written; return chars_written; } struct file_operations nvdebug_read_reg32_file_ops = { .read = nvdebug_reg32_read, }; //// ==v== PTOP_DEVICE_INFO ==v== //// // Called to start or resume a sequence. Prior to 4.19, *pos is unreliable. // Initializes iterator `idx` state and returns it. Ends sequence on NULL. static void* device_info_file_seq_start(struct seq_file *s, loff_t *pos) { static int idx; // If start of sequence, reset `idx` if (*pos == 0) idx = 0; // Number of possible info entries is fixed, and list is sparse if (idx >= NV_PTOP_DEVICE_INFO__SIZE_1) return NULL; return &idx; } // Steps to next record. Returns new value of `idx`. // Calls show() on non-NULL return static void* device_info_file_seq_next(struct seq_file *s, void *idx, loff_t *pos) { (*pos)++; // Required by seq interface // Number of possible info entries is fixed, and list is sparse if ((*(int*)idx)++ >= NV_PTOP_DEVICE_INFO__SIZE_1) return NULL; return idx; } // Print info at index *idx. Returns non-zero on error. static int device_info_file_seq_show(struct seq_file *s, void *idx) { ptop_device_info_t curr_info; struct nvdebug_state *g = &g_nvdebug_state[seq2gpuidx(s)]; curr_info.raw = nvdebug_readl(g, NV_PTOP_DEVICE_INFO(*(int*)idx)); // Check for read errors if (curr_info.raw == -1) return -EIO; // Parse and print the data switch(curr_info.info_type) { case INFO_TYPE_DATA: // As of early 2022, only the ENUM2 format of this entry exists if (curr_info.is_not_enum2) break; seq_printf(s, "| BAR0 Base %#.8x\n" "| instance %d\n", curr_info.pri_base << 12, curr_info.inst_id); if (curr_info.fault_id_is_valid) seq_printf(s, "| Fault ID: %3d\n", curr_info.fault_id); break; case INFO_TYPE_ENUM: if (curr_info.engine_is_valid) seq_printf(s, "| Host's Engine ID: %2d\n", curr_info.engine_enum); if (curr_info.runlist_is_valid) seq_printf(s, "| Runlist ID: %2d\n", curr_info.runlist_enum); if (curr_info.intr_is_valid) seq_printf(s, "| Interrupt ID: %2d\n", curr_info.intr_enum); if (curr_info.reset_is_valid) seq_printf(s, "| Reset ID: %2d\n", curr_info.reset_enum); break; case INFO_TYPE_ENGINE_TYPE: seq_printf(s, "| Engine Type: %2d (", curr_info.engine_type); if (curr_info.engine_type < ENGINE_TYPES_LEN) seq_printf(s, "%s)\n", ENGINE_TYPES_NAMES[curr_info.engine_type]); else seq_printf(s, "Unknown Engine, introduced post-Ampere)\n"); break; case INFO_TYPE_NOT_VALID: default: // Device info records are sparse, so skip unset or unknown ones return 0; } // Draw a line between each device entry if (!curr_info.has_next_entry) seq_printf(s, "+---------------------+\n"); return 0; } static void device_info_file_seq_stop(struct seq_file *s, void *idx) { // No cleanup needed } static const struct seq_operations device_info_file_seq_ops = { .start = device_info_file_seq_start, .next = device_info_file_seq_next, .stop = device_info_file_seq_stop, .show = device_info_file_seq_show, }; static int device_info_file_open(struct inode *inode, struct file *f) { return seq_open(f, &device_info_file_seq_ops); } struct file_operations device_info_file_ops = { .open = device_info_file_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release, };