1/** 2 * \file 3 * \brief Hacky MMAP support for VFS. 4 * \bug The current implementation is a thin layer over an anonymous memobj. 5 * It does not share memory, and does not propagate any updates. 6 */ 7 8/* 9 * Copyright (c) 2011, ETH Zurich. 10 * All rights reserved. 11 * 12 * This file is distributed under the terms in the attached LICENSE file. 13 * If you do not find this file, copies can be found by writing to: 14 * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group. 15 */ 16 17#include <stdio.h> 18#include <stdlib.h> 19#include <string.h> 20#include <barrelfish/barrelfish.h> 21#include <barrelfish/memobj.h> 22#include <vfs/mmap.h> 23 24/** 25 * \brief Page fault handler 26 * 27 * \param memobj The memory object 28 * \param region The associated vregion 29 * \param offset Offset into memory object of the page fault 30 * \param type The fault type 31 */ 32static errval_t pagefault(struct memobj *memobj, struct vregion *vregion, 33 genvaddr_t offset, vm_fault_type_t type) 34{ 35 errval_t err; 36 assert(memobj->type == MEMOBJ_VFS); 37 struct memobj_vfs *mv = (struct memobj_vfs *)memobj; 38 struct memobj_anon *anon = &mv->anon; 39 struct vspace *vspace = vregion_get_vspace(vregion); 40 struct pmap *pmap = vspace_get_pmap(vspace); 41 genvaddr_t vregion_base = vregion_get_base_addr(vregion); 42 genvaddr_t vregion_off = vregion_get_offset(vregion); 43 44 assert(vregion_off == 0); // not sure if we handle this correctly 45 46 // Walk the ordered list to find the matching frame, but don't map it yet 47 struct memobj_frame_list *walk = anon->frame_list; 48 while (walk) { 49 if (offset >= walk->offset && offset < walk->offset + walk->size) { 50 break; 51 } 52 walk = walk->next; 53 } 54 55 if (walk == NULL) { 56 return LIB_ERR_MEMOBJ_WRONG_OFFSET; 57 } 58 59 genvaddr_t map_offset = vregion_off + walk->offset; 60 size_t nbytes = walk->size; 61 62 // how much do we need to read from the file? 63 if (map_offset >= mv->filesize) { 64 // nothing 65 goto do_map; 66 } else if (map_offset + nbytes > mv->filesize) { 67 // limit size of read to maximum mapping (rest is zero-filled) 68 nbytes = mv->filesize - map_offset; 69 } 70 71#if 0 72 debug_printf("fault at offset %lx, mapping at %lx-%lx from file data %lx-%lx\n", 73 offset, vregion_base + map_offset, 74 vregion_base + map_offset + walk->size, 75 map_offset + mv->offset, 76 map_offset + mv->offset + nbytes); 77#endif 78 79 // map frame writable at temporary location so that we can safely fill it 80 void *buf; 81 struct memobj *tmp_memobj = NULL; 82 struct vregion *tmp_vregion = NULL; 83 err = vspace_map_one_frame(&buf, walk->size, walk->frame, 84 &tmp_memobj, &tmp_vregion); 85 if (err_is_fail(err)) { 86 DEBUG_ERR(err, "error setting up temp mapping in mmap pagefault handler\n"); 87 return err; // XXX 88 } 89 90 // seek file handle 91 err = vfs_seek(mv->vh, VFS_SEEK_SET, map_offset + mv->offset); 92 if (err_is_fail(err)) { 93 return err; 94 } 95 96 // read contents into frame 97 size_t rsize, pos = 0; 98 do { 99 err = vfs_read(mv->vh, (char *)buf + pos, nbytes - pos, &rsize); 100 if (err_is_fail(err)) { 101 break; 102 } 103 pos += rsize; 104 } while(rsize > 0 && pos < nbytes); 105 106 // destroy temp mappings 107 // FIXME: the API for tearing down mappings is really unclear! is this sufficient? 108 err = vregion_destroy(tmp_vregion); 109 assert(err_is_ok(err)); 110 err = memobj_destroy_one_frame(tmp_memobj); 111 assert(err_is_ok(err)); 112 //free(tmp_vregion); 113 //free(tmp_memobj); 114 115do_map: 116 // map at target address with appropriate flags 117 err = pmap->f.map(pmap, vregion_base + map_offset, walk->frame, 0, 118 walk->size, vregion_get_flags(vregion), NULL, NULL); 119 if (err_is_fail(err)) { 120 return err_push(err, LIB_ERR_PMAP_MAP); 121 } 122 123 return SYS_ERR_OK; 124} 125 126/** 127 * \brief Initialize 128 * 129 * \param memobj The memory object 130 * \param size Size of the memory region 131 * \param flags Memory object specific flags 132 * \param vh VFS handle for underlying file 133 * \param offset Offset within file to start mapping 134 * \param filesize Size of file data to map, anything above this is zero-filled 135 */ 136errval_t memobj_create_vfs(struct memobj_vfs *memobj, size_t size, 137 memobj_flags_t flags, vfs_handle_t vh, off_t offset, 138 size_t filesize) 139{ 140 errval_t err; 141 142 // create anon memobj 143 err = memobj_create_anon(&memobj->anon, size, flags); 144 if (err_is_fail(err)) { 145 return err; 146 } 147 148 // replace pagefault handler with our own 149 ((struct memobj *)memobj)->f.pagefault = pagefault; 150 151 // reset type 152 ((struct memobj *)memobj)->type = MEMOBJ_VFS; 153 154 memobj->vh = vh; 155 memobj->offset = offset; 156 memobj->filesize = filesize; 157 158 return SYS_ERR_OK; 159} 160 161// FIXME: why aren't the destructors instance methods? -AB 162errval_t memobj_destroy_vfs(struct memobj *memobj) 163{ 164 USER_PANIC("NYI"); 165} 166 167// Kludge to push changes in VFS memobj back out to disk 168errval_t memobj_flush_vfs(struct memobj *memobj, struct vregion *vregion) 169{ 170 errval_t err; 171 172 assert(memobj->type == MEMOBJ_VFS); 173 174 struct memobj_vfs *mv = (struct memobj_vfs *)memobj; 175 struct vspace *vspace = vregion_get_vspace(vregion); 176 struct pmap *pmap = vspace_get_pmap(vspace); 177 genvaddr_t vregion_base = vregion_get_base_addr(vregion); 178 lvaddr_t vregion_lbase = vspace_genvaddr_to_lvaddr(vregion_base); 179 genvaddr_t vregion_off = vregion_get_offset(vregion); 180 181 assert(vregion_off == 0); // not sure if we handle this correctly 182 183 /* TODO: mv->size instead of BASE_PAGE_SIZE?*/ 184 for (genvaddr_t off = 0; off < mv->filesize ; off += BASE_PAGE_SIZE){ 185 // For each page check if it's in memory 186 err = pmap->f.lookup(pmap, vregion_base + off, NULL); 187 if (err_is_fail(err)) { 188 continue; // Page not in memory 189#if 0 /* this optimisation may not be correct if flags were changed -AB */ 190 } else if ((retflags & VREGION_FLAGS_WRITE) == 0) { 191 continue; // Not writable 192#endif 193 } 194 195 //TRACE("Flushing page at address: %lx\n", vregion_base + off); 196 197 // seek file handle 198 err = vfs_seek(mv->vh, VFS_SEEK_SET, off + mv->offset); 199 if (err_is_fail(err)) { 200 return err; 201 } 202 203 // write contents to file 204 size_t rsize, pos = 0; 205 size_t nbytes = mv->filesize - off; 206 if (nbytes > BASE_PAGE_SIZE) { 207 nbytes = BASE_PAGE_SIZE; 208 } 209 210 do { 211 err = vfs_write(mv->vh, (char *)vregion_lbase + off + pos, 212 nbytes - pos, &rsize); 213 if (err_is_fail(err)) { 214 return err; 215 } 216 pos += rsize; 217 } while(rsize > 0 && pos < nbytes); 218 assert(pos==nbytes); 219 } 220 221 return SYS_ERR_OK; 222} 223 224/** 225 * \brief Wrapper to create and map a file object, optionally at a fixed address 226 * 227 * The memory object and vregion are returned so the user can call fill and 228 * pagefault on it to create actual mappings. 229 */ 230static errval_t vspace_map_file_internal(genvaddr_t opt_base, 231 size_t opt_alignment, 232 size_t size, vregion_flags_t flags, 233 vfs_handle_t file, off_t offset, 234 size_t filesize, 235 struct vregion **ret_vregion, 236 struct memobj **ret_memobj) 237{ 238 errval_t err1, err2; 239 struct memobj *memobj = NULL; 240 struct vregion *vregion = NULL; 241 242 // Allocate space 243 memobj = malloc(sizeof(struct memobj_vfs)); 244 if (!memobj) { 245 err1 = LIB_ERR_MALLOC_FAIL; 246 goto error; 247 } 248 vregion = malloc(sizeof(struct vregion)); 249 if (!vregion) { 250 err1 = LIB_ERR_MALLOC_FAIL; 251 goto error; 252 } 253 254 // Create a memobj and vregion 255 err1 = memobj_create_vfs((struct memobj_vfs *)memobj, size, 0, file, offset, 256 filesize); 257 if (err_is_fail(err1)) { 258 err1 = err_push(err1, LIB_ERR_MEMOBJ_CREATE_VFS); 259 goto error; 260 } 261 262 if (opt_base != 0) { 263 err1 = vregion_map_fixed(vregion, get_current_vspace(), memobj, 0, size, 264 opt_base, flags); 265 } else if (opt_alignment != 0) { 266 err1 = vregion_map_aligned(vregion, get_current_vspace(), memobj, 0, size, 267 flags, opt_alignment); 268 } else { 269 err1 = vregion_map(vregion, get_current_vspace(), memobj, 0, size, flags); 270 } 271 if (err_is_fail(err1)) { 272 err1 = err_push(err1, LIB_ERR_VREGION_MAP); 273 goto error; 274 } 275 276 *ret_vregion = vregion; 277 *ret_memobj = memobj; 278 279 return SYS_ERR_OK; 280 281 error: 282 if (memobj) { 283 err2 = memobj_destroy_vfs(memobj); 284 if (err_is_fail(err2)) { 285 DEBUG_ERR(err2, "memobj_destroy_anon failed"); 286 } 287 free(memobj); 288 } 289 if (vregion) { 290 err2 = vregion_destroy(vregion); 291 if (err_is_fail(err2)) { 292 DEBUG_ERR(err2, "vregion_destroy failed"); 293 } 294 free(vregion); 295 } 296 return err1; 297} 298 299errval_t vspace_map_file(size_t size, vregion_flags_t flags, 300 vfs_handle_t file, off_t offset, size_t filesize, 301 struct vregion **ret_vregion, 302 struct memobj **ret_memobj) 303{ 304 return vspace_map_file_internal(0, 0, size, flags, file, offset, filesize, 305 ret_vregion, ret_memobj); 306} 307 308errval_t vspace_map_file_fixed(genvaddr_t base, size_t size, 309 vregion_flags_t flags, vfs_handle_t file, 310 off_t offset, size_t filesize, 311 struct vregion **ret_vregion, 312 struct memobj **ret_memobj) 313{ 314 assert(base != 0); 315 return vspace_map_file_internal(base, 0, size, flags, file, offset, filesize, 316 ret_vregion, ret_memobj); 317} 318 319errval_t vspace_map_file_aligned(size_t alignment, size_t size, 320 vregion_flags_t flags, vfs_handle_t file, 321 off_t offset, size_t filesize, 322 struct vregion **ret_vregion, 323 struct memobj **ret_memobj) 324{ 325 return vspace_map_file_internal(0, alignment, size, flags, file, offset, 326 filesize, ret_vregion, ret_memobj); 327} 328