[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Minios-devel] [UNIKRAFT PATCH v5 3/9] plat/linuxu: Add linuxu (x86_64) interrupts support



From: Costin Lupu <costin.lupu@xxxxxxxxx>

We use signals to emulate the behavior of interrupts on plat/linuxu.

To directly issue syscalls to the Linux kernel, we need to be
ABI-compatible with it. For some type definitions that are not fully
specified by POSIX, but that are used in syscalls (looking at you,
sigset_t and fd_set), we therefore provide our own internal definitions
instead of relying on those provided by a libc implementation that might
be POSIX-compliant, but not ABI-compatible.

Signed-off-by: Costin Lupu <costin.lupu@xxxxxxxxx>
Signed-off-by: Simon Kuenzer <simon.kuenzer@xxxxxxxxx>
Signed-off-by: Florian Schmidt <florian.schmidt@xxxxxxxxx>
---
 plat/linuxu/Makefile.uk                     |   1 +
 plat/linuxu/include/linuxu/signal.h         | 163 ++++++++++++++++
 plat/linuxu/include/linuxu/syscall-x86_64.h |   2 +
 plat/linuxu/include/linuxu/syscall.h        |  27 ++-
 plat/linuxu/irq.c                           | 199 ++++++++++++++++++++
 plat/linuxu/lcpu.c                          |  36 ++--
 6 files changed, 407 insertions(+), 21 deletions(-)
 create mode 100644 plat/linuxu/include/linuxu/signal.h
 create mode 100644 plat/linuxu/irq.c

diff --git a/plat/linuxu/Makefile.uk b/plat/linuxu/Makefile.uk
index 2de4b3a..c401f71 100644
--- a/plat/linuxu/Makefile.uk
+++ b/plat/linuxu/Makefile.uk
@@ -30,4 +30,5 @@ LIBLINUXUPLAT_SRCS-y              += 
$(LIBLINUXUPLAT_BASE)/console.c
 LIBLINUXUPLAT_SRCS-y              += $(LIBLINUXUPLAT_BASE)/shutdown.c
 LIBLINUXUPLAT_SRCS-y              += $(LIBLINUXUPLAT_BASE)/memory.c
 LIBLINUXUPLAT_SRCS-y              += $(LIBLINUXUPLAT_BASE)/lcpu.c
+LIBLINUXUPLAT_SRCS-y              += $(LIBLINUXUPLAT_BASE)/irq.c
 LIBLINUXUPLAT_SRCS-y              += $(LIBLINUXUPLAT_BASE)/time.c
