1 /* $NetBSD: fwcfg.c,v 1.7 2022/01/22 07:53:06 pho Exp $ */ 2 3 /*- 4 * Copyright (c) 2017 Jared McNeill <jmcneill (at) invisible.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __RCSID("$NetBSD: fwcfg.c,v 1.7 2022/01/22 07:53:06 pho Exp $"); 31 32 #define FUSE_USE_VERSION FUSE_MAKE_VERSION(2, 6) 33 34 #include <sys/ioctl.h> 35 #include <sys/param.h> 36 37 #include <err.h> 38 #include <errno.h> 39 #include <fcntl.h> 40 #include <fuse.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <unistd.h> 45 46 #include <dev/ic/qemufwcfgio.h> 47 48 #include "virtdir.h" 49 50 #define _PATH_FWCFG "/dev/qemufwcfg" 51 52 struct fwcfg_file { 53 uint32_t size; 54 uint16_t select; 55 uint16_t reserved; 56 char name[56]; 57 }; 58 59 static int fwcfg_fd; 60 static mode_t fwcfg_dir_mask = 0555; 61 static mode_t fwcfg_file_mask = 0444; 62 static uid_t fwcfg_uid; 63 static gid_t fwcfg_gid; 64 65 static virtdir_t fwcfg_virtdir; 66 67 static void 68 set_index(uint16_t index) 69 { 70 if (ioctl(fwcfg_fd, FWCFGIO_SET_INDEX, &index) != 0) 71 err(EXIT_FAILURE, "failed to set index 0x%04x", index); 72 } 73 74 static void 75 read_data(void *buf, size_t buflen) 76 { 77 if (read(fwcfg_fd, buf, buflen) != (ssize_t)buflen) 78 err(EXIT_FAILURE, "failed to read data"); 79 } 80 81 static int 82 fwcfg_getattr(const char *path, struct stat *st) 83 { 84 virt_dirent_t *ep; 85 86 if (strcmp(path, "/") == 0) { 87 memset(st, 0, sizeof(*st)); 88 st->st_mode = S_IFDIR | fwcfg_dir_mask; 89 st->st_nlink = 2; 90 return 0; 91 } 92 93 if ((ep = virtdir_find(&fwcfg_virtdir, path, strlen(path))) == NULL) 94 return -ENOENT; 95 96 switch (ep->type) { 97 case 'f': 98 memcpy(st, &fwcfg_virtdir.file, sizeof(*st)); 99 st->st_size = (off_t)ep->tgtlen; 100 st->st_mode = S_IFREG | fwcfg_file_mask; 101 break; 102 case 'd': 103 memcpy(st, &fwcfg_virtdir.dir, sizeof(*st)); 104 st->st_mode = S_IFDIR | fwcfg_dir_mask; 105 break; 106 } 107 st->st_ino = (ino_t)virtdir_offset(&fwcfg_virtdir, ep) + 10; 108 109 return 0; 110 } 111 112 static int 113 fwcfg_readdir(const char *path, void *buf, fuse_fill_dir_t filler, 114 off_t offset, struct fuse_file_info *fi) 115 { 116 static VIRTDIR *dirp; 117 virt_dirent_t *dp; 118 119 if (offset == 0) { 120 if ((dirp = openvirtdir(&fwcfg_virtdir, path)) == NULL) 121 return 0; 122 filler(buf, ".", NULL, 0); 123 filler(buf, "..", NULL, 0); 124 } 125 while ((dp = readvirtdir(dirp)) != NULL) { 126 if (filler(buf, dp->d_name, NULL, 0) != 0) 127 return 0; 128 } 129 closevirtdir(dirp); 130 dirp = NULL; 131 132 return 0; 133 } 134 135 static int 136 fwcfg_open(const char *path, struct fuse_file_info *fi) 137 { 138 return 0; 139 } 140 141 static int 142 fwcfg_read(const char *path, char *buf, size_t size, off_t offset, 143 struct fuse_file_info *fi) 144 { 145 virt_dirent_t *ep; 146 uint8_t tmp[64]; 147 148 if ((ep = virtdir_find(&fwcfg_virtdir, path, strlen(path))) == NULL) 149 return -ENOENT; 150 151 if (ep->select == 0) 152 return -ENOENT; 153 154 set_index(ep->select); 155 156 /* Seek to correct offset */ 157 while (offset > 0) { 158 const size_t len = MIN(sizeof(tmp), (size_t)offset); 159 read_data(tmp, len); 160 offset -= (off_t)len; 161 } 162 163 /* Read the data */ 164 read_data(buf, size); 165 166 return (int)size; 167 } 168 169 static int 170 fwcfg_statfs(const char *path, struct statvfs *st) 171 { 172 uint32_t count; 173 174 set_index(FW_CFG_FILE_DIR); 175 read_data(&count, sizeof(count)); 176 177 memset(st, 0, sizeof(*st)); 178 st->f_files = be32toh(count); 179 180 return 0; 181 } 182 183 static struct fuse_operations fwcfg_ops = { 184 .getattr = fwcfg_getattr, 185 .readdir = fwcfg_readdir, 186 .open = fwcfg_open, 187 .read = fwcfg_read, 188 .statfs = fwcfg_statfs, 189 }; 190 191 static void 192 build_tree(virtdir_t *v) 193 { 194 char path[PATH_MAX]; 195 struct fwcfg_file f; 196 uint32_t count, n; 197 struct stat st; 198 199 memset(&st, 0, sizeof(st)); 200 st.st_uid = fwcfg_uid; 201 st.st_gid = fwcfg_gid; 202 virtdir_init(v, NULL, &st, &st, &st); 203 204 set_index(FW_CFG_FILE_DIR); 205 read_data(&count, sizeof(count)); 206 for (n = 0; n < be32toh(count); n++) { 207 read_data(&f, sizeof(f)); 208 snprintf(path, sizeof(path), "/%s", f.name); 209 virtdir_add(v, path, strlen(path), 'f', NULL, 210 be32toh(f.size), be16toh(f.select)); 211 } 212 } 213 214 #if 0 215 static __dead void 216 usage(void) 217 { 218 fprintf(stderr, "Usage: %s [-F path] [-g gid] [-M dir-mode] " 219 "[-m file-mode] [-u uid] [fuse-options]", getprogname()); 220 exit(EXIT_FAILURE); 221 } 222 #endif 223 224 int 225 main(int argc, char *argv[]) 226 { 227 const char *path = _PATH_FWCFG; 228 int ch; 229 long m; 230 char *ep; 231 232 fwcfg_uid = geteuid(); 233 fwcfg_gid = getegid(); 234 235 while ((ch = getopt(argc, argv, "F:g:m:M:u:")) != -1) { 236 switch (ch) { 237 case 'F': 238 path = optarg; 239 break; 240 case 'g': 241 fwcfg_gid = (gid_t)atoi(optarg); 242 break; 243 case 'm': 244 m = strtol(optarg, &ep, 8); 245 if (optarg == ep || *ep || m < 0) 246 errx(EXIT_FAILURE, "invalid file mode: %s", 247 optarg); 248 fwcfg_file_mask = (mode_t)m; 249 break; 250 case 'M': 251 m = strtol(optarg, &ep, 8); 252 if (optarg == ep || *ep || m < 0) 253 errx(EXIT_FAILURE, "invalid directory mode: %s", 254 optarg); 255 fwcfg_dir_mask = (mode_t)m; 256 break; 257 case 'u': 258 fwcfg_uid = (uid_t)atoi(optarg); 259 break; 260 #ifdef notyet 261 default: 262 usage(); 263 #endif 264 } 265 } 266 267 fwcfg_fd = open(path, O_RDWR); 268 if (fwcfg_fd == -1) 269 err(EXIT_FAILURE, "failed to open %s", path); 270 271 build_tree(&fwcfg_virtdir); 272 273 return fuse_main(argc, argv, &fwcfg_ops, NULL); 274 } 275