1/**
2 * \file
3 * \brief Inter-processor interrupt (IPI) notify mechanism.
4 */
5
6/*
7 * Copyright (c) 2007, 2008, 2010, ETH Zurich.
8 * All rights reserved.
9 *
10 * This file is distributed under the terms in the attached LICENSE file.
11 * If you do not find this file, copies can be found by writing to:
12 * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
13 */
14
15#include <kernel.h>
16#include <dispatch.h>
17#include <capabilities.h>
18#include <arch/x86/ipi_notify.h>
19#include <arch/x86/global.h>
20#include <arch/x86/apic.h>
21#include <barrelfish_kpi/syscalls.h>
22#include <barrelfish_kpi/paging_arch.h>
23
24// Max number of notification IDs that fit into message passing buffers
25#define MAX_CHANIDS             65535
26
27/// User-space endpoints awaiting notifications
28static struct cte endpoints[MAX_CHANIDS];
29
30#define NOTIFY_FIFO_SIZE        64
31#define NOTIFY_FIFO_BYTES       (NOTIFY_FIFO_SIZE * sizeof(uint64_t))
32
33/// Page used for notify FIFOs
34static char my_notify_page[NOTIFY_FIFO_BYTES * MAX_COREID];
35
36// Private head/tail pointers for notify FIFOs
37static uint64_t notifyhead[MAX_COREID];
38static uint64_t notifytail[MAX_COREID];
39
40static uint8_t my_arch_id;
41
42errval_t ipi_register_notification(capaddr_t ep, int chanid)
43{
44    struct cte *recv;
45    errval_t err;
46
47    err = caps_lookup_slot(&dcb_current->cspace.cap, ep,
48                           2, &recv, CAPRIGHTS_WRITE);
49    if (err_is_fail(err)) {
50        return err_push(err, SYS_ERR_IRQ_LOOKUP);
51    }
52
53    assert(recv != NULL);
54
55    // Return w/error if cap is not an endpoint
56    if(recv->cap.type != ObjType_EndPoint) {
57        return SYS_ERR_IRQ_NOT_ENDPOINT;
58    }
59
60    // Return w/error if no listener on endpoint
61    if(recv->cap.u.endpoint.listener == NULL) {
62        return SYS_ERR_IRQ_NO_LISTENER;
63    }
64
65    if(chanid < MAX_CHANIDS) {
66        // check that we don't overwrite someone else's handler
67        if (endpoints[chanid].cap.type != ObjType_Null) {
68            printf("kernel: installing new handler for IPI notification %d\n", chanid);
69        }
70        return caps_copy_to_cte(&endpoints[chanid], recv, false, 0, 0);
71    } else {
72        return SYS_ERR_IRQ_INVALID;
73    }
74}
75
76void ipi_handle_notify(void)
77{
78    uint64_t val = 0;
79
80    for(coreid_t srccore = 0; srccore < MAX_COREID; srccore++) {
81        volatile uint64_t *fifo = (void *)&my_notify_page[NOTIFY_FIFO_BYTES * srccore];
82
83        if (global->notify[my_arch_id] == 0) {
84            panic("NO PCN for core %d!", my_arch_id);
85        }
86
87        // Which slot in the fifo to poll
88        uint64_t slot = notifytail[srccore] % NOTIFY_FIFO_SIZE;
89
90        while (fifo[slot] != 0) {
91            val = fifo[slot];
92
93            assert(endpoints[val].cap.type != ObjType_Null);
94            lmp_deliver_notification(&endpoints[val].cap);
95
96            fifo[slot] = 0; // ACK
97            notifytail[srccore]++;
98            slot = notifytail[srccore] % NOTIFY_FIFO_SIZE;
99        }
100    }
101
102    // XXX: Dispatch last listener -- should be done by scheduler
103    if(val != 0) {
104        dispatch(endpoints[val].cap.u.endpoint.listener);
105    }
106}
107
108struct sysret ipi_raise_notify(coreid_t coreid, uintptr_t chanid)
109{
110    char *notify_page = (char *)local_phys_to_mem(global->notify[coreid]);
111
112    if (notify_page == NULL || coreid >= MAX_COREID) {
113        printf("UMPNOTIFY ERROR!\n");
114        return SYSRET(SYS_ERR_ILLEGAL_INVOCATION);
115    }
116
117    // Locate our private notification fifo and head ptr
118    volatile uint64_t *fifo = (void *)&notify_page[my_arch_id * NOTIFY_FIFO_BYTES];
119    uint64_t slot = notifyhead[coreid] % NOTIFY_FIFO_SIZE;
120
121    // Make sure the next slot is empty
122    if (fifo[slot] != 0) {
123        panic("FULL");
124    }
125
126    // Update notify fifo
127    fifo[slot] = (uint64_t)chanid;
128    notifyhead[coreid]++;
129
130    // Send IPI to dest kernel
131    apic_send_std_ipi(coreid, xapic_none, APIC_INTER_CORE_VECTOR);
132
133    return SYSRET(SYS_ERR_OK);
134}
135
136void ipi_notify_init(void)
137{
138    my_arch_id = apic_get_id();
139    // Publish the address of the notify page in the global kernel state
140    global->notify[my_arch_id] = local_phys_to_gen_phys(mem_to_local_phys((lvaddr_t)my_notify_page));
141}
142