1/* 2 * Copyright 2016, Data61 3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO) 4 * ABN 41 687 119 230. 5 * 6 * This software may be distributed and modified according to the terms of 7 * the BSD 2-Clause license. Note that NO WARRANTY is provided. 8 * See "LICENSE_BSD2.txt" for details. 9 * 10 * @TAG(D61_BSD) 11 */ 12 13#include "process.h" 14#include "proc_client_watch.h" 15#include "../../common.h" 16#include "../../state.h" 17#include "../../dispatchers/dispatcher.h" 18#include <refos-rpc/proc_common.h> 19 20/*! @file 21 @brief Process server death notification book-keeping module. */ 22 23static void 24client_watch_free_ep_cslot(cspacepath_t *ep) 25{ 26 assert(ep && ep->capPtr); 27 28 /* Free the endpoint and its cslot. */ 29 vka_cnode_revoke(ep); 30 vka_cnode_delete(ep); 31 vka_cspace_free(&procServ.vka, ep->capPtr); 32 memset(ep, 0, sizeof(cspacepath_t)); 33 kfree(ep); 34} 35 36static int 37client_watch_find(struct proc_watch_list *wl, uint32_t pid) 38{ 39 assert(wl && wl->magic == REFOS_PCB_WATCHLIST_MAGIC); 40 int count = cvector_count(&wl->clientList); 41 for (int i = 0; i < count; i++) { 42 uint32_t _pid = (uint32_t) cvector_get(&wl->clientList, i); 43 if (_pid == pid) { 44 return i; 45 } 46 } 47 return -1; 48} 49 50/* --------------------------- Proc watch client interface functions ---------------------------- */ 51 52void 53client_watch_init(struct proc_watch_list *wl) 54{ 55 assert(wl); 56 wl->magic = REFOS_PCB_WATCHLIST_MAGIC; 57 cvector_init(&wl->clientList); 58 cvector_init(&wl->endpointNotifyList); 59} 60 61void 62client_watch_release(struct proc_watch_list *wl) 63{ 64 assert(wl && wl->magic == REFOS_PCB_WATCHLIST_MAGIC); 65 int count = cvector_count(&wl->endpointNotifyList); 66 for (int i = 0; i < count; i++) { 67 cspacepath_t *ep = (cspacepath_t *) cvector_get(&wl->endpointNotifyList, i); 68 client_watch_free_ep_cslot(ep); 69 } 70 cvector_free(&wl->clientList); 71 cvector_free(&wl->endpointNotifyList); 72 wl->magic = 0; 73} 74 75seL4_CPtr 76client_watch_get(struct proc_watch_list *wl, uint32_t pid) 77{ 78 int idx = client_watch_find(wl, pid); 79 if (idx == -1) { 80 return 0; 81 } 82 cspacepath_t *ep = (cspacepath_t *) cvector_get(&wl->endpointNotifyList, idx); 83 assert(ep && ep->capPtr); 84 return ep->capPtr; 85} 86 87int 88client_watch(struct proc_watch_list *wl, uint32_t pid, seL4_CPtr notifyEP) 89{ 90 if (!notifyEP) { 91 return EINVALIDPARAM; 92 } 93 94 /* Allocate cspacepath_t structure. */ 95 cspacepath_t *cslot = kmalloc(sizeof(cspacepath_t)); 96 if (!cslot) { 97 ROS_ERROR("client_watch failed to malloc cslot structure. Procserv out of memory."); 98 return ENOMEM; 99 } 100 memset(cslot, 0, sizeof(cspacepath_t)); 101 102 /* Save the cap path, and take ownership of the given notify EP. */ 103 vka_cspace_make_path(&procServ.vka, notifyEP, cslot); 104 105 int idx = client_watch_find(wl, pid); 106 if (idx != -1) { 107 /* We are already watching this client. Unwatch first. */ 108 client_unwatch(wl, pid); 109 } 110 111 /* And finally, add to our watch list. */ 112 dprintf("Adding client_watch pid %d notifyEP 0x%x\n", pid, notifyEP); 113 cvector_add(&wl->clientList, (cvector_item_t) pid); 114 cvector_add(&wl->endpointNotifyList, (cvector_item_t) cslot); 115 assert(cvector_count(&wl->clientList) == cvector_count(&wl->endpointNotifyList)); 116 117 return ESUCCESS; 118} 119 120void 121client_unwatch(struct proc_watch_list *wl, uint32_t pid) 122{ 123 int idx = client_watch_find(wl, pid); 124 if (idx == -1) { 125 /* Not watched. Nothing to do. */ 126 return; 127 } 128 129 /* Free the endpoint and remove from watch list. */ 130 cspacepath_t *ep = (cspacepath_t *) cvector_get(&wl->endpointNotifyList, idx); 131 client_watch_free_ep_cslot(ep); 132 cvector_delete(&wl->endpointNotifyList, idx); 133 cvector_delete(&wl->clientList, idx); 134} 135 136void 137client_watch_notify_death_callback(struct proc_pcb *pcb, void *cookie) 138{ 139 uint32_t deathPID = (int) cookie; 140 assert(pcb && pcb->magic == REFOS_PCB_MAGIC); 141 142 if (pcb->pid == deathPID) { 143 /* Skip this PID; no point notifying the dying process of its own death. */ 144 return; 145 } 146 147 /* Retrieve the watch notify endpoint, if any. */ 148 seL4_CPtr ep = client_watch_get(&pcb->clientWatchList, deathPID); 149 if (!ep) { 150 /* This process isn't watching the dying client. */ 151 return; 152 } 153 154 dprintf("Notifying client %d [%s] of the death of %d with EP 0x%x...\n", 155 pcb->pid, pcb->debugProcessName, deathPID, ep); 156 if (!pcb->notificationBuffer) { 157 ROS_WARNING("Process %d [%s] is watching client with no notification buffer!", 158 pcb->pid, pcb->debugProcessName); 159 ROS_WARNING("This death notification will be ignored."); 160 return; 161 } 162 163 /* Construct the notification. */ 164 struct proc_notification exitNotification; 165 exitNotification.magic = PROCSERV_NOTIFICATION_MAGIC; 166 exitNotification.label = PROCSERV_NOTIFY_DEATH; 167 exitNotification.arg[0] = deathPID; 168 169 /* Append notification to watcher's notification buffer. */ 170 int error = rb_write(pcb->notificationBuffer, (char*)(&exitNotification), 171 sizeof(exitNotification)); 172 if (error) { 173 ROS_WARNING("Failed to write death fault notification to buffer."); 174 return; 175 } 176 177 /* Notify the pager of this fault. */ 178 dispatcher_notify(ep); 179 client_unwatch(&pcb->clientWatchList, deathPID); 180} 181