diff --git a/include/l4/api/ipc.h b/include/l4/api/ipc.h index aa659f2..90a020f 100644 --- a/include/l4/api/ipc.h +++ b/include/l4/api/ipc.h @@ -13,8 +13,8 @@ #if defined (__KERNEL__) /* These are for internally created ipc paths. */ -int ipc_send(l4id_t to); -int ipc_sendrecv(l4id_t to, l4id_t from); +int ipc_send(l4id_t to, int full); +int ipc_sendrecv(l4id_t to, l4id_t from, int full); #endif diff --git a/include/l4/generic/space.h b/include/l4/generic/space.h index b559f91..526c875 100644 --- a/include/l4/generic/space.h +++ b/include/l4/generic/space.h @@ -42,7 +42,8 @@ void address_space_remove(struct address_space *space); void address_space_reference_lock(); void address_space_reference_unlock(); void init_address_space_list(void); -int check_access(unsigned long vaddr, unsigned long size, unsigned int flags); +int check_access(unsigned long vaddr, unsigned long size, + unsigned int flags, int page_in); #endif #endif /* __SPACE_H__ */ diff --git a/include/l4/generic/tcb.h b/include/l4/generic/tcb.h index c4488c6..eaa5f9e 100644 --- a/include/l4/generic/tcb.h +++ b/include/l4/generic/tcb.h @@ -26,6 +26,9 @@ #define TASK_SUSPENDING (1 << 1) #define TASK_RESUMING (1 << 2) +/* IPC resulted in a fault error (For ipcs that cannot page fault) */ +#define IPC_EFAULT (1 << 3) + /* Task states */ enum task_state { TASK_INACTIVE = 0, diff --git a/include/l4/glue/arm/message.h b/include/l4/glue/arm/message.h index 78672f6..2ae7578 100644 --- a/include/l4/glue/arm/message.h +++ b/include/l4/glue/arm/message.h @@ -83,4 +83,13 @@ #include INC_GLUE(memlayout.h) +#if defined (__KERNEL__) +struct utcb { + u32 mr[MR_TOTAL]; /* MRs that are mapped to real registers */ + u32 saved_tag; /* Saved tag field for stacked ipcs */ + u32 saved_sender; /* Saved sender field for stacked ipcs */ + u32 mr_rest[MR_REST]; /* Complete the utcb for up to 64 words */ +}; +#endif + #endif /* __GLUE_ARM_MESSAGE_H__ */ diff --git a/src/api/ipc.c b/src/api/ipc.c index 97a22e8..487e871 100644 --- a/src/api/ipc.c +++ b/src/api/ipc.c @@ -1,7 +1,7 @@ /* * Inter-process communication * - * Copyright (C) 2007 Bahadir Balban + * Copyright (C) 2007-2009 Bahadir Bilgehan Balban */ #include #include @@ -14,13 +14,46 @@ #include INC_API(syscall.h) #include INC_GLUE(message.h) +/* + * ipc syscall uses an ipc_type variable and flags and send/recv + * details are embedded in this variable. + */ +#define IPC_TYPE_FLAGS_SHIFT 2 enum IPC_TYPE { IPC_INVALID = 0, IPC_SEND = 1, IPC_RECV = 2, - IPC_SENDRECV = 3 + IPC_SENDRECV = 3, + IPC_SEND_FULL = 5, + IPC_RECV_FULL = 6, + IPC_SENDRECV_FULL = 7, + IPC_SEND_EXTENDED = 9, + IPC_RECV_EXTENDED = 10, + IPC_SENDRECV_EXTENDED = 11, }; +/* Copy full utcb region from one task to another. */ +int ipc_full_copy(struct ktcb *to, struct ktcb *from) +{ + struct utcb *from_utcb = (struct utcb *)from->utcb_address; + struct utcb *to_utcb = (struct utcb *)to->utcb_address; + int ret; + + /* Check that utcb memory accesses won't fault us */ + if ((ret = check_access(to->utcb_address, UTCB_SIZE, + MAP_SVC_RW_FLAGS, 0)) < 0) + return ret; + if ((ret = check_access(to->utcb_address, UTCB_SIZE, + MAP_SVC_RW_FLAGS, 0)) < 0) + return ret; + + /* Directly copy from one utcb to another */ + memcpy(to_utcb->mr_rest, from_utcb->mr_rest, + MR_REST * sizeof(unsigned int)); + + return 0; +} + /* * Copies message registers from one ktcb stack to another. During the return * from system call, the registers are popped from the stack. In the future @@ -30,10 +63,11 @@ enum IPC_TYPE { * L4_ANYTHREAD. This is done for security since the receiver cannot trust * the sender info provided by the sender task. */ -int ipc_msg_copy(struct ktcb *to, struct ktcb *from) +int ipc_msg_copy(struct ktcb *to, struct ktcb *from, int full) { unsigned int *mr0_src = KTCB_REF_MR0(from); unsigned int *mr0_dst = KTCB_REF_MR0(to); + int ret = 0; /* NOTE: * Make sure MR_TOTAL matches the number of registers saved on stack. @@ -44,7 +78,11 @@ int ipc_msg_copy(struct ktcb *to, struct ktcb *from) if (to->expected_sender == L4_ANYTHREAD) mr0_dst[MR_SENDER] = from->tid; - return 0; + /* Check if full utcb copying is requested and do it */ + if (full) + ret = ipc_full_copy(to, from); + + return ret; } int sys_ipc_control(syscall_context_t *regs) @@ -61,10 +99,11 @@ int sys_ipc_control(syscall_context_t *regs) */ /* Interruptible ipc */ -int ipc_send(l4id_t recv_tid) +int ipc_send(l4id_t recv_tid, int full) { struct ktcb *receiver = tcb_find(recv_tid); struct waitqueue_head *wqhs, *wqhr; + int ret = 0; wqhs = &receiver->wqh_send; wqhr = &receiver->wqh_recv; @@ -89,14 +128,18 @@ int ipc_send(l4id_t recv_tid) spin_unlock(&wqhs->slock); /* Copy message registers */ - ipc_msg_copy(receiver, current); + if ((ret = ipc_msg_copy(receiver, current, full)) < 0) { + /* Set ipc error flag in receiver */ + BUG_ON(ret != -EFAULT); + receiver->flags |= IPC_EFAULT; + } // printk("%s: (%d) Waking up (%d)\n", __FUNCTION__, // current->tid, receiver->tid); /* Wake it up, we can yield here. */ sched_resume_sync(receiver); - return 0; + return ret; } /* The receiver is not ready and/or not expecting us */ @@ -116,12 +159,19 @@ int ipc_send(l4id_t recv_tid) current->flags &= ~TASK_INTERRUPTED; return -EINTR; } + + /* Did ipc fail with a fault error? */ + if (current->flags & IPC_EFAULT) { + current->flags &= ~IPC_EFAULT; + return -EFAULT; + } return 0; } -int ipc_recv(l4id_t senderid) +int ipc_recv(l4id_t senderid, int full) { struct waitqueue_head *wqhs, *wqhr; + int ret = 0; wqhs = ¤t->wqh_send; wqhr = ¤t->wqh_recv; @@ -154,12 +204,20 @@ int ipc_recv(l4id_t senderid) task_unset_wqh(sleeper); spin_unlock(&wqhr->slock); spin_unlock(&wqhs->slock); - ipc_msg_copy(current, sleeper); + + /* Copy message registers */ + if ((ret = ipc_msg_copy(current, sleeper, + full)) < 0) { + + /* Set ipc fault flag on sleeper */ + BUG_ON(ret != -EFAULT); + sleeper->flags |= IPC_EFAULT; + } // printk("%s: (%d) Waking up (%d)\n", __FUNCTION__, // current->tid, sleeper->tid); sched_resume_sync(sleeper); - return 0; + return ret; } } } @@ -181,6 +239,13 @@ int ipc_recv(l4id_t senderid) current->flags &= ~TASK_INTERRUPTED; return -EINTR; } + + /* Did ipc fail with a fault error? */ + if (current->flags & IPC_EFAULT) { + current->flags &= ~IPC_EFAULT; + return -EFAULT; + } + return 0; } @@ -198,20 +263,22 @@ int ipc_recv(l4id_t senderid) * (6) System task calls ipc_send() sending the return result. * (7) Rendezvous occurs. Both tasks exchange mrs and leave rendezvous. */ -int ipc_sendrecv(l4id_t to, l4id_t from) +int ipc_sendrecv(l4id_t to, l4id_t from, int full) { int ret = 0; if (to == from) { /* Send ipc request */ - ipc_send(to); + if ((ret = ipc_send(to, full)) < 0) + return ret; /* * Get reply. * A client would block its server only very briefly * between these calls. */ - ipc_recv(from); + if ((ret = ipc_recv(from, full)) < 0) + return ret; } else { printk("%s: Unsupported ipc operation.\n", __FUNCTION__); ret = -ENOSYS; @@ -225,13 +292,28 @@ static inline int __sys_ipc(l4id_t to, l4id_t from, unsigned int ipc_type) switch (ipc_type) { case IPC_SEND: - ret = ipc_send(to); + ret = ipc_send(to, 0); break; case IPC_RECV: - ret = ipc_recv(from); + ret = ipc_recv(from, 0); break; case IPC_SENDRECV: - ret = ipc_sendrecv(to, from); + ret = ipc_sendrecv(to, from, 0); + break; + case IPC_SEND_FULL: + ret = ipc_send(to, 1); + break; + case IPC_RECV_FULL: + ret = ipc_recv(from, 1); + break; + case IPC_SENDRECV_FULL: + ret = ipc_sendrecv(to, from, 1); + break; + case IPC_SEND_EXTENDED: + break; + case IPC_RECV_EXTENDED: + break; + case IPC_SENDRECV_EXTENDED: break; case IPC_INVALID: default: @@ -267,6 +349,7 @@ int sys_ipc(syscall_context_t *regs) { l4id_t to = (l4id_t)regs->r0; l4id_t from = (l4id_t)regs->r1; + unsigned int flags = (unsigned int)regs->r2; unsigned int ipc_type = 0; int ret = 0; @@ -292,6 +375,9 @@ int sys_ipc(syscall_context_t *regs) /* [1] for Receive, [1:0] for both */ ipc_type |= ((from != L4_NILTHREAD) << 1); + /* Short, full or extended ipc set here. Bits [3:2] */ + ipc_type |= (flags & L4_IPC_FLAGS_MASK) << IPC_TYPE_FLAGS_SHIFT; + if (ipc_type == IPC_INVALID) { ret = -EINVAL; goto error; @@ -302,7 +388,7 @@ int sys_ipc(syscall_context_t *regs) return ret; error: - printk("Erroneous ipc by: %d. Err: %d\n", current->tid, ret); + // printk("Erroneous ipc by: %d. Err: %d\n", current->tid, ret); ipc_type = IPC_INVALID; return ret; } diff --git a/src/api/kip.c b/src/api/kip.c index 36c201c..cde9e29 100644 --- a/src/api/kip.c +++ b/src/api/kip.c @@ -23,19 +23,19 @@ int __sys_kread(int rd, void *dest) switch(rd) { case KDATA_PAGE_MAP: // printk("Handling KDATA_PAGE_MAP request.\n"); - if (check_access(vaddr, sizeof(page_map), MAP_USR_RW_FLAGS) < 0) + if (check_access(vaddr, sizeof(page_map), MAP_USR_RW_FLAGS, 1) < 0) return -EINVAL; memcpy(dest, &page_map, sizeof(page_map)); break; case KDATA_BOOTDESC: // printk("Handling KDATA_BOOTDESC request.\n"); - if (check_access(vaddr, bootdesc->desc_size, MAP_USR_RW_FLAGS) < 0) + if (check_access(vaddr, bootdesc->desc_size, MAP_USR_RW_FLAGS, 1) < 0) return -EINVAL; memcpy(dest, bootdesc, bootdesc->desc_size); break; case KDATA_BOOTDESC_SIZE: // printk("Handling KDATA_BOOTDESC_SIZE request.\n"); - if (check_access(vaddr, sizeof(unsigned int), MAP_USR_RW_FLAGS) < 0) + if (check_access(vaddr, sizeof(unsigned int), MAP_USR_RW_FLAGS, 1) < 0) return -EINVAL; *(unsigned int *)dest = bootdesc->desc_size; break; diff --git a/src/arch/arm/exception.c b/src/arch/arm/exception.c index 77176d0..2b4f4a5 100644 --- a/src/arch/arm/exception.c +++ b/src/arch/arm/exception.c @@ -58,7 +58,7 @@ void fault_ipc_to_pager(u32 faulty_pc, u32 fsr, u32 far) offsetof(syscall_context_t, r3)); /* Send ipc to the task's pager */ - ipc_sendrecv(current->pagerid, current->pagerid); + ipc_sendrecv(current->pagerid, current->pagerid, 0); /* * FIXME: CHECK TASK KILL REPLY !!! diff --git a/src/generic/space.c b/src/generic/space.c index 1128f7c..5d8b569 100644 --- a/src/generic/space.c +++ b/src/generic/space.c @@ -151,7 +151,7 @@ struct address_space *address_space_create(struct address_space *orig) * If its not mapped-in, it generates a page-in request to the thread's * pager. If fault hasn't cleared, aborts. */ -int check_access(unsigned long vaddr, unsigned long size, unsigned int flags) +int check_access(unsigned long vaddr, unsigned long size, unsigned int flags, int page_in) { int err; @@ -159,20 +159,17 @@ int check_access(unsigned long vaddr, unsigned long size, unsigned int flags) if (size >= USER_AREA_SIZE) return -EINVAL; - /* Check if in user range, but this is more up to the pager to decide */ - if (current->tid == PAGER_TID) { - if (!(vaddr >= INITTASK_AREA_START && vaddr < INITTASK_AREA_END)) - return -EINVAL; - } else { - if (!(vaddr >= USER_AREA_START && vaddr < USER_AREA_END)) - return -EINVAL; + /* Check if the address is mapped with given flags */ + if (!check_mapping(vaddr, size, flags)) { + /* Is a page in requested? */ + if (page_in) { + /* Ask pager if paging in is possible */ + if((err = pager_pagein_request(vaddr, size, flags)) < 0) + return err; + } else + return -EFAULT; } - /* If not mapped, ask pager whether this is possible */ - if (!check_mapping(vaddr, size, flags)) - if((err = pager_pagein_request(vaddr, size, flags)) < 0) - return err; - return 0; } diff --git a/src/generic/time.c b/src/generic/time.c index 617564e..bd70ddd 100644 --- a/src/generic/time.c +++ b/src/generic/time.c @@ -77,7 +77,7 @@ int sys_time(syscall_context_t *args) int set = (int)args->r1; int retries = 20; - if (check_access((unsigned long)tv, sizeof(*tv), MAP_USR_RW_FLAGS) < 0) + if (check_access((unsigned long)tv, sizeof(*tv), MAP_USR_RW_FLAGS, 1) < 0) return -EINVAL; /* Get time */ diff --git a/tasks/libl4/src/arm/syscalls.S b/tasks/libl4/src/arm/syscalls.S index 2430447..7ae44fb 100644 --- a/tasks/libl4/src/arm/syscalls.S +++ b/tasks/libl4/src/arm/syscalls.S @@ -53,7 +53,7 @@ END_PROC(l4_kread) /* * For clone() we need special assembler handling - * Same signature as ipc(): @r0 = to, @r1 = from + * Same signature as ipc(): @r0 = to, @r1 = from @r2 = flags * * NOTE: Note that this breaks l4 system call interface, * this should be moved elsewhere and modified using existing l4 mechanisms. @@ -62,6 +62,11 @@ BEGIN_PROC(arch_clone) stmfd sp!, {r4-r8,lr} @ Save context. utcb_address r12 @ Get utcb address. ldmia r12!, {r3-r8} @ Load 6 Message registers from utcb. MR0-MR5 + + cmp r2, #0 +1: + bne 1b + ldr r12, =__l4_ipc mov lr, pc ldr pc, [r12] @ Perform the ipc() @@ -101,6 +106,11 @@ BEGIN_PROC(l4_ipc) stmfd sp!, {r4-r8,lr} @ Save context. utcb_address r12 @ Get utcb address. ldmia r12!, {r3-r8} @ Load 6 Message registers from utcb. MR0-MR5 + + cmp r2, #0 +1: + bne 1b + ldr r12, =__l4_ipc mov lr, pc ldr pc, [r12] diff --git a/tasks/libposix/fork.c b/tasks/libposix/fork.c index dccd4cb..4072bf8 100644 --- a/tasks/libposix/fork.c +++ b/tasks/libposix/fork.c @@ -52,7 +52,7 @@ int fork(void) return ret; } -extern int arch_clone(l4id_t to, l4id_t from); +extern int arch_clone(l4id_t to, l4id_t from, unsigned int flags); int clone(int (*fn)(void *), void *child_stack, int flags, void *arg, ...) { @@ -74,7 +74,7 @@ int clone(int (*fn)(void *), void *child_stack, int flags, void *arg, ...) write_mr(L4SYS_ARG1, flags); /* Perform an ipc but with different return logic. See implementation. */ - if ((ret = arch_clone(PAGER_TID, PAGER_TID)) < 0) { + if ((ret = arch_clone(PAGER_TID, PAGER_TID, 0)) < 0) { print_err("%s: L4 IPC Error: %d.\n", __FUNCTION__, ret); return ret; } diff --git a/tasks/test0/SConstruct b/tasks/test0/SConstruct index 6312087..8be9ec7 100644 --- a/tasks/test0/SConstruct +++ b/tasks/test0/SConstruct @@ -68,11 +68,42 @@ env = Environment(CC = 'arm-none-linux-gnueabi-gcc', CPPFLAGS = "-D__USERSPACE__", CPPPATH = ['#include', libl4_incpath, libposix_incpath, kernel_incpath]) + +test_exec_ld_script = "include/test_exec_linker.lds" +# The kernel build environment: +test_exec_env = Environment(CC = 'arm-none-linux-gnueabi-gcc', + # We don't use -nostdinc because sometimes we need standard headers, + # such as stdarg.h e.g. for variable args, as in printk(). + CCFLAGS = ['-O3', '-nostdlib', '-ffreestanding', '-std=gnu99', '-Wall', '-Werror'], + LINKFLAGS = ['-nostdlib', '-T' + test_exec_ld_script, "-L" + libc_libpath, "-L" + libl4_path, \ + '-L' + libposix_libpath], + ASFLAGS = ['-D__ASSEMBLY__'], + PROGSUFFIX = '.axf', # The suffix to use for final executable + ENV = {'PATH' : os.environ['PATH']}, # Inherit shell path + LIBS = [libc_name, 'gcc', libc_name, 'libl4', 'libposix', libc_name], + CPPFLAGS = "-D__USERSPACE__", + CPPPATH = ['#include', libl4_incpath, libposix_incpath, kernel_incpath]) + src = [glob("src/*.c"), glob("*.c"), glob("*.S"), glob("src/arch/arm/*.c"), glob("../libcont/*.c")] objs = env.Object(src) physical_base = env.Command(physical_base_ld_script, prev_image, get_physical_base) crt0_copied = env.Command("crt0.o", libc_crt0, copy_crt0) +test_exec_src = [glob("src/test_exec/*.c")] +test_exec_objs = test_exec_env.Object(test_exec_src) +test_exec_name = "test_exec" +test_exec = test_exec_env.Program(test_exec_name, test_exec_objs + [crt0_copied]) +test_exec_env.Alias(test_exec_name, test_exec) + +env.Depends(objs, test_exec) task = env.Program(task_name, objs + [crt0_copied]) env.Alias(task_name, task) + +# I find this to be a BUG related to SCons. SCons is still good compared to +# notoriously horrible makefiles, but it could have been better. +# if test_exec doesn't depend on physical_base, test_exec is compiled but +# task complains that physical_base is not there. However we already declared +# its dependency below. + +env.Depends(test_exec, physical_base) env.Depends(task, physical_base) diff --git a/tasks/test0/include/linker.lds b/tasks/test0/include/linker.lds index efa49eb..f1c0c3e 100644 --- a/tasks/test0/include/linker.lds +++ b/tasks/test0/include/linker.lds @@ -33,9 +33,9 @@ SECTIONS .data : AT (ADDR(.data) - offset) { . = ALIGN(4K); - _start_test1 = .; - *(.test1) - _end_test1 = .; + _start_test_exec = .; + *(.testexec) + _end_test_exec = .; *(.data) } .bss : AT (ADDR(.bss) - offset) { *(.bss) } diff --git a/tasks/test0/include/test_exec_linker.lds b/tasks/test0/include/test_exec_linker.lds new file mode 100644 index 0000000..9ac1736 --- /dev/null +++ b/tasks/test0/include/test_exec_linker.lds @@ -0,0 +1,37 @@ +/* + * Simple linker script for userspace or svc tasks. + * + * Copyright (C) 2007 Bahadir Balban + */ + +/* + * The only catch with this linker script is that everything + * is linked starting at virtual_base, and loaded starting + * at physical_base. virtual_base is the predefined region + * of virtual memory for userland applications. physical_base + * is determined at build-time, it is one of the subsequent pages + * that come after the kernel image's load area. + */ +/* USER_AREA_START, see memlayout.h */ +virtual_base = 0x10000000; +__stack = (0x20000000 - 0x1000 - 8); /* First page before the env/args */ +INCLUDE "include/physical_base.lds" + +/* physical_base = 0x228000; */ +offset = virtual_base - physical_base; + +ENTRY(_start) + +SECTIONS +{ + . = virtual_base; + _start_text = .; + .text : AT (ADDR(.text) - offset) { crt0.o(.text) *(.text) } + /* rodata is needed else your strings will link at physical! */ + .rodata : AT (ADDR(.rodata) - offset) { *(.rodata) } + .rodata1 : AT (ADDR(.rodata1) - offset) { *(.rodata1) } + . = ALIGN(4K); + .data : AT (ADDR(.data) - offset) { *(.data) } + .bss : AT (ADDR(.bss) - offset) { *(.bss) } + _end = .; +} diff --git a/tasks/test0/main.c b/tasks/test0/main.c index 0563d75..186a78e 100644 --- a/tasks/test0/main.c +++ b/tasks/test0/main.c @@ -19,7 +19,7 @@ void wait_pager(l4id_t partner) for (int i = 0; i < 6; i++) write_mr(i, i); l4_send(partner, L4_IPC_TAG_SYNC); - // printf("Pager synced with us.\n"); + printf("Pager synced with us.\n"); } pid_t parent_of_all; diff --git a/tasks/test0/src/exectest.c b/tasks/test0/src/exectest.c index 239f710..5aa7bb3 100644 --- a/tasks/test0/src/exectest.c +++ b/tasks/test0/src/exectest.c @@ -11,14 +11,14 @@ #include #include -extern char _start_test1[]; -extern char _end_test1[]; +extern char _start_test_exec[]; +extern char _end_test_exec[]; int exectest(void) { int fd; - void *exec_start = (void *)_start_test1; - unsigned long size = _end_test1 - _start_test1; + void *exec_start = (void *)_start_test_exec; + unsigned long size = _end_test_exec - _start_test_exec; int left, cnt; char *argv[5]; char filename[128]; diff --git a/tasks/test0/src/test_exec/container.c b/tasks/test0/src/test_exec/container.c new file mode 100644 index 0000000..2e145e3 --- /dev/null +++ b/tasks/test0/src/test_exec/container.c @@ -0,0 +1,25 @@ +/* + * Container entry point for this task. + * + * Copyright (C) 2007-2009 Bahadir Bilgehan Balban + */ + +#include +#include +#include +#include /* Initialisers for posix library */ + +void main(void); + +void __container_init(void) +{ + /* Generic L4 thread initialisation */ + __l4_init(); + + /* Initialise posix library for application */ + libposix_init(); + + /* Entry to main */ + main(); +} + diff --git a/tasks/test0/src/test_exec/container.o b/tasks/test0/src/test_exec/container.o new file mode 100644 index 0000000..9dc268e Binary files /dev/null and b/tasks/test0/src/test_exec/container.o differ diff --git a/tasks/test0/src/test_exec/test_exec.c b/tasks/test0/src/test_exec/test_exec.c new file mode 100644 index 0000000..eccd763 --- /dev/null +++ b/tasks/test0/src/test_exec/test_exec.c @@ -0,0 +1,36 @@ +/* + * Some tests for posix syscalls. + * + * Copyright (C) 2007 Bahadir Balban + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void wait_pager(l4id_t partner) +{ + // printf("%s: Syncing with pager.\n", __TASKNAME__); + for (int i = 0; i < 6; i++) + write_mr(i, i); + l4_send(partner, L4_IPC_TAG_SYNC); + // printf("Pager synced with us.\n"); +} + +void main(void) +{ + wait_pager(0); + if (getpid() == 2) { + printf("EXECVE TEST -- PASSED --\n", getpid()); + printf("\n(Thread %d): Continues to sync with the pager...\n", getpid()); + while (1) + wait_pager(0); + } + _exit(0); +} + diff --git a/tasks/test0/src/test_exec/test_exec.o b/tasks/test0/src/test_exec/test_exec.o new file mode 100644 index 0000000..165d430 Binary files /dev/null and b/tasks/test0/src/test_exec/test_exec.o differ diff --git a/tasks/test0/test_exec.S b/tasks/test0/test_exec.S new file mode 100644 index 0000000..d78833d --- /dev/null +++ b/tasks/test0/test_exec.S @@ -0,0 +1,5 @@ +.section .testexec + +.align 4 +.incbin "test_exec.axf" +.align 4