1 /* $NetBSD: fwcfg.c,v 1.5 2017/11/30 15:42:18 wiz Exp $ */ 2 3 /*- 4 * Copyright (c) 2017 Jared McNeill <jmcneill@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.5 2017/11/30 15:42:18 wiz Exp $"); 31 32 #include <sys/ioctl.h> 33 34 #include <err.h> 35 #include <errno.h> 36 #include <fcntl.h> 37 #include <fuse.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <unistd.h> 42 43 #include <dev/ic/qemufwcfgio.h> 44 45 #include "virtdir.h" 46 47 #define _PATH_FWCFG "/dev/qemufwcfg" 48 49 struct fwcfg_file { 50 uint32_t size; 51 uint16_t select; 52 uint16_t reserved; 53 char name[56]; 54 }; 55 56 static int fwcfg_fd; 57 static mode_t fwcfg_dir_mask = 0555; 58 static mode_t fwcfg_file_mask = 0444; 59 static uid_t fwcfg_uid; 60 static gid_t fwcfg_gid; 61 62 static virtdir_t fwcfg_virtdir; 63 64 static void 65 set_index(uint16_t index) 66 { 67 if (ioctl(fwcfg_fd, FWCFGIO_SET_INDEX, &index) != 0) 68 err(EXIT_FAILURE, "failed to set index 0x%04x", index); 69 } 70 71 static void 72 read_data(void *buf, size_t buflen) 73 { 74 if (read(fwcfg_fd, buf, buflen) != (ssize_t)buflen) 75 err(EXIT_FAILURE, "failed to read data"); 76 } 77 78 static int 79 fwcfg_getattr(const char *path, struct stat *st) 80 { 81 virt_dirent_t *ep; 82 83 if (strcmp(path, "/") == 0) { 84 memset(st, 0, sizeof(*st)); 85 st->st_mode = S_IFDIR | fwcfg_dir_mask; 86 st->st_nlink = 2; 87 return 0; 88 } 89 90 if ((ep = virtdir_find(&fwcfg_virtdir, path, strlen(path))) == NULL) 91 return -ENOENT; 92 93 switch (ep->type) { 94 case 'f': 95 memcpy(st, &fwcfg_virtdir.file, sizeof(*st)); 96 st->st_size = (off_t)ep->tgtlen; 97 st->st_mode = S_IFREG | fwcfg_file_mask; 98 break; 99 case 'd': 100 memcpy(st, &fwcfg_virtdir.dir, sizeof(*st)); 101 st->st_mode = S_IFDIR | fwcfg_dir_mask; 102 break; 103 } 104 st->st_ino = (ino_t)virtdir_offset(&fwcfg_virtdir, ep) + 10; 105 106 return 0; 107 } 108 109 static int 110 fwcfg_readdir(const char *path, void *buf, fuse_fill_dir_t filler, 111 off_t offset, struct fuse_file_info *fi) 112 { 113 static VIRTDIR *dirp; 114 virt_dirent_t *dp; 115 116 if (offset == 0) { 117 if ((dirp = openvirtdir(&fwcfg_virtdir, path)) == NULL) 118 return 0; 119 filler(buf, ".", NULL, 0); 120 filler(buf, "..", NULL, 0); 121 } 122 while ((dp = readvirtdir(dirp)) != NULL) { 123 if (filler(buf, dp->d_name, NULL, 0) != 0) 124 return 0; 125 } 126 closevirtdir(dirp); 127 dirp = NULL; 128 129 return 0; 130 } 131 132 static int 133 fwcfg_open(const char *path, struct fuse_file_info *fi) 134 { 135 return 0; 136 } 137 138 static int 139 fwcfg_read(const char *path, char *buf, size_t size, off_t offset, 140 struct fuse_file_info *fi) 141 { 142 virt_dirent_t *ep; 143 uint8_t tmp[64]; 144 145 if ((ep = virtdir_find(&fwcfg_virtdir, path, strlen(path))) == NULL) 146 return -ENOENT; 147 148 if (ep->select == 0) 149 return -ENOENT; 150 151 set_index(ep->select); 152 153 /* Seek to correct offset */ 154 while (offset > 0) { 155 const size_t len = MIN(sizeof(tmp), (size_t)offset); 156 read_data(tmp, len); 157 offset -= (off_t)len; 158 } 159 160 /* Read the data */ 161 read_data(buf, size); 162 163 return (int)size; 164 } 165 166 static int 167 fwcfg_statfs(const char *path, struct statvfs *st) 168 { 169 uint32_t count; 170 171 set_index(FW_CFG_FILE_DIR); 172 read_data(&count, sizeof(count)); 173 174 memset(st, 0, sizeof(*st)); 175 st->f_files = be32toh(count); 176 177 return 0; 178 } 179 180 static struct fuse_operations fwcfg_ops = { 181 .getattr = fwcfg_getattr, 182 .readdir = fwcfg_readdir, 183 .open = fwcfg_open, 184 .read = fwcfg_read, 185 .statfs = fwcfg_statfs, 186 }; 187 188 static void 189 build_tree(virtdir_t *v) 190 { 191 char path[PATH_MAX]; 192 struct fwcfg_file f; 193 uint32_t count, n; 194 struct stat st; 195 196 memset(&st, 0, sizeof(st)); 197 st.st_uid = fwcfg_uid; 198 st.st_gid = fwcfg_gid; 199 virtdir_init(v, NULL, &st, &st, &st); 200 201 set_index(FW_CFG_FILE_DIR); 202 read_data(&count, sizeof(count)); 203 for (n = 0; n < be32toh(count); n++) { 204 read_data(&f, sizeof(f)); 205 snprintf(path, sizeof(path), "/%s", f.name); 206 virtdir_add(v, path, strlen(path), 'f', NULL, 207 be32toh(f.size), be16toh(f.select)); 208 } 209 } 210 211 #if 0 212 static __dead void 213 usage(void) 214 { 215 fprintf(stderr, "Usage: %s [-F path] [-g gid] [-M dir-mode] " 216 "[-m file-mode] [-u uid] [fuse-options]", getprogname()); 217 exit(EXIT_FAILURE); 218 } 219 #endif 220 221 int 222 main(int argc, char *argv[]) 223 { 224 const char *path = _PATH_FWCFG; 225 int ch; 226 long m; 227 char *ep; 228 229 fwcfg_uid = geteuid(); 230 fwcfg_gid = getegid(); 231 232 while ((ch = getopt(argc, argv, "F:g:m:M:u:")) != -1) { 233 switch (ch) { 234 case 'F': 235 path = optarg; 236 break; 237 case 'g': 238 fwcfg_gid = (gid_t)atoi(optarg); 239 break; 240 case 'm': 241 m = strtol(optarg, &ep, 8); 242 if (optarg == ep || *ep || m < 0) 243 errx(EXIT_FAILURE, "invalid file mode: %s", 244 optarg); 245 fwcfg_file_mask = (mode_t)m; 246 break; 247 case 'M': 248 m = strtol(optarg, &ep, 8); 249 if (optarg == ep || *ep || m < 0) 250 errx(EXIT_FAILURE, "invalid directory mode: %s", 251 optarg); 252 fwcfg_dir_mask = (mode_t)m; 253 break; 254 case 'u': 255 fwcfg_uid = (uid_t)atoi(optarg); 256 break; 257 #ifdef notyet 258 default: 259 usage(); 260 #endif 261 } 262 } 263 264 fwcfg_fd = open(path, O_RDWR); 265 if (fwcfg_fd == -1) 266 err(EXIT_FAILURE, "failed to open %s", path); 267 268 build_tree(&fwcfg_virtdir); 269 270 return fuse_main(argc, argv, &fwcfg_ops, NULL); 271 } 272