From af72bfd7a78ecf2513cdb63918fc7695705642e3 Mon Sep 17 00:00:00 2001 From: Christian Dietrich <christian.dietrich@informatik.uni-erlangen.de> Date: Mon, 24 Oct 2016 13:16:56 +0200 Subject: [PATCH] posix: port to LLVM 3.8/3.9 --- README.md | 57 ++++-------------------- arch/generic/CMakeLists.txt | 2 + arch/generic/timing-arch.cc | 4 ++ arch/i386/dispatch.cc | 2 +- arch/i386/idt.cc | 2 +- arch/i386/lapic.cc | 2 +- arch/i386/machine.cc | 7 +-- arch/i386/machine.h | 2 +- arch/i386/memutil.cc | 6 +++ arch/i386/output.h | 1 - arch/i386/reschedule-ast.cc | 2 +- arch/i386/serial.cc | 4 +- arch/i386/syscall.cc | 58 +++++++++++++------------ arch/patmos/timing-arch.cc | 2 + generator/attributor/main.cc | 10 ++++- generator/coder/syscall_full.py | 77 +++++++++++++++++++-------------- generator/extractor/main.cc | 33 +++++++++----- os/timing.h | 3 +- toolchain/detect | 3 +- toolchain/llvm-link.py | 2 +- 20 files changed, 142 insertions(+), 137 deletions(-) create mode 100644 arch/generic/timing-arch.cc diff --git a/README.md b/README.md index 8af0753..0381b1b 100644 --- a/README.md +++ b/README.md @@ -16,11 +16,7 @@ dependability as the first-class design goal targeting safety-critical, embedded applications, the system provides an OSEK/AUTOSAR-conform interface (currently ECC1). -Currently, dOSEK supports three platforms: - -- x86: 32-Bit version of dOSEK that runs on bare metal -- posix: 32-Bit version of dOSEK that runs on Linux/x86 -- arm: Port to the Panda Board/zedboard (currently WIP) +**This repository contains the PATMOS version of dOSEK used for SysWCET** For more information about the dOSEK concepts, see https://www4.cs.fau.de/Research/dOSEK/ @@ -28,56 +24,19 @@ For more information about the dOSEK concepts, see Software Requirements --------------------- - apt-get install binutils-dev build-essential clang-3.4 cmake \ - g++-multilib gcc-multilib git grub-common grub-pc-bin \ - llvm-3.3-dev llvm-3.3-runtime python-minimal python3 \ - python3-lxml python3-pyparsing python3-pip \ - qemu-system-x86 xorriso - - LLVM_CONFIG_PATH=/usr/bin/llvm-config-3.3 pip3 install llvmpy - -For the ARM version, you will additionally need - - gcc-arm-none-eabi gdb-arm-none-eabi qemu-system-arm +For Building -------- dOSEK uses cmake as a build system. -We recommend to make an out-of-source build -To build and run all test-cases you have to type: - - mkdir build; cd build - <Path to dOSEK>/new_build_env.py - make build_and_test - -To get help about the available targets use +We recommend to make an out-of-source build: - make help # long help - make h # short help -`after_first_checkout.sh` is only necessary, if you are going to -contribute via our gerrit code review system. - -Docker Images -------------- - -[](https://registry.hub.docker.com/u/danceos/dosek-base/) - - -We provide [docker.io](http://www.docker.com) images for a basic -build environment. These images provide an SSH port and access to an -Ubuntu machine that contains all build dependencies. You can either -build the images yourself with - - cd scripts/docker; make - make run - make ssh - -or you can pull it directly from the docker Hub + mkdir build; cd build + ../new_build_env.py -a patmos --generate-pml=true - docker pull danceos/dosek-base - cd scripts/docker; make run; make ssh +To evaluate the Worst-Case Response Time of the aborted computation +benchmark, please run: -In either cases, the password is `dosek`. In the default -configuration, no SSH port will be exposed to the outside world. + make wcet-bench-timing-aborted_computation CIRCUIT=0 VERBOSE=2 diff --git a/arch/generic/CMakeLists.txt b/arch/generic/CMakeLists.txt index d650105..fbc3551 100644 --- a/arch/generic/CMakeLists.txt +++ b/arch/generic/CMakeLists.txt @@ -1,5 +1,7 @@ set(SRCS startup.cc + timing-arch.cc + ) # Add to include directories diff --git a/arch/generic/timing-arch.cc b/arch/generic/timing-arch.cc new file mode 100644 index 0000000..e7adca6 --- /dev/null +++ b/arch/generic/timing-arch.cc @@ -0,0 +1,4 @@ +extern "C" int __attribute__((weak)) arch_timing_get() { + static int time; + return ++time; +} diff --git a/arch/i386/dispatch.cc b/arch/i386/dispatch.cc index ea13a78..1054bf0 100644 --- a/arch/i386/dispatch.cc +++ b/arch/i386/dispatch.cc @@ -30,7 +30,7 @@ volatile uint16_t dispatch_task; * This interrupt is called after the next task is determined by the scheduler in ring 3 * and performs the actual dispatching in ring 0. */ -IRQ_HANDLER(IRQ_DISPATCH) { +ISR(IRQ_DISPATCH) { #ifdef CONFIG_DEPENDABILITY_ENCODED // decode task ID uint16_t id = dispatch_task.decode(); diff --git a/arch/i386/idt.cc b/arch/i386/idt.cc index 7432678..9c86309 100644 --- a/arch/i386/idt.cc +++ b/arch/i386/idt.cc @@ -42,7 +42,7 @@ ISR(unhandled) { // NMI error handler -IRQ_HANDLER(2) { +ISR(2) { CALL_HOOK(FaultDetectedHook, TRAPdetected, 0, 0); Machine::halt(); diff --git a/arch/i386/lapic.cc b/arch/i386/lapic.cc index 44b8c7b..fc10a1e 100644 --- a/arch/i386/lapic.cc +++ b/arch/i386/lapic.cc @@ -30,7 +30,7 @@ void LAPIC::init() { /** \brief Spurious interrupt handler */ -IRQ_HANDLER(255) { +ISR(255) { // count spurious interrupt spurious_interrupts++; diff --git a/arch/i386/machine.cc b/arch/i386/machine.cc index 2ab245f..1f119f4 100644 --- a/arch/i386/machine.cc +++ b/arch/i386/machine.cc @@ -7,10 +7,8 @@ using namespace arch; // new syscall to trigger interrupt using local APIC // for now, any function can be called using syscall(), // so it is easy to add this for this test. -noinline void __OS_trigger_syscall() { - uint8_t irq; - asm volatile("" : "=a"(irq)); - Machine::trigger_interrupt(irq); +noinline void __OS_trigger_syscall(uint32_t irq) { + Machine::trigger_interrupt((uint8_t) irq); } void Machine::trigger_interrupt_from_user(uint8_t irq) { @@ -25,4 +23,3 @@ void Machine::trigger_interrupt_from_user(uint8_t irq) { while (x < 100) x++; } #endif - diff --git a/arch/i386/machine.h b/arch/i386/machine.h index 962c1a8..ab614da 100644 --- a/arch/i386/machine.h +++ b/arch/i386/machine.h @@ -293,6 +293,6 @@ struct Machine #define __asm_label(a) #a #define _asm_label(a) __asm_label(a) -#define asm_label(label) asm volatile (".asm_label." label "_%=:" :: "m" (*(void *)0)) +#define asm_label(label) asm volatile (".L." label "_%=:" :: "m" (*(int *)0)) #endif // __MACHINE_H__ diff --git a/arch/i386/memutil.cc b/arch/i386/memutil.cc index d1f15bf..a67769b 100644 --- a/arch/i386/memutil.cc +++ b/arch/i386/memutil.cc @@ -37,3 +37,9 @@ extern "C" void memset(void *dest, int pat, size_t size) destination[i] = pattern; } } + +extern "C" int strlen(char *dst) { + int i = 0; + while (*dst) { i++; dst++; } + return i; +} diff --git a/arch/i386/output.h b/arch/i386/output.h index 68379ab..f70df52 100644 --- a/arch/i386/output.h +++ b/arch/i386/output.h @@ -7,7 +7,6 @@ #define __OUTPUT_H__ #include "arch/generic/ostream.h" - #ifndef FAIL #if DEBUG diff --git a/arch/i386/reschedule-ast.cc b/arch/i386/reschedule-ast.cc index a5a03e4..b6b5c14 100644 --- a/arch/i386/reschedule-ast.cc +++ b/arch/i386/reschedule-ast.cc @@ -22,7 +22,7 @@ static const uint32_t iret_schedule[] = { }; #endif -IRQ_HANDLER(IRQ_RESCHEDULE) { +ISR(IRQ_RESCHEDULE) { #ifndef CONFIG_ARCH_PRIVILEGE_ISOLATION // If we have no privilege isolation, we have to switch to the os_stack asm volatile ("mov %0, %%esp;" :: "i"(&(_estack_os) - 2048)); diff --git a/arch/i386/serial.cc b/arch/i386/serial.cc index 09ab381..878f23b 100644 --- a/arch/i386/serial.cc +++ b/arch/i386/serial.cc @@ -18,10 +18,10 @@ Serial::Serial(ports port) : PORT((uint16_t) port) { void Serial::putchar(char character) { // wait while transmit not empty - bool empty; + bool empty; int i = 10000; do { empty = (inb(PORT + 5) & 0x20); - } while(!empty); + } while(!empty && --i > 0) ; outb(PORT, character); } diff --git a/arch/i386/syscall.cc b/arch/i386/syscall.cc index e1fe60c..0474dd2 100644 --- a/arch/i386/syscall.cc +++ b/arch/i386/syscall.cc @@ -15,7 +15,7 @@ namespace arch { /* Syscalls that run in userspace, this are indirect syscall, they are intended for longer syscalls */ -extern "C" __attribute__((naked)) void sysenter_syscall() { +extern "C" /*__attribute__((naked)) */ void sysenter_syscall() { // get arguments from registers void *fun, *sp; uint32_t arg1, arg2, arg3; @@ -51,45 +51,49 @@ extern "C" __attribute__((naked)) void sysenter_syscall() { } -/** \brief Syscall interrupt handler. This handler is used for direct - syscalls that do not run in userspace, but in kernelmode */ -IRQ_HANDLER(IRQ_SYSCALL) { - // get arguments from registers - // also, store pointer to context in %esi before we change %esp - void* fun; - uint32_t arg1, arg2, arg3; - asm volatile("" : "=a"(arg1), "=b"(arg2), "=S"(arg3), "=d"(fun)); - // block ISR2s by raising APIC task priority - LAPIC::set_task_prio(128); + extern "C" void irq_syscall_handler(void * fun, uint32_t arg1) { + // block ISR2s by raising APIC task priority + LAPIC::set_task_prio(128); - // reenable ISR1s - Machine::enable_interrupts(); + // reenable ISR1s + Machine::enable_interrupts(); #ifdef CONFIG_ARCH_MPU - // save page directory - uint32_t pd; - asm volatile("mov %%cr3, %0" : "=D"(pd)); + // save page directory + uint32_t pd; + asm volatile("mov %%cr3, %0" : "=D"(pd)); - // change to OS page directory - PageDirectory::enable(pagedir_os); + // change to OS page directory + PageDirectory::enable(pagedir_os); #endif - // call syscall function with arguments - asm volatile("call *%0" :: "r"(fun), "a"(arg1), "b"(arg2), "S"(arg3)); + // call syscall function with arguments + ((void (*)(uint32_t))fun)(arg1); #ifdef CONFIG_ARCH_MPU - // restore page directory - asm volatile("mov %0, %%cr3" :: "D"(pd)); + // restore page directory + PageDirectory::enable(pd); #endif - // reenable ISR2s by resetting APIC task priority - Machine::disable_interrupts(); - LAPIC::set_task_prio(0); + // reenable ISR2s by resetting APIC task priority + Machine::disable_interrupts(); + LAPIC::set_task_prio(0); - // return from interrupt and proceed with caller in ring 3 - Machine::return_from_interrupt(); +} + +/** \brief Syscall interrupt handler. This handler is used for direct + syscalls that do not run in userspace, but in kernelmode */ +IRQ_HANDLER(IRQ_SYSCALL) { + asm volatile( + "push %eax;" + "push %edx;" + "call irq_syscall_handler;" + "pop %eax;" + "pop %edx;" + "iret;" + ); } diff --git a/arch/patmos/timing-arch.cc b/arch/patmos/timing-arch.cc index af3829b..a76a66a 100644 --- a/arch/patmos/timing-arch.cc +++ b/arch/patmos/timing-arch.cc @@ -3,6 +3,8 @@ #include "irq.h" #include "machine.h" +int __attribute__((weak)) timing_print() {}; + void timing_dump_trap(const arch::IRQ::Context & x) { (void) x; diff --git a/generator/attributor/main.cc b/generator/attributor/main.cc index c1f2bf1..809fc90 100644 --- a/generator/attributor/main.cc +++ b/generator/attributor/main.cc @@ -14,7 +14,7 @@ #include <llvm/Config/llvm-config.h> -#if LLVM_VERSION_MAJOR >= 3 && LLVM_VERSION_MINOR >= 6 +#if LLVM_VERSION_MAJOR >= 3 && LLVM_VERSION_MINOR >= 9 #undef LLVM_LEGACY #else #define LLVM_LEGACY 1 @@ -110,10 +110,16 @@ static inline std::unique_ptr<Module> LoadFile(const char *argv0, const std::str int main(int argc, char **argv) { // Print a stack trace if we signal out. +#ifdef LLVM_LEGACY sys::PrintStackTraceOnErrorSignal(); + LLVMContext &Context = getGlobalContext(); +#else + sys::PrintStackTraceOnErrorSignal(argv[0]); + LLVMContext Context; +#endif PrettyStackTraceProgram X(argc, argv); - LLVMContext &Context = getGlobalContext(); + llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. cl::ParseCommandLineOptions(argc, argv, "llvm dosek attributor\n"); diff --git a/generator/coder/syscall_full.py b/generator/coder/syscall_full.py index 40bde30..0c99a72 100644 --- a/generator/coder/syscall_full.py +++ b/generator/coder/syscall_full.py @@ -200,7 +200,9 @@ class FullSystemCalls(BaseCoder): kernelspace.add(Statement("%s.do_tick()" % counter.impl.name)) for alarm in self.system_graph.alarms: if alarm.conf.counter == counter: - kernelspace.add(Statement("AlarmCheck%s()" % alarm.impl.name)) + alarm.carried_syscall.function = abb.function + check_alarm = self.alarms(self).generate_check_alarm_softcounter(alarm) + kernelspace.add(Statement(check_alarm)) kernelspace.add(Comment("Dispatch directly back to Userland")) self.call(kernelspace, "Dispatcher::ResumeToTask", @@ -382,15 +384,53 @@ class AlarmTemplate(CodeTemplate): return ret - def __generate_softcounter_event(self, alarm): + def generate_check_alarm_softcounter(self, alarm): + args = {"alarm": alarm.impl.name} abb = alarm.carried_syscall + ret = self.expand_snippet("if_alarm", **args) + "\n" userspace, kernelspace = Block(), Block() + if abb.isA(S.ActivateTask): self.rules.syscall_rules.ActivateTask(abb, userspace, kernelspace) else: self.rules.syscall_rules.SetEvent(abb, userspace, kernelspace) - return "".join(kernelspace.expand(None)) + ret += "".join(kernelspace.expand(None)) + ret += self.expand_snippet("endif_alarm", **args) + "\n" + + return ret + + def generate_check_alarm(self, alarm): + callback_name = "OSEKOS_ALARMCB_%s" % (alarm.name) + has_callback = (self.system_graph.find(GraphFunction, callback_name) != None) + args = {"alarm": alarm.impl.name} + # Add a label to identify this basic block + ret = "" + ret += " abb_%(label)s: (void) &&abb_%(label)s;\n" %{ + "label": alarm.carried_checkalarm.generated_function_name() + } + ret += self.expand_snippet("if_alarm", **args) + "\n" + # This alarm has an callback + if has_callback: + # Add a Declaration + decl = FunctionDeclaration(callback_name, extern_c = True) + self.generator.source_file.function_manager.add(decl) + ret += self.expand_snippet("alarm_alarmcallback", callback = callback_name) + "\n" + + carried = alarm.carried_syscall + # SetEvent needs two arguments + if alarm.conf.event: + arglist = "(0, 0)" + else: + arglist = "(0)" + # Add a label to identify this basic block + ret += " abb_%(label)s: (void) &&abb_%(label)s;\n" %{ + "label": carried.generated_function_name() + } + ret += " " + carried.generated_function_name() + arglist + ";\n" + ret += self.expand_snippet("endif_alarm", **args) + "\n" + + return ret def generate_check_alarms(self, snippet, args): if not self.system_graph.AlarmHandlerSubtask: @@ -402,36 +442,9 @@ class AlarmTemplate(CodeTemplate): ret += [" " + kickoff.generated_function_name() + "();\n"] for alarm in self.system_graph.alarms: - callback_name = "OSEKOS_ALARMCB_%s" % (alarm.name) - has_callback = (self.system_graph.find(GraphFunction, callback_name) != None) - args = {"alarm": alarm.impl.name} - # Add a label to identify this basic block - ret += " abb_%(label)s: (void) &&abb_%(label)s;\n" %{ - "label": alarm.carried_checkalarm.generated_function_name() - } - ret += self.expand_snippet("if_alarm", **args) + "\n" - # This alarm has an callback - if has_callback: - # Add a Declaration - decl = FunctionDeclaration(callback_name, extern_c = True) - self.generator.source_file.function_manager.add(decl) - ret += self.expand_snippet("alarm_alarmcallback", callback = callback_name) + "\n" - if alarm.conf.counter.conf.softcounter: - ret += self.__generate_softcounter_event(alarm) - else: - carried = alarm.carried_syscall - # SetEvent needs two arguments - if alarm.conf.event: - arglist = "(0, 0)" - else: - arglist = "(0)" - # Add a label to identify this basic block - ret += " abb_%(label)s: (void) &&abb_%(label)s;\n" %{ - "label": alarm.carried_syscall.generated_function_name() - } - ret += " " + carried.generated_function_name() + arglist + ";\n" - ret += self.expand_snippet("endif_alarm", **args) + "\n" + continue + ret += [self.generate_check_alarm(alarm)] ret += [" " + iret.generated_function_name() + "();\n"] diff --git a/generator/extractor/main.cc b/generator/extractor/main.cc index 09a56ea..b321350 100644 --- a/generator/extractor/main.cc +++ b/generator/extractor/main.cc @@ -30,6 +30,7 @@ #include <llvm/Linker/Linker.h> #include <llvm/IR/Verifier.h> #include <llvm/IR/CFG.h> +#include <llvm/IR/DebugInfoMetadata.h> #endif @@ -150,16 +151,17 @@ static inline void SplitBasicBlocks(Module &module) { } } -static inline void AddKickoffFunctionCalls(Module & module) { +static inline void AddKickoffFunctionCalls(Module & module, LLVMContext &Context) { std::vector<Type *> args; - FunctionType *kickoff_type = FunctionType::get(Type::getVoidTy(getGlobalContext()), args, false); + FunctionType *kickoff_type = FunctionType::get(Type::getVoidTy(Context), args, false); unsigned task_count = 0; for (auto &function : module) { + BasicBlock &bb = *function.begin(); - IRBuilder<> Builder(getGlobalContext()); + IRBuilder<> Builder(Context); if (function.getName().startswith("OSEKOS_TASK_FUNC")) { - Instruction *inst = &*bb.begin(); - Builder.SetInsertPoint(inst); + Instruction *inst = &*bb.begin(); + Builder.SetInsertPoint(inst); std::stringstream ss; ss << "OSEKOS_kickoff_" << task_count++; @@ -167,7 +169,11 @@ static inline void AddKickoffFunctionCalls(Module & module) { Function::ExternalLinkage, ss.str(), &module); std::vector<Value*> ArgsV; - Builder.CreateCall(F, ArgsV); + CallInst *CS = Builder.CreateCall(F, ArgsV); +#ifndef LLVM_LEGACY + CS->setDebugLoc(DebugLoc::get(function.getSubprogram()->getLine(), + 0, function.getSubprogram())); +#endif // Split after Call BasicBlock::iterator it = bb.begin(); @@ -306,19 +312,24 @@ static inline void DumpModuleStructure(Module &module) { out << "}\n"; } -static inline void TransformModule(Module &module) { +static inline void TransformModule(Module &module, LLVMContext &Context) { SplitBasicBlocks(module); - AddKickoffFunctionCalls(module); + AddKickoffFunctionCalls(module, Context); RenameSystemcalls(module); DumpModuleStructure(module); } int main(int argc, char **argv) { // Print a stack trace if we signal out. +#ifdef LLVM_LEGACY sys::PrintStackTraceOnErrorSignal(); + LLVMContext &Context = getGlobalContext(); +#else + sys::PrintStackTraceOnErrorSignal(argv[0]); + LLVMContext Context; +#endif PrettyStackTraceProgram X(argc, argv); - LLVMContext &Context = getGlobalContext(); llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. cl::ParseCommandLineOptions(argc, argv, "llvm dosek extractor\n"); @@ -365,7 +376,7 @@ int main(int argc, char **argv) { //---- SNIP ---- // Here, we add our custom extractor - TransformModule(*Composite); + TransformModule(*Composite, Context); //-------------- if (DumpAsm) errs() << "Here's the assembly:\n" << *Composite; @@ -376,7 +387,7 @@ int main(int argc, char **argv) { #else std::error_code ECC; tool_output_file Out(OutputFilename, ECC, sys::fs::F_None); - std::string EC = ECC.message(); + std::string EC = (! ECC) ? "" : ECC.message(); #endif if (EC != "") { errs() << EC << '\n'; diff --git a/os/timing.h b/os/timing.h index 9b8a5f9..8d16488 100644 --- a/os/timing.h +++ b/os/timing.h @@ -14,8 +14,9 @@ extern "C" { #else #define timing_start(a) 0 #define timing_end(a) 0 -#define timing_dump() do {} while (0); +#define timing_dump() do { Machine::shutdown(); } while(0) #define timing_loop_bound(low, high) +#define timing_print() #endif #define TIMING_POINT_NO_INTERRUPTS_IN_BLOCK 0x200 diff --git a/toolchain/detect b/toolchain/detect index 57e987b..06843cf 100755 --- a/toolchain/detect +++ b/toolchain/detect @@ -139,7 +139,8 @@ def arch_patmos(toolchain): toolchain['target']['platin'] = must(find_program("platin"), "Platin WCET Analyzer") if __name__ == "__main__": - arches = {"i386": [(llvm_config, {"hint": "/proj/i4danceos/tools/llvm-3.4/bin"}), + arches = {"i386": [(llvm_config, {"hint": "/proj/i4danceos/tools/llvm-3.4/bin", + "recommended": "3.9"}), arch_i386], "ARM": [(llvm_config, {"hint": "/proj/i4danceos/tools/llvm-3.4/bin"}), arch_ARM_A9], diff --git a/toolchain/llvm-link.py b/toolchain/llvm-link.py index 6dca244..22986df 100755 --- a/toolchain/llvm-link.py +++ b/toolchain/llvm-link.py @@ -64,7 +64,7 @@ def llvm_link(files, output): return output def llvm_opt(input, output): - check_output([toolchain['target']['opt'], "-std-compile-opts", "-always-inline", "-o", output, input]) + check_output([toolchain['target']['opt'], "-always-inline", "-o", output, input]) return output def llvm_opt_careful(input, output): -- GitLab