1 /* 2 * Copyright 2007 The Android Open Source Project 3 * 4 * Fake device support. 5 */ 6 /* 7 Implementation notes: 8 9 There are a couple of basic scenarios, exemplified by the "fb" and 10 "events" devices. The framebuffer driver is pretty simple, handling a 11 few ioctl()s and managing a stretch of memory. We can just intercept a 12 few calls. The input event driver can be used in a select() or poll() 13 call with other file descriptors, which either requires us to do some 14 fancy tricks with select() and poll(), or requires that we return a real 15 file descriptor (perhaps based on a socketpair). 16 17 We have three basic approaches to dealing with "fake" file descriptors: 18 19 (1) Always use real fds. We can dup() an open /dev/null to get a number 20 for the cases where we don't need a socketpair. 21 (2) Always use fake fds with absurdly high numeric values. Testing to see 22 if the fd is one we handle is trivial (range check). This doesn't 23 work for select(), which uses fd bitmaps accessed through macros. 24 (3) Use a mix of real and fake fds, in a high range (512-1023). Because 25 it's in the "real" range, we can pass real fds around for things that 26 are handed to poll() and select(), but because of the high numeric 27 value we *should* be able to get away with a trivial range check. 28 29 Approach (1) is the most portable and least likely to break, but the 30 efficiencies gained in approach (2) make it more desirable. There is 31 a small risk of application fds wandering into our range, but we can 32 minimize that by asserting on a "guard zone" and/or obstructing dup2(). 33 (We can also dup2(/dev/null) to "reserve" our fds, but that wastes 34 resources.) 35 */ 36 37 #include "Common.h" 38 39 #include <stdlib.h> 40 #include <string.h> 41 #include <sys/types.h> 42 #include <sys/socket.h> 43 #include <assert.h> 44 #include <fnmatch.h> 45 46 /* 47 * Devices we intercept. 48 * 49 * Needed: 50 * /dev/alarm 51 * radio 52 */ 53 typedef FakeDev* (*wsFileHook)(const char *path, int flags); 54 55 typedef struct FakedPath { 56 const char *pathexpr; 57 wsFileHook hook; 58 } FakedPath; 59 60 FakedPath fakedpaths[] = 61 { 62 { "/dev/graphics/fb0", wsOpenDevFb }, 63 { "/dev/hw3d", NULL }, 64 { "/dev/eac", wsOpenDevAudio }, 65 { "/dev/tty0", wsOpenDevConsoleTty }, 66 { "/dev/input/event0", wsOpenDevEvent }, 67 { "/dev/input/*", NULL }, 68 { "/dev/log/*", wsOpenDevLog }, 69 { "/sys/class/power_supply/*", wsOpenDevPower }, 70 { "/sys/power/state", wsOpenSysPower }, 71 { "/sys/power/wake_lock", wsOpenSysPower }, 72 { "/sys/power/wake_unlock", wsOpenSysPower }, 73 { "/sys/devices/platform/android-vibrator/enable", wsOpenDevVibrator }, 74 { "/sys/qemu_trace/*", NULL }, 75 { NULL, NULL } 76 }; 77 78 79 /* 80 * Generic drop-in for an unimplemented call. 81 * 82 * Returns -1, which conveniently is the same as MAP_FAILED for mmap. 83 */ 84 static int notImplemented(FakeDev* dev, const char* callName) 85 { 86 wsLog("WARNING: unimplemented %s() on '%s' %p\n", 87 callName, dev->debugName, dev->state); 88 errno = kNoHandlerError; 89 return -1; 90 } 91 92 /* 93 * Default implementations. We want to log as much information as we can 94 * so that we can fill in the missing implementation. 95 * 96 * TODO: for some or all of these we will want to display the full arg list. 97 */ 98 static int noClose(FakeDev* dev, ...) 99 { 100 return 0; 101 } 102 static FakeDev* noDup(FakeDev* dev, ...) 103 { 104 notImplemented(dev, "dup"); 105 return NULL; 106 } 107 static int noRead(FakeDev* dev, ...) 108 { 109 return notImplemented(dev, "read"); 110 } 111 static int noReadv(FakeDev* dev, ...) 112 { 113 return notImplemented(dev, "readv"); 114 } 115 static int noWrite(FakeDev* dev, ...) 116 { 117 return notImplemented(dev, "write"); 118 } 119 static int noWritev(FakeDev* dev, ...) 120 { 121 return notImplemented(dev, "writev"); 122 } 123 static int noMmap(FakeDev* dev, ...) 124 { 125 return notImplemented(dev, "mmap"); 126 } 127 static int noIoctl(FakeDev* dev, ...) 128 { 129 return notImplemented(dev, "ioctl"); 130 } 131 132 133 /* 134 * Create a new FakeDev entry. 135 * 136 * We mark the fd slot as "used" in the bitmap, but don't add it to the 137 * table yet since the entry is not fully prepared. 138 */ 139 FakeDev* wsCreateFakeDev(const char* debugName) 140 { 141 FakeDev* newDev; 142 int cc; 143 144 assert(debugName != NULL); 145 146 newDev = (FakeDev*) calloc(1, sizeof(FakeDev)); 147 if (newDev == NULL) 148 return NULL; 149 150 newDev->debugName = strdup(debugName); 151 newDev->state = NULL; 152 153 newDev->close = (Fake_close) noClose; 154 newDev->dup = (Fake_dup) noDup; 155 newDev->read = (Fake_read) noRead; 156 newDev->readv = (Fake_readv) noReadv; 157 newDev->write = (Fake_write) noWrite; 158 newDev->writev = (Fake_writev) noWritev; 159 newDev->mmap = (Fake_mmap) noMmap; 160 newDev->ioctl = (Fake_ioctl) noIoctl; 161 162 /* 163 * Allocate a new entry. The bit vector map is really only used as a 164 * performance boost in the current implementation. 165 */ 166 cc = pthread_mutex_lock(&gWrapSim.fakeFdLock); assert(cc == 0); 167 int newfd = wsAllocBit(gWrapSim.fakeFdMap); 168 cc = pthread_mutex_unlock(&gWrapSim.fakeFdLock); assert(cc == 0); 169 170 if (newfd < 0) { 171 wsLog("WARNING: ran out of 'fake' file descriptors\n"); 172 free(newDev); 173 return NULL; 174 } 175 newDev->fd = newfd + kFakeFdBase; 176 newDev->otherFd = -1; 177 assert(gWrapSim.fakeFdList[newDev->fd - kFakeFdBase] == NULL); 178 179 return newDev; 180 } 181 182 /* 183 * Create a new FakeDev entry, and open a file descriptor that actually 184 * works. 185 */ 186 FakeDev* wsCreateRealFakeDev(const char* debugName) 187 { 188 FakeDev* newDev = wsCreateFakeDev(debugName); 189 if (newDev == NULL) 190 return newDev; 191 192 int fds[2]; 193 194 if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) { 195 wsLog("socketpair() failed: %s\n", strerror(errno)); 196 wsFreeFakeDev(newDev); 197 return NULL; 198 } 199 200 if (dup2(fds[0], newDev->fd) < 0) { 201 wsLog("dup2(%d,%d) failed: %s\n", 202 fds[0], newDev->fd, strerror(errno)); 203 wsFreeFakeDev(newDev); 204 return NULL; 205 } 206 close(fds[0]); 207 208 /* okay to leave this one in the "normal" range; not visible to app */ 209 newDev->otherFd = fds[1]; 210 211 return newDev; 212 } 213 214 /* 215 * Free fake device entry. 216 */ 217 void wsFreeFakeDev(FakeDev* dev) 218 { 219 if (dev == NULL) 220 return; 221 222 wsLog("## closing/freeing '%s' (%d/%d)\n", 223 dev->debugName, dev->fd, dev->otherFd); 224 225 /* 226 * If we assigned a file descriptor slot, free it up. 227 */ 228 if (dev->fd >= 0) { 229 int cc; 230 231 gWrapSim.fakeFdList[dev->fd - kFakeFdBase] = NULL; 232 233 cc = pthread_mutex_lock(&gWrapSim.fakeFdLock); assert(cc == 0); 234 wsFreeBit(gWrapSim.fakeFdMap, dev->fd - kFakeFdBase); 235 cc = pthread_mutex_unlock(&gWrapSim.fakeFdLock); assert(cc == 0); 236 } 237 if (dev->otherFd >= 0) 238 close(dev->otherFd); 239 240 if (dev->debugName) free(dev->debugName); 241 free(dev); 242 } 243 244 /* 245 * Map a file descriptor to a fake device. 246 * 247 * Returns NULL if there's no corresponding entry. 248 */ 249 FakeDev* wsFakeDevFromFd(int fd) 250 { 251 /* quick range test */ 252 if (fd < kFakeFdBase || fd >= kFakeFdBase + kMaxFakeFdCount) 253 return NULL; 254 255 return gWrapSim.fakeFdList[fd - kFakeFdBase]; 256 } 257 258 259 /* 260 * Check to see if we're opening a device that we want to fake out. 261 * 262 * We return a file descriptor >= 0 on success, -1 if we're not interested, 263 * or -2 if we explicitly want to pretend that the device doesn't exist. 264 */ 265 int wsInterceptDeviceOpen(const char* pathName, int flags) 266 { 267 FakedPath* p = fakedpaths; 268 269 while (p->pathexpr) { 270 if (fnmatch(p->pathexpr, pathName, 0) == 0) { 271 if (p->hook != NULL) { 272 FakeDev* dev = p->hook(pathName, flags); 273 if (dev != NULL) { 274 /* 275 * Now that the device entry is ready, add it to the list. 276 */ 277 wsLog("## created fake dev %d: '%s' %p\n", 278 dev->fd, dev->debugName, dev->state); 279 gWrapSim.fakeFdList[dev->fd - kFakeFdBase] = dev; 280 return dev->fd; 281 } 282 } else { 283 wsLog("## rejecting attempt to open %s\n", pathName); 284 errno = ENOENT; 285 return -2; 286 } 287 break; 288 } 289 p++; 290 } 291 return -1; 292 } 293 294 /* 295 * Check to see if we're accessing a device that we want to fake out. 296 * Returns 0 if the device can be (fake) opened with the given mode, 297 * -1 if it can't, -2 if it can't and we don't want to allow fallback 298 * to the host-device either. 299 * TODO: actually check the mode. 300 */ 301 int wsInterceptDeviceAccess(const char *pathName, int mode) 302 { 303 FakedPath *p = fakedpaths; 304 305 while (p->pathexpr) { 306 if (fnmatch(p->pathexpr, pathName, 0) == 0) { 307 if (p->hook) { 308 return 0; 309 } else { 310 wsLog("## rejecting attempt to open %s\n", pathName); 311 errno = ENOENT; 312 return -2; 313 } 314 break; 315 } 316 p++; 317 } 318 errno = ENOENT; 319 return -1; 320 } 321