/*! @brief Handles faults on windows mapped to anonymous memory. This function is responsible for handling VM faults on windows which have been mapped to the process server's own anonymous dataspaces, including ones that have been content-initialised. If the dataspace has been set to content-initialised, then we will need to delegate and save the reply cap to reply to it once the content has been initialised. If it has not been initialised we simply map the dataspace page and reply. @param m The recieved IPC fault message from the kernel. @param f The VM fault message info struct. @param aw Found associated window of the faulting address & client. @param window The window structure of the faulting address & client. @return ESUCCESS on success, refos_err_t otherwise. */ static int handle_vm_fault_dspace(struct procserv_msg *m, struct procserv_vmfault_msg *f, struct w_associated_window *aw, struct w_window *window) { assert(f && f->pcb); assert(aw && window && window->mode == W_MODE_ANONYMOUS); vaddr_t dspaceOffset = (f->faultAddr + window->ramDataspaceOffset) - REFOS_PAGE_ALIGN(aw->offset); struct ram_dspace *dspace = window->ramDataspace; assert(dspace && dspace->magic == RAM_DATASPACE_MAGIC); dvprintf("# PID %d VM fault ―――――▶ anon RAM dspace %d\n", f->pcb->pid, dspace->ID); if (dspace->contentInitEnabled) { /* Data space is backed by external content. Content initialisation delegation. */ int contentInitState = ram_dspace_need_content_init(dspace, dspaceOffset); if (contentInitState < 0) { output_segmentation_fault("Failed to retrieve content-init state.", f); return EINVALID; } if (contentInitState == true) { /* Content has not yet been initialised so we delegate. */ if (f->faultAddr + window->ramDataspaceOffset >= aw->offset + aw->size) { output_segmentation_fault("Fault address out of range!", f); return EINVALID; } /* Find the pager's PCB. */ assert(dspace->contentInitPID != PID_NULL); struct proc_pcb* cinitPCB = pid_get_pcb(&procServ.PIDList, dspace->contentInitPID); if (!cinitPCB) { output_segmentation_fault("Invalid content initialiser PID.", f); return EINVALID; } if (!dspace->contentInitEP.capPtr) { output_segmentation_fault("Invalid content-init endpoint!", f); return EINVALID; } /* Save the reply endpoint. */ int error = ram_dspace_add_content_init_waiter_save_current_caller(dspace, dspaceOffset); if (error != ESUCCESS) { output_segmentation_fault("Failed to save reply cap as dspace waiter!", f); return EINVALID; } /* Set up and send the fault notification. */ struct proc_notification vmFaultNotification; vmFaultNotification.magic = PROCSERV_NOTIFICATION_MAGIC; vmFaultNotification.label = PROCSERV_NOTIFY_CONTENT_INIT; vmFaultNotification.arg[0] = dspace->ID; vmFaultNotification.arg[1] = REFOS_PAGE_ALIGN(dspaceOffset); fault_delegate_notification(f, cinitPCB, dspace->contentInitEP, vmFaultNotification, false); /* Return an error here to avoid resuming the client. */ return EDELEGATED; } /* Fallthrough to normal dspace mapping if content-init state is set to already provided. */ } /* Get the page at the dataspaceOffset into the dataspace. */ seL4_CPtr frame = ram_dspace_get_page(dspace, dspaceOffset); if (!frame) { output_segmentation_fault("Out of memory to allocate page or read off end of dspace.", f); return ENOMEM; } /* Map this frame into the client process's page directory. */ int error = vs_map(&f->pcb->vspace, f->faultAddr, &frame, 1); if (error != ESUCCESS) { output_segmentation_fault("Failed to map frame into client's vspace at faultAddr.", f); return error; } return ESUCCESS; }
int test_vspace_mapping(void) { test_start("vspace mapping"); /* Create a vspace for testing mapping. */ struct vs_vspace vs; int error = vs_initialise(&vs, 31337); test_assert(error == ESUCCESS); test_assert(vs.magic == REFOS_VSPACE_MAGIC); /* Create a memory segment window. */ const vaddr_t window = 0x10000; const vaddr_t windowSize = 0x8000; int windowID; error = vs_create_window(&vs, window, windowSize, W_PERMISSION_WRITE | W_PERMISSION_READ, true, &windowID); test_assert(error == ESUCCESS); test_assert(windowID != W_INVALID_WINID); /* Allocate a frame to map. */ vka_object_t frame; error = vka_alloc_frame(&procServ.vka, seL4_PageBits, &frame); test_assert(error == ESUCCESS); test_assert(frame.cptr != 0); /* Try to map in some invalid spots. */ tvprintf("trying mapping into invalid spots...\n"); error = vs_map(&vs, 0x9A0, &frame.cptr, 1); test_assert(error == EINVALIDWINDOW); error = vs_map(&vs, window - 0x9A0, &frame.cptr, 1); test_assert(error == EINVALIDWINDOW); error = vs_map(&vs, window + windowSize + 0x1, &frame.cptr, 1); test_assert(error == EINVALIDWINDOW); error = vs_map(&vs, window + windowSize + 0x5123, &frame.cptr, 1); test_assert(error == EINVALIDWINDOW); /* Try to unmap from some invalid spots. */ tvprintf("trying unmapping from invalid spots...\n"); error = vs_unmap(&vs, window - 0x9A0, 1); test_assert(error == EINVALIDWINDOW); error = vs_unmap(&vs, window + windowSize + 0x423, 5); test_assert(error == EINVALIDWINDOW); error = vs_unmap(&vs, window, windowSize + 1); test_assert(error == EINVALIDWINDOW); /* Map the frame many times in all the valid spots. */ for (vaddr_t waddr = window; waddr < window + windowSize; waddr += (1 << seL4_PageBits)) { tvprintf("trying mapping into valid spot 0x%x...\n", (uint32_t) waddr); /* Map the frame. */ error = vs_map(&vs, waddr, &frame.cptr, 1); test_assert(error == ESUCCESS); /* Try to map frame here again. Should complain. */ error = vs_map(&vs, waddr, &frame.cptr, 1); test_assert(error == EUNMAPFIRST); } /* Unmap and remap the frame many times in all the valid spots. */ for (vaddr_t waddr = window; waddr < window + windowSize; waddr += (1 << seL4_PageBits)) { tvprintf("trying remapping into valid spot 0x%x...\n", (uint32_t) waddr); /* Unmap the frame. */ error = vs_unmap(&vs, waddr, 1); test_assert(error == ESUCCESS); /* Remap the frame. */ error = vs_map(&vs, waddr, &frame.cptr, 1); test_assert(error == ESUCCESS); } /* Clean up. Note that deleting the vspace should delete the created window. */ tvprintf("cleaning up everything in vspace...\n"); vs_unref(&vs); test_assert(vs.magic != REFOS_VSPACE_MAGIC); vka_free_object(&procServ.vka, &frame); return test_success(); }