diff --git a/README.md b/README.md index 8af0753ef0e24fc88a8ffd85e5de9d0204ec59ad..0381b1b1de4ab5045516c3382d2431b62e856bdf 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 d65010526c86df3ac70c3c29b3466a084e5428e5..fbc3551ff330e63b311c3cc52ce521f22cb42d4a 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 0000000000000000000000000000000000000000..e7adca6b13bc2c043935b8cdd0bf457a58bf6cdb --- /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 ea13a78a70542d09ef1819c641d5e7826913d438..1054bf061a1dd8899077a58fa2d7ca8ec725cbcf 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 74326788c6d63bf82f35bdbe4c500df9aa667627..9c86309f3880ff19eccf6749d472da96b8fa5c06 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 44b8c7b024c9fd5fa5182c5e9b21a09c25588f34..fc10a1ed0e45fa53365ed9b5d7ed120231ec3c91 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 2ab245f6e321217512473b0d00433ac1ed66465b..1f119f4fe0ceaac1ec65e1d214bb3bdd2fa78a7a 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 962c1a8af41341e0b16e5722c8d52442e9f78bf8..ab614da8d17c67d6631b38a68abc609dcea9027b 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 d1f15bf701722db6b14e55e3621d1f2196de0c45..a67769b1386737c32f973ac56489d7fd034b3d88 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 68379ab256d842df0457051cc975dd39df1f7d82..f70df5255c10400ef1a229fc8a3c3eb5088733ce 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 a5a03e4f25f0cf032b72e7b3fae1228a9fbfcb3f..b6b5c140fa0120f233857e81deaf4c5be8765d2c 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 09ab3815aa197ff672e131f946ca1a4f2f2e3349..878f23bb44a957ce18a85faa1e9c3d5932151fb1 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 e1fe60c706e6ed45c31ce6990b81c0546e4ff69c..0474dd2c163b6b391a429f660d13b82fd04597bf 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 af3829b5070b5b00c39032ed111dede4ab75b939..a76a66a680b2b234ee11bfad1f67452815ba958d 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 c1f2bf1d6b6f7e63ebcc882aeda493112f25a259..809fc90bf5232f9d3955657641649ec5ee313bd8 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 40bde30768d7551e58a3f6607f86b6547f0b214a..0c99a72a54b787f12ecabcdf134f4bb66600fbbf 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 09a56eab48d5b5ff5854c0e42a9f1a5782b4ac0a..b3213500f38c8692d5b85b95e5215494532ade0e 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 9b8a5f9d578811d29094f1b4454837588fabebf2..8d164883ca8dc1f53516fe18f2f923621a0b638c 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 57e987b84c0da6f102d2b1c386f470f68af51395..06843cff034c909bf3a02e0d5e306b1feeeb393a 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 6dca244d67582c23b9dd27d30d3f71b54677af63..22986df7d79a1d8a1eb223c5c5ec91a85abe5c01 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):