diff --git a/plat/linuxu/include/linuxu/signal.h 
b/plat/linuxu/include/linuxu/signal.h
new file mode 100644
index 0000000..84234f6
--- /dev/null
+++ b/plat/linuxu/include/linuxu/signal.h
@@ -0,0 +1,163 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Authors: Costin Lupu <costin.lupu@xxxxxxxxx>
+ *
+ * Copyright (c) 2018, NEC Europe Ltd., NEC Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY.
+ */
+
+/* This file contains definitions for interfacing with the Linux kernel. To
+ * do so, it needs to use ABI-compatible definitions of types such as sigset_t
+ * which are not exactly defined by the POSIX standard and hence can vary
+ * between libc implementations. This is why we provide this file for use
+ * in the plat/linuxu code instead of including a libc-provided signal.h.
+ */
+
+#ifndef __SIGNAL_H__
+#define __SIGNAL_H__
+
+/* Signal numbers */
+#define SIGALRM       14
+
+/* type definitions */
+typedef unsigned long k_sigset_t;
+typedef struct {
+       unsigned long fds_bits[128 / sizeof(long)];
+} k_fd_set;
+
+/* sigaction */
+typedef void (*uk_sighandler_t)(int);
+typedef void (*uk_sigrestore_t)(void);
+
+struct uk_sigaction {
+       uk_sighandler_t sa_handler;
+       int sa_flags;
+       uk_sigrestore_t sa_restorer;
+       k_sigset_t sa_mask;
+};
+
+/* sigaction flags */
+#define SA_SIGINFO      0x00000004
+#define SA_RESTORER     0x04000000
+
+
+/* Signal enabling/disabling definitions (sigprocmask) */
+#ifndef SIG_BLOCK
+#define SIG_BLOCK     0
+#endif
+#ifndef SIG_UNBLOCK
+#define SIG_UNBLOCK   1
+#endif
+#ifndef SIG_SETMASK
+#define SIG_SETMASK   2
+#endif
+
+/* sigset utils */
+#define SIGSET_WORDS_NUM    (sizeof(k_sigset_t) / sizeof(unsigned long))
+
+#define k_sigemptyset(set) \
+       ({ \
+               unsigned int __count = 0; \
+               unsigned long *__set = (set); \
+               while (__count++ < SIGSET_WORDS_NUM) \
+                       *__set++ = 0; \
+               0; \
+       })
+
+#define k_sigfillset(set) \
+       ({ \
+               unsigned int __count = 0; \
+               unsigned long *__set = (set); \
+               while (__count++ < SIGSET_WORDS_NUM) \
+                       *__set++ = ~0UL; \
+               0; \
+       })
+
+#define k_sigisemptyset(set) \
+       ({ \
+               unsigned int __count = 0; \
+               const unsigned long *__set = (set); \
+               int __ret = __set[__count++]; \
+               while (!__ret && __count < SIGSET_WORDS_NUM) \
+                       __ret = __set[__count++]; \
+               __ret == 0; \
+       })
+
+
+#define sig_word_idx(sig) \
+       (((sig) - 1) / (8 * sizeof(unsigned long)))
+
+#define sig_word_mask(sig) \
+       (1UL << (((sig) - 1) % (8 * sizeof(unsigned long))))
+
+
+#define k_sigaddset(set, sig) \
+       ({ \
+               unsigned long __word = sig_word_idx(sig); \
+               unsigned long __mask = sig_word_mask(sig); \
+               unsigned long *__set = (set); \
+               __set[__word] |= __mask; \
+               0; \
+       })
+
+#define k_sigdelset(set, sig) \
+       ({ \
+               unsigned long __word = sig_word_idx(sig); \
+               unsigned long __mask = sig_word_mask(sig); \
+               unsigned long *__set = (set); \
+               __set[__word] &= ~__mask; \
+               0; \
+       })
+
+#define k_sigismember(set, sig) \
+       ({ \
+               unsigned long __word = sig_word_idx(sig); \
+               unsigned long __mask = sig_word_mask(sig); \
+               unsigned long *__set = (set); \
+               __set[__word] & __mask ? 1 : 0; \
+       })
+
+
+/* Signal event definitions */
+typedef union uk_sigval {
+       int sival_int;
+       void *sival_ptr;
+} uk_sigval_t;
+
+typedef struct uk_sigevent {
+       uk_sigval_t sigev_value;
+       int sigev_signo;
+       int sigev_notify;
+
+       /* We aren't interested now in what follows here */
+       int pad[64];
+
+} uk_sigevent_t;
+
+#endif /* __SIGNAL_H__ */
diff --git a/plat/linuxu/include/linuxu/syscall-x86_64.h 
b/plat/linuxu/include/linuxu/syscall-x86_64.h
index c3c4550..09e6cae 100644
--- a/plat/linuxu/include/linuxu/syscall-x86_64.h
+++ b/plat/linuxu/include/linuxu/syscall-x86_64.h
@@ -44,6 +44,8 @@
 #define __SC_CLOSE   3
 #define __SC_MMAP    9
 #define __SC_MUNMAP 11
+#define __SC_RT_SIGACTION   13
+#define __SC_RT_SIGPROCMASK 14
 #define __SC_IOCTL  16
 #define __SC_EXIT   60
 #define __SC_PSELECT6 270
diff --git a/plat/linuxu/include/linuxu/syscall.h 
b/plat/linuxu/include/linuxu/syscall.h
index 7ebc9c0..5f2ee61 100644
--- a/plat/linuxu/include/linuxu/syscall.h
+++ b/plat/linuxu/include/linuxu/syscall.h
@@ -38,7 +38,7 @@
 
 #include <time.h>
 #include <sys/types.h>
-#include <sys/select.h>
+#include <linuxu/signal.h>
 
 #if defined __X86_64__
 #include <linuxu/syscall-x86_64.h>
