The following changes since commit fad2f1e22bbe4a7c8d9f5fb9df7ea4f5b563bd9b:
Allow freeaddrinfo(NULL) (XCC) (2015-10-14 17:14:45 -0400)
are available in the git repository at:
g...@github.com:dlibenzi/akaros user_memory_access
for you to fetch changes up to 1e38a5455115b6ef1dd1b2505d38182b9d9adc75:
Added new kernel test case (2015-10-15 17:34:50 -0700)
----------------------------------------------------------------
Davide Libenzi (5):
Added heapsort utility function to the lib/ framework.
Added safe user memory access APIs
Plugged the exception handling code
Fixed a warning in the copy to/from user code.
Added new kernel test case
kern/arch/x86/trap.c | 33 ++++++++-
kern/arch/x86/uaccess.h | 149 ++++++++++++++++++++++++++++++++++++++++
kern/include/ex_table.h | 9 +++
kern/include/sort.h | 9 +++
kern/lib/Kbuild | 1 +
kern/lib/sort.c | 41 +++++++++++
kern/src/Kbuild | 1 +
kern/src/ex_table.c | 62 +++++++++++++++++
kern/src/init.c | 2 +
kern/src/ktest/Kconfig.postboot | 5 ++
kern/src/ktest/pb_ktests.c | 17 +++++
11 files changed, 326 insertions(+), 3 deletions(-)
create mode 100644 kern/arch/x86/uaccess.h
create mode 100644 kern/include/ex_table.h
create mode 100644 kern/include/sort.h
create mode 100644 kern/lib/sort.c
create mode 100644 kern/src/ex_table.c
diff --git a/kern/arch/x86/trap.c b/kern/arch/x86/trap.c
index e31b559..4b65310 100644
--- a/kern/arch/x86/trap.c
+++ b/kern/arch/x86/trap.c
@@ -16,6 +16,7 @@
#include <syscall.h>
#include <kdebug.h>
#include <kmalloc.h>
+#include <ex_table.h>
#include <arch/mptables.h>
taskstate_t ts;
@@ -29,6 +30,18 @@ pseudodesc_t idt_pd;
struct irq_handler *irq_handlers[NUM_IRQS];
spinlock_t irq_handler_wlock = SPINLOCK_INITIALIZER_IRQSAVE;
+static bool try_handle_exception_fixup(struct hw_trapframe *hw_tf)
+{
+ uintptr_t fixup_ip;
+
+ if (in_kernel(hw_tf) && ((fixup_ip = get_fixup_ip(hw_tf->tf_rip)) != 0)) {
+ hw_tf->tf_rip = fixup_ip;
+ return true;
+ }
+
+ return false;
+}
+
const char *x86_trapname(int trapno)
{
static const char *const excnames[] = {
@@ -229,6 +242,9 @@ static bool __handle_page_fault(struct hw_trapframe *hw_tf, unsigned long *aux)
enable_irq();
if (!pcpui->cur_proc) {
+ if (try_handle_exception_fixup(hw_tf))
+ return TRUE;
+
/* still catch KPFs */
assert((hw_tf->tf_cs & 3) == 0);
print_trapframe(hw_tf);
@@ -248,6 +264,9 @@ static bool __handle_page_fault(struct hw_trapframe *hw_tf, unsigned long *aux)
if (in_kernel(hw_tf))
pcpui->__lock_checking_enabled++;
if (err) {
+ if (try_handle_exception_fixup(hw_tf))
+ return TRUE;
+
if (in_kernel(hw_tf)) {
print_trapframe(hw_tf);
backtrace_kframe(hw_tf);
@@ -285,6 +304,8 @@ static void trap_dispatch(struct hw_trapframe *hw_tf)
struct per_cpu_info *pcpui;
bool handled = TRUE;
unsigned long aux = 0;
+ uintptr_t fixup_ip;
+
// Handle processor exceptions.
switch(hw_tf->tf_trapno) {
case T_NMI:
@@ -342,7 +363,8 @@ static void trap_dispatch(struct hw_trapframe *hw_tf)
handled = __handle_page_fault(hw_tf, &aux);
break;
case T_FPERR:
- handle_fperr(hw_tf);
+ if (!(handled = try_handle_exception_fixup(hw_tf)))
+ handle_fperr(hw_tf);
break;
case T_SYSCALL:
enable_irq();
@@ -362,8 +384,13 @@ static void trap_dispatch(struct hw_trapframe *hw_tf)
handled = FALSE;
}
}
- if (!handled)
- reflect_unhandled_trap(hw_tf->tf_trapno, hw_tf->tf_err, aux);
+
+ if (!handled) {
+ if ((fixup_ip = get_fixup_ip(hw_tf->tf_rip)) != 0)
+ hw_tf->tf_rip = fixup_ip;
+ else
+ reflect_unhandled_trap(hw_tf->tf_trapno, hw_tf->tf_err, aux);
+ }
}
/* Helper. For now, this copies out the TF to pcpui. Eventually, we should
diff --git a/kern/arch/x86/uaccess.h b/kern/arch/x86/uaccess.h
new file mode 100644
index 0000000..472d065
--- /dev/null
+++ b/kern/arch/x86/uaccess.h
@@ -0,0 +1,149 @@
+#ifndef ROS_INC_UACCESS_H
+#define ROS_INC_UACCESS_H
+
+#include <ros/errno.h>
+#include <stdint.h>
+
+#define ASM_STAC
+#define ASM_CLAC
+#define __m(x) *(x)
+
+struct fixup {
+ uint64_t insn;
+ uint64_t fixup;
+};
+
+#define _ASM_EXTABLE_INIT() \
+ asm volatile( \
+ " .pushsection \"__ex_table\",\"a\"\n" \
+ " .balign 16\n" \
+ " .popsection\n" \
+ : :)
+
+#define _ASM_EXTABLE(from,to) \
+ " .pushsection \"__ex_table\",\"a\"\n" \
+ " .balign 16\n" \
+ " .quad (" #from ") - .\n" \
+ " .quad (" #to ") - .\n" \
+ " .popsection\n"
+
+#define _ASM_EXTABLE_EX(from,to) \
+ " .pushsection \"__ex_table_ex\",\"a\"\n" \
+ " .balign 16\n" \
+ " .quad (" #from ") - .\n" \
+ " .quad (" #to ") - . + 0x7ffffff0\n" \
+ " .popsection\n"
+
+#define __put_user_asm(x, addr, err, itype, rtype, ltype, errret) \
+ asm volatile(ASM_STAC "\n" \
+ "1: mov"itype" %"rtype"1,%2\n" \
+ "2: " ASM_CLAC "\n" \
+ ".section .fixup,\"ax\"\n" \
+ "3: mov %3,%0\n" \
+ " jmp 2b\n" \
+ ".previous\n" \
+ _ASM_EXTABLE(1b, 3b) \
+ : "=r"(err) \
+ : ltype(x), "m" (__m(addr)), "i" (errret), "0" (err))
+
+#define __get_user_asm(x, addr, err, itype, rtype, ltype, errret) \
+ asm volatile(ASM_STAC "\n" \
+ "1: mov"itype" %2,%"rtype"1\n" \
+ "2: " ASM_CLAC "\n" \
+ ".section .fixup,\"ax\"\n" \
+ "3: mov %3,%0\n" \
+ " xor"itype" %"rtype"1,%"rtype"1\n" \
+ " jmp 2b\n" \
+ ".previous\n" \
+ _ASM_EXTABLE(1b, 3b) \
+ : "=r" (err), ltype(x) \
+ : "m" (__m(addr)), "i" (errret), "0" (err))
+
+#define __user_memcpy(dst, src, count, err, errret) \
+ asm volatile(ASM_STAC "\n" \
+ "1: rep movsb\n" \
+ "2: " ASM_CLAC "\n" \
+ ".section .fixup,\"ax\"\n" \
+ "3: mov %4,%0\n" \
+ " jmp 2b\n" \
+ ".previous\n" \
+ _ASM_EXTABLE(1b, 3b) \
+ : "=r"(err) \
+ : "D" (dst), "S" (src), "c" (count), "i" (errret), "0" (err))
+
+static inline int copy_to_user(void *dst, const void *src, unsigned int count)
+{
+ int err = 0;
+
+ if (!__builtin_constant_p(count))
+ __user_memcpy(dst, src, count, err, -EFAULT);
+ else {
+ switch (count) {
+ case 1:
+ __put_user_asm(*(const uint8_t *) src, (uint8_t *) dst, err, "b",
+ "b", "iq", -EFAULT);
+ break;
+ case 2:
+ __put_user_asm(*(const uint16_t *) src, (uint16_t *) dst, err, "w",
+ "w", "ir", -EFAULT);
+ break;
+ case 4:
+ __put_user_asm(*(const uint32_t *) src, (uint32_t *) dst, err, "l",
+ "k", "ir", -EFAULT);
+ break;
+ case 8:
+ __put_user_asm(*(const uint64_t *) src, (uint64_t *) dst, err, "q",
+ "", "er", -EFAULT);
+ break;
+ default:
+ __user_memcpy(dst, src, count, err, -EFAULT);
+ }
+ }
+
+ return err;
+}
+
+static inline int copy_from_user(void *dst, const void *src,
+ unsigned int count)
+{
+ int err = 0;
+
+ if (!__builtin_constant_p(count))
+ __user_memcpy(dst, src, count, err, -EFAULT);
+ else {
+ switch (count) {
+ case 1:
+ __get_user_asm(*(uint8_t *) dst, (const uint8_t *) src, err, "b",
+ "b", "=q", -EFAULT);
+ break;
+ case 2:
+ __get_user_asm(*(uint16_t *) dst, (const uint16_t *) src, err, "w",
+ "w", "=r", -EFAULT);
+ break;
+ case 4:
+ __get_user_asm(*(uint32_t *) dst, (const uint32_t *) src, err, "l",
+ "k", "=r", -EFAULT);
+ break;
+ case 8:
+ __get_user_asm(*(uint64_t *) dst, (const uint64_t *) src, err, "q",
+ "", "=r", -EFAULT);
+ break;
+ default:
+ __user_memcpy(dst, src, count, err, -EFAULT);
+ }
+ }
+
+ return err;
+}
+
+static inline uintptr_t ex_insn_addr(const struct fixup *x)
+{
+ return (uintptr_t) &x->insn + x->insn;
+}
+
+static inline uintptr_t ex_fixup_addr(const struct fixup *x)
+{
+ return (uintptr_t) &x->fixup + x->fixup;
+}
+
+#endif /* !ROS_INC_UACCESS_H */
diff --git a/kern/include/ex_table.h b/kern/include/ex_table.h
new file mode 100644
index 0000000..8ef2885
--- /dev/null
+++ b/kern/include/ex_table.h
@@ -0,0 +1,9 @@
+#ifndef ROS_INC_EX_TABLE_H
+#define ROS_INC_EX_TABLE_H
+
+#include <stdint.h>
+
+void exception_table_init(void);
+uintptr_t get_fixup_ip(uintptr_t xip);
+
+#endif /* ROS_INC_EX_TABLE_H */
diff --git a/kern/include/sort.h b/kern/include/sort.h
new file mode 100644
index 0000000..11b98b6
--- /dev/null
+++ b/kern/include/sort.h
@@ -0,0 +1,9 @@
+#ifndef ROS_INC_LIB_SORT_H
+#define ROS_INC_LIB_SORT_H
+
+#include <stdio.h>
+
+void sort(void *base, size_t count, size_t size,
+ int (*cmp)(const void *, const void *));
+
+#endif /* ROS_INC_LIB_SORT_H */
diff --git a/kern/lib/Kbuild b/kern/lib/Kbuild
index 1cb408f..aa4a004 100644
--- a/kern/lib/Kbuild
+++ b/kern/lib/Kbuild
@@ -1,2 +1,3 @@
+obj-y += sort.o
obj-y += zlib_deflate/
obj-y += zlib_inflate/
diff --git a/kern/lib/sort.c b/kern/lib/sort.c
new file mode 100644
index 0000000..ec448df
--- /dev/null
+++ b/kern/lib/sort.c
@@ -0,0 +1,41 @@
+
+#include <stdio.h>
+
+static void mem_swap(void *a, void *b, size_t size)
+{
+ for (; size > 0; size--, a++, b++) {
+ char tmp = *(char*) a;
+
+ *(char *) a = *(char *) b;
+ *(char *) b = tmp;
+ }
+}
+
+void sort(void *base, size_t count, size_t size,
+ int (*cmp)(const void *, const void *))
+{
+ ssize_t n = count * size, c;
+
+ for (ssize_t i = (count / 2 - 1) * size; i >= 0; i -= size) {
+ for (ssize_t r = i; r * 2 + size < n; r = c) {
+ c = r * 2 + size;
+ if (c < n - size && cmp(base + c, base + c + size) < 0)
+ c += size;
+ if (cmp(base + r, base + c) >= 0)
+ break;
+ mem_swap(base + r, base + c, size);
+ }
+ }
+
+ for (ssize_t i = n - size; i > 0; i -= size) {
+ mem_swap(base, base + i, size);
+ for (ssize_t r = 0; r * 2 + size < i; r = c) {
+ c = r * 2 + size;
+ if (c < i - size && cmp(base + c, base + c + size) < 0)
+ c += size;
+ if (cmp(base + r, base + c) >= 0)
+ break;
+ mem_swap(base + r, base + c, size);
+ }
+ }
+}
diff --git a/kern/src/Kbuild b/kern/src/Kbuild
index 0981c66..d2a9d18 100644
--- a/kern/src/Kbuild
+++ b/kern/src/Kbuild
@@ -26,6 +26,7 @@ obj-y += error.o
obj-$(CONFIG_ETH_AUDIO) += eth_audio.o
obj-y += event.o
obj-y += ext2fs.o
+obj-y += ex_table.o
obj-y += fdtap.o
obj-y += find_next_bit.o
obj-y += find_last_bit.o
diff --git a/kern/src/ex_table.c b/kern/src/ex_table.c
new file mode 100644
index 0000000..8f060a7
--- /dev/null
+++ b/kern/src/ex_table.c
@@ -0,0 +1,62 @@
+
+#include <ex_table.h>
+#include <arch/uaccess.h>
+#include <sort.h>
+
+extern struct fixup __start___ex_table;
+extern struct fixup __stop___ex_table;
+
+static int fixup_cmp(const void *f1, const void *f2)
+{
+ return ((const struct fixup *) f1)->insn <
+ ((const struct fixup *) f2)->insn ? -1: +1;
+}
+
+void exception_table_init(void)
+{
+ struct fixup *first = &__start___ex_table;
+ struct fixup *last = &__stop___ex_table;
+ uint64_t offset = 0;
+
+ for (struct fixup *fx = first; fx < last; fx++) {
+ fx->insn += offset;
+ offset += sizeof(fx->insn);
+ fx->fixup += offset;
+ offset += sizeof(fx->fixup);
+ }
+
+ sort(first, last - first, sizeof(*first), fixup_cmp);
+
+ offset = 0;
+ for (struct fixup *fx = first; fx < last; fx++) {
+ fx->insn -= offset;
+ offset += sizeof(fx->insn);
+ fx->fixup -= offset;
+ offset += sizeof(fx->fixup);
+ }
+
+ /* Avoid undefined __start___ex_table and __stop___ex_table errors when
+ * no code is using the exception table facility.
+ */
+ _ASM_EXTABLE_INIT();
+}
+
+uintptr_t get_fixup_ip(uintptr_t xip)
+{
+ const struct fixup *first = &__start___ex_table;
+ const struct fixup *last = &__stop___ex_table;
+
+ while (first <= last) {
+ const struct fixup *x = first + ((last - first) >> 1);
+ uintptr_t insn = ex_insn_addr(x);
+
+ if (insn < xip)
+ first = x + 1;
+ else if (insn > xip)
+ last = x - 1;
+ else
+ return (uintptr_t) ex_fixup_addr(x);
+ }
+
+ return 0;
+}
diff --git a/kern/src/init.c b/kern/src/init.c
index a795119..ba88555 100644
--- a/kern/src/init.c
+++ b/kern/src/init.c
@@ -29,6 +29,7 @@
#include <radix.h>
#include <mm.h>
#include <frontend.h>
+#include <ex_table.h>
#include <arch/init.h>
#include <bitmask.h>
@@ -64,6 +65,7 @@ void kernel_init(multiboot_info_t *mboot_info)
cons_init();
print_cpuinfo();
+ exception_table_init();
cache_init(); // Determine systems's cache properties
pmem_init(multiboot_kaddr);
kmem_cache_init(); // Sets up slab allocator
diff --git a/kern/src/ktest/Kconfig.postboot b/kern/src/ktest/Kconfig.postboot
index 90b61ca..70ef774 100644
--- a/kern/src/ktest/Kconfig.postboot
+++ b/kern/src/ktest/Kconfig.postboot
@@ -238,3 +238,8 @@ config TEST_u16pool
depends on PB_KTESTS
bool "u16 pool"
default n
+
+config TEST_uaccess
+ depends on PB_KTESTS
+ bool "Tests user memory access fault trapping"
+ default y
diff --git a/kern/src/ktest/pb_ktests.c b/kern/src/ktest/pb_ktests.c
index 5dfd003..1866b6e 100644
--- a/kern/src/ktest/pb_ktests.c
+++ b/kern/src/ktest/pb_ktests.c
@@ -6,6 +6,7 @@
#include <arch/mmu.h>
#include <arch/arch.h>
+#include <arch/uaccess.h>
#include <bitmask.h>
#include <smp.h>
@@ -2085,6 +2086,21 @@ bool test_u16pool(void)
return FALSE;
}
+bool test_uaccess(void)
+{
+ char buf[128] = { 0 };
+ char buf2[128] = { 0 };
+
+ if (copy_to_user((void *) UDATA, buf, sizeof(buf)) == 0)
+ return false;
+ if (copy_from_user(buf, (const void *) UDATA, sizeof(buf)) == 0)
+ return false;
+ if (copy_from_user(buf, buf2, sizeof(buf)) != 0)
+ return false;
+
+ return true;
+}
+
static struct ktest ktests[] = {
#ifdef CONFIG_X86
KTEST_REG(ipi_sending, CONFIG_TEST_ipi_sending),
@@ -2124,6 +2140,7 @@ static struct ktest ktests[] = {
KTEST_REG(alarm, CONFIG_TEST_alarm),
KTEST_REG(kmalloc_incref, CONFIG_TEST_kmalloc_incref),
KTEST_REG(u16pool, CONFIG_TEST_u16pool),
+ KTEST_REG(uaccess, CONFIG_TEST_uaccess),
};
static int num_ktests = sizeof(ktests) / sizeof(struct ktest);
linker_func_1(register_pb_ktests)