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