@@ -80,7 +80,7 @@ static inline int sys_exit(int status)
 #define PROT_WRITE    (0x2)
 #define PROT_EXEC     (0x4)
 static inline void *sys_mmap(void *addr, size_t len, int prot, int flags,
-              int fd, off_t offset)
+               int fd, off_t offset)
 {
        return (void *) syscall6(__SC_MMAP,
                                 (long) (addr),
@@ -95,8 +95,29 @@ static inline void *sys_mmap(void *addr, size_t len, int 
prot, int flags,
        sys_mmap((addr), (len), (PROT_READ | PROT_WRITE), \
                 (MAP_SHARED | MAP_ANONYMOUS), -1, 0)
 
+
+static inline int sys_sigaction(int signum, const struct uk_sigaction *action,
+               struct uk_sigaction *oldaction)
+{
+       return (int) syscall4(__SC_RT_SIGACTION,
+                             (long) signum,
+                             (long) action,
+                             (long) oldaction,
+                             sizeof(k_sigset_t));
+}
+
+static inline int sys_sigprocmask(int how,
+               const k_sigset_t *set, k_sigset_t *oldset)
+{
+       return (int) syscall4(__SC_RT_SIGPROCMASK,
+                             (long) how,
+                             (long) set,
+                             (long) oldset,
+                             sizeof(k_sigset_t));
+}
+
 static inline int sys_pselect6(int nfds,
-               fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
+               k_fd_set *readfds, k_fd_set *writefds, k_fd_set *exceptfds,
                const struct timespec *timeout, const void *sigmask)
 {
        return (int) syscall6(__SC_PSELECT6,
diff --git a/plat/linuxu/irq.c b/plat/linuxu/irq.c
new file mode 100644
index 0000000..bc21d3f
--- /dev/null
+++ b/plat/linuxu/irq.c
@@ -0,0 +1,199 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Authors: Costin Lupu <costin.lupu@xxxxxxxxx>
+ *
+ * Copyright (c) 2018, NEC Europe Ltd., NEC Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY.
+ */
+#include <string.h>
+#include <uk/alloc.h>
+#include <uk/list.h>
+#include <uk/plat/lcpu.h>
+#include <uk/plat/irq.h>
+#include <uk/assert.h>
+#include <linuxu/syscall.h>
+#include <linuxu/signal.h>
+
+#define IRQS_NUM    16
+
+/* IRQ handlers declarations */
+struct irq_handler {
+       irq_handler_func_t func;
+       void *arg;
+
+       struct uk_sigaction oldaction;
+
+       UK_SLIST_ENTRY(struct irq_handler) entries;
+};
+
+UK_SLIST_HEAD(irq_handler_head, struct irq_handler);
+static struct irq_handler_head irq_handlers[IRQS_NUM];
+
+static struct uk_alloc *allocator;
+static k_sigset_t handled_signals_set;
+static unsigned long irq_enabled;
+
+void ukplat_lcpu_enable_irq(void)
+{
+       int rc;
+
+       rc = sys_sigprocmask(SIG_UNBLOCK, &handled_signals_set, NULL);
+       if (unlikely(rc != 0))
+               UK_CRASH("Failed to unblock signals (%d)\n", rc);
+
+       irq_enabled = 1;
+}
+
+void ukplat_lcpu_disable_irq(void)
+{
+       int rc;
+
+       rc = sys_sigprocmask(SIG_BLOCK, &handled_signals_set, NULL);
+       if (unlikely(rc != 0))
+               UK_CRASH("Failed to block signals (%d)\n", rc);
+
+       irq_enabled = 0;
+}
+
+int ukplat_lcpu_irqs_disabled(void)
+{
+       return (irq_enabled == 0);
+}
+
+unsigned long ukplat_lcpu_save_irqf(void)
+{
+       unsigned long flags = irq_enabled;
+
+       if (irq_enabled)
+               ukplat_lcpu_disable_irq();
+
+       return flags;
+}
+
+void ukplat_lcpu_restore_irqf(unsigned long flags)
+{
+       if (flags) {
+               if (!irq_enabled)
+                       ukplat_lcpu_enable_irq();
+
+       } else if (irq_enabled)
+               ukplat_lcpu_disable_irq();
+}
+
+void ukplat_lcpu_irqs_handle_pending(void)
+{
+       /* TO BE DONE */
+}
+
+void __restorer(void);
+asm("__restorer:mov $15,%rax\nsyscall");
+
+static void _irq_handle(int irq)
+{
+       struct irq_handler *h;
+       int handled = 0;
+
+       UK_ASSERT(irq >= 0 && irq < IRQS_NUM);
+
+       UK_SLIST_FOREACH(h, &irq_handlers[irq], entries) {
+               if (h->func(h->arg) == 1) {
+                       handled = 1;
+                       break;
+               }
+       }
+
+       if (!handled)
+               UK_CRASH("Unhandled irq=%d\n", irq);
+}
+
+int ukplat_irq_register(unsigned long irq, irq_handler_func_t func, void *arg)
+{
+       struct irq_handler *h;
+       struct uk_sigaction action;
+       k_sigset_t set;
+       unsigned long flags;
+       int rc;
+
+       if (irq >= IRQS_NUM)
+               return -EINVAL;
+
+       /* New handler */
+       h = uk_malloc(allocator, sizeof(struct irq_handler));
+       if (!h)
+               return -ENOMEM;
+       h->func = func;
+       h->arg = arg;
+
+       /* Register signal action */
+       memset(&action, 0, sizeof(action));
+       action.sa_handler = _irq_handle;
+       action.sa_flags = SA_RESTORER;
+       action.sa_restorer = __restorer;
+
+       rc = sys_sigaction((int) irq, &action, &h->oldaction);
+       if (rc != 0)
+               goto err;
+
+       flags = ukplat_lcpu_save_irqf();
+       UK_SLIST_INSERT_HEAD(&irq_handlers[irq], h, entries);
+       ukplat_lcpu_restore_irqf(flags);
+
+       /* Unblock the signal */
+       k_sigemptyset(&set);
+       k_sigaddset(&set, irq);
+
+       rc = sys_sigprocmask(SIG_UNBLOCK, &set, NULL);
+       if (unlikely(rc != 0))
+               UK_CRASH("Failed to unblock signals: %d\n", rc);
+
+       /* Add to our handled signals set */
+       k_sigaddset(&handled_signals_set, irq);
+
+       return 0;
+
+err:
+       uk_free(allocator, h);
+       return -rc;
+}
+
+int ukplat_irq_init(struct uk_alloc *a)
+{
+       UK_ASSERT(!irq_enabled);
+       UK_ASSERT(!allocator);
+
+       allocator = a;
+
+       /* Clear list head */
+       for (int i = 0; i < IRQS_NUM; i++)
+               UK_SLIST_INIT(&irq_handlers[i]);
+
+       k_sigemptyset(&handled_signals_set);
+
+       return 0;
+}
diff --git a/plat/linuxu/lcpu.c b/plat/linuxu/lcpu.c
index 42f5690..afb1d7e 100644
--- a/plat/linuxu/lcpu.c
+++ b/plat/linuxu/lcpu.c
@@ -33,36 +33,36 @@
  */
 
 #include <stdlib.h>
+#include <errno.h>
+#include <uk/plat/lcpu.h>
+#include <_time.h>
 #include <linuxu/syscall.h>
 #include <uk/print.h>
-#include <uk/plat/lcpu.h>
 
-void ukplat_lcpu_halt(void)
+static void do_pselect(struct timespec *timeout)
 {
        int ret;
        int nfds = 0;
-       fd_set *readfds = NULL;
-       fd_set *writefds = NULL;
-       fd_set *exceptfds = NULL;
+       k_fd_set *readfds = NULL;
+       k_fd_set *writefds = NULL;
+       k_fd_set *exceptfds = NULL;
 
-       ret = sys_pselect6(nfds, readfds, writefds, exceptfds, NULL, NULL);
-       if (ret < 0)
+       ret = sys_pselect6(nfds, readfds, writefds, exceptfds, timeout, NULL);
+       if (ret < 0 && ret != -EINTR)
                uk_printd(DLVL_WARN, "Failed to halt LCPU: %d\n", ret);
 }
 
-void ukplat_lcpu_halt_to(unsigned long millis)
+void halt(void)
+{
+       do_pselect(NULL);
+}
+
+void time_block_until(__snsec until)
 {
-       int ret;
-       int nfds = 0;
-       fd_set *readfds = NULL;
-       fd_set *writefds = NULL;
-       fd_set *exceptfds = NULL;
        struct timespec timeout;
 
-       timeout.tv_sec  = millis / 1000;
-       timeout.tv_nsec = millis % 1000 * 1000000;
+       timeout.tv_sec  = until / ukarch_time_sec_to_nsec(1);
+       timeout.tv_nsec = until % ukarch_time_sec_to_nsec(1);
 
-       ret = sys_pselect6(nfds, readfds, writefds, exceptfds, &timeout, NULL);
-       if (ret < 0)
-               uk_printd(DLVL_WARN, "Failed to halt LCPU: %d\n", ret);
+       do_pselect(&timeout);
 }
-- 
2.18.0


_______________________________________________
Minios-devel mailing list
Minios-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/minios-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.