Bare support for dispatching parallel processes
authorBarret Rhoden <brho@cs.berkeley.edu>
Mon, 17 Aug 2009 11:05:46 +0000 (04:05 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Mon, 31 Aug 2009 20:59:18 +0000 (13:59 -0700)
Reworked some of the interrupt handling so we can directly call
functions from the IDT handler.  Very basic userland support (they just
spin if they are an extra core).  Gritty example of how to use this from
the kernel in manager...

20 files changed:
kern/arch/i386/Makefrag
kern/arch/i386/env.c
kern/arch/i386/process.c [new file with mode: 0644]
kern/arch/i386/smp.c
kern/arch/i386/trap.c
kern/arch/i386/trap.h
kern/arch/i386/trapentry.S
kern/arch/sparc/env.c
kern/include/env.h
kern/include/kfs.h
kern/include/smp.h
kern/include/trap.h
kern/src/Makefrag
kern/src/env.c
kern/src/kfs.c
kern/src/manager.c
kern/src/process.c
kern/src/testing.c
user/apps/roslib/mhello.c [new file with mode: 0644]
user/roslib/src/i386/entry.S

index 3789053..d3c918a 100644 (file)
@@ -25,4 +25,5 @@ KERN_ARCH_SRCFILES := $(KERN_ARCH_SRC_DIR)/entry.S \
                       $(KERN_ARCH_SRC_DIR)/smp.c \
                       $(KERN_ARCH_SRC_DIR)/apic.c \
                       $(KERN_ARCH_SRC_DIR)/kdebug.c \
+                      $(KERN_ARCH_SRC_DIR)/process.c \
                       $(KERN_ARCH_SRC_DIR)/env.c
index 33f1d4f..9041a7d 100644 (file)
@@ -58,21 +58,21 @@ env_set_program_counter(env_t* e, uintptr_t pc)
 }
 
 void
-env_init_trapframe(env_t* e)
+env_init_trapframe(trapframe_t *tf)
 {
        // Set up appropriate initial values for the segment registers.
        // GD_UD is the user data segment selector in the GDT, and
        // GD_UT is the user text segment selector (see inc/memlayout.h).
        // The low 2 bits of each segment register contains the
        // Requestor Privilege Level (RPL); 3 means user mode.
-       e->env_tf.tf_ds = GD_UD | 3;
-       e->env_tf.tf_es = GD_UD | 3;
-       e->env_tf.tf_ss = GD_UD | 3;
-       e->env_tf.tf_esp = USTACKTOP;
-       e->env_tf.tf_cs = GD_UT | 3;
+       tf->tf_ds = GD_UD | 3;
+       tf->tf_es = GD_UD | 3;
+       tf->tf_ss = GD_UD | 3;
+       tf->tf_esp = USTACKTOP;
+       tf->tf_cs = GD_UT | 3;
        // You will set e->env_tf.tf_eip later.
        // set the env's EFLAGSs to have interrupts enabled
-       e->env_tf.tf_eflags |= 0x00000200; // bit 9 is the interrupts-enabled
+       tf->tf_eflags |= 0x00000200; // bit 9 is the interrupts-enabled
 }
 
 // Flush all mapped pages in the user portion of the address space
diff --git a/kern/arch/i386/process.c b/kern/arch/i386/process.c
new file mode 100644 (file)
index 0000000..b802291
--- /dev/null
@@ -0,0 +1,32 @@
+#include <arch/arch.h>
+#include <arch/trap.h>
+#include <process.h>
+#include <smp.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+void __startcore(void)
+{
+       uint32_t coreid = core_id();
+       struct proc *p_to_run = per_cpu_info[coreid].p_to_run;
+       trapframe_t local_tf, *tf_to_pop = per_cpu_info[coreid].tf_to_pop;
+
+       // EOI - we received the interrupt.  probably no issues with receiving
+       // further interrupts in this function.
+       lapic_send_eoi();
+
+       printk("Startcore on core %d\n", coreid);
+       assert(p_to_run);
+       if (!tf_to_pop) {
+               tf_to_pop = &local_tf;
+               memset(tf_to_pop, 0, sizeof(*tf_to_pop));
+               env_init_trapframe(tf_to_pop);
+               // Note the init_tf sets tf_to_pop->tf_esp = USTACKTOP;
+               tf_to_pop->tf_regs.reg_eax = 1;
+               tf_to_pop->tf_eip = p_to_run->env_entry;
+       }
+       proc_startcore(p_to_run, tf_to_pop);
+}
+
+
index 200c7bb..e299bab 100644 (file)
@@ -28,7 +28,6 @@ handler_wrapper_t             handler_wrappers[NUM_HANDLER_WRAPPERS];
 static int smp_call_function(uint8_t type, uint8_t dest, poly_isr_t handler, TV(t) data,
                              handler_wrapper_t** wait_wrapper)
 {
-       extern handler_t interrupt_handlers[NUM_INTERRUPT_HANDLERS];
        int8_t state = 0;
        uint32_t wrapper_num;
        handler_wrapper_t* wrapper;
index 9220afb..708e1c1 100644 (file)
@@ -256,7 +256,7 @@ irq_handler(trapframe_t *tf)
        if (handler_tbl[tf->tf_trapno].isr != 0)
                handler_tbl[tf->tf_trapno].isr(tf, handler_tbl[tf->tf_trapno].data);
        // if we're a general purpose IPI function call, down the cpu_list
-       if ((0xf0 <= tf->tf_trapno) && (tf->tf_trapno < 0xf0 +NUM_HANDLER_WRAPPERS))
+       if ((I_SMP_CALL0 <= tf->tf_trapno) && (tf->tf_trapno <= I_SMP_CALL_LAST))
                down_checklist(handler_wrappers[tf->tf_trapno & 0x0f].cpu_list);
 
        // Send EOI.  might want to do this in assembly, and possibly earlier
index 00767cb..d737467 100644 (file)
 #define T_SYSCALL   0x80                       // system call
 #define T_DEFAULT   0xdeadbeef         // catchall
 
+/* IPIs */
+/* Direct/Hardwired IPIs.  Hardwired in trapentry.S */
+#define I_STARTCORE 230
+#define I_DEATH     231
+/* smp_call_function IPIs, keep in sync with NUM_HANDLER_WRAPPERS (and < 16)
+ * it's important that this begins with 0xf0.  check i386/trap.c for details.
+ */
+#define I_SMP_CALL0 0xf0 // 240
+#define I_SMP_CALL1 0xf1
+#define I_SMP_CALL2 0xf2
+#define I_SMP_CALL3 0xf3
+#define I_SMP_CALL4 0xf4
+#define I_SMP_CALL_LAST I_SMP_CALL4
+/* Testing IPI (used in testing.c) */
+#define I_TESTING 0xff
+
+
+
 #ifndef __ASSEMBLER__
 
 #include <arch/types.h>
index 3087851..4a6d213 100644 (file)
@@ -1,11 +1,14 @@
-/* See COPYRIGHT for copyright information. */
-
+/* See COPYRIGHT for copyright information.
+ * The two TRAP* macros (minus the .data parts) are from the JOS project.
+ * Everything else:
+ * Copyright (c) 2009 The Regents of the University of California
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * See LICENSE for details.
+ */
 #include <arch/mmu.h>
 #include <arch/trap.h>
 #include <ros/memlayout.h>
 
-
-
 ###################################################################
 # exceptions/interrupts
 ###################################################################
        .long name;                                                     \
        .long num
 
+/* Same as above, but takes a specific function to jump to.  See comments 
+ * below from _allirqs for details.
+ */
+#define IRQ_HANDLER_SPEC(name, num, func)                                      \
+       .text;                                                                                                 \
+       .globl name;                                                                                   \
+       .type name, @function;                                                                 \
+       .align 2;                                                                                              \
+       name:                                                                                                  \
+       pushl $0;                                                                  \
+       pushl $(num);                                                              \
+       cld;                                                                       \
+       pushl %ds;                                                                 \
+       pushl %es;                                                                 \
+       pushal;                                                                    \
+       movw $GD_KD, %ax;                                                          \
+       movw %ax, %ds;                                                             \
+       movw %ax, %es;                                                             \
+       pushl %esp;                                                                \
+       movl $0, %ebp;                                                             \
+       call (func);                                                               \
+       popl %esp;                                                                 \
+       popal;                                                                     \
+       popl %es;                                                                  \
+       popl %ds;                                                                  \
+       addl $0x8, %esp;                                                           \
+       iret;                                                                      \
+       .data;                                                                     \
+       .long name;                                                                \
+       .long num
+
 .data
 .globl trap_tbl
 trap_tbl:
 
 /*
- * Lab 3: Your code here for generating entry points for the different traps.
+ * Generate entry points for the different traps.
  */
 TRAPHANDLER_NOEC(ISR_divide_error, T_DIVIDE)
 TRAPHANDLER_NOEC(ISR_debug_exceptions, T_DEBUG)
@@ -106,17 +140,23 @@ IRQ_HANDLER(IRQ12, 44)
 IRQ_HANDLER(IRQ13, 45)
 IRQ_HANDLER(IRQ14, 46)
 IRQ_HANDLER(IRQ15, 47)
-/* 20 general purpose vectors, for use by the LAPIC.  Can expand later. */
+/* 25 general purpose vectors, for use by the LAPIC.  Can expand later. */
+IRQ_HANDLER_SPEC(IRQ198, I_STARTCORE, __startcore) 
+IRQ_HANDLER_SPEC(IRQ199, I_DEATH, irq_handler) 
+IRQ_HANDLER(IRQ200, 232)
+IRQ_HANDLER(IRQ201, 233)
+IRQ_HANDLER(IRQ202, 234)
 IRQ_HANDLER(IRQ203, 235)
 IRQ_HANDLER(IRQ204, 236)
 IRQ_HANDLER(IRQ205, 237)
 IRQ_HANDLER(IRQ206, 238)
 IRQ_HANDLER(IRQ207, 239)
-IRQ_HANDLER(IRQ208, 240)
-IRQ_HANDLER(IRQ209, 241)
-IRQ_HANDLER(IRQ210, 242)
-IRQ_HANDLER(IRQ211, 243)
-IRQ_HANDLER(IRQ212, 244)
+/* 0xf0 - start of the SMP_CALL IPIS */
+IRQ_HANDLER(IRQ208, I_SMP_CALL0)
+IRQ_HANDLER(IRQ209, I_SMP_CALL1)
+IRQ_HANDLER(IRQ210, I_SMP_CALL2)
+IRQ_HANDLER(IRQ211, I_SMP_CALL3)
+IRQ_HANDLER(IRQ212, I_SMP_CALL4)
 IRQ_HANDLER(IRQ213, 245)
 IRQ_HANDLER(IRQ214, 246)
 IRQ_HANDLER(IRQ215, 247)
@@ -127,10 +167,11 @@ IRQ_HANDLER(IRQ219, 251)
 IRQ_HANDLER(IRQ220, 252)
 IRQ_HANDLER(IRQ221, 253)
 IRQ_HANDLER(IRQ222, 254)
-IRQ_HANDLER(IRQ223, 255)
+IRQ_HANDLER(IRQ223, 255) # I_TESTING in testing.c
 
+/* Technically, these HANDLER entries do not need to be in numeric order */
 TRAPHANDLER_NOEC(ISR_syscall, T_SYSCALL)
-/* Make sure default is last!! */
+/* But make sure default is last!! */
 TRAPHANDLER_NOEC(ISR_default, T_DEFAULT)
 
 .data
index 7b25e23..ede1ed1 100644 (file)
@@ -88,14 +88,14 @@ env_set_program_counter(env_t* e, uintptr_t pc)
 }
 
 void
-env_init_trapframe(env_t* e)
+env_init_trapframe(trapframe_t *tf)
 {
        extern char trap_table;
 
-       e->env_tf.gpr[14] = USTACKTOP-64;
-       e->env_tf.psr = PSR_S; // but PS = 0
-       e->env_tf.wim = 0;
-       e->env_tf.tbr = (uint32_t)&trap_table;
+       tf->gpr[14] = USTACKTOP-64;
+       tf->psr = PSR_S; // but PS = 0
+       tf->wim = 0;
+       tf->tbr = (uint32_t)&trap_table;
 }
 
 // Flush all mapped pages in the user portion of the address space
index 2f93f79..a18f80a 100644 (file)
@@ -54,6 +54,7 @@ struct Env {
        uint32_t env_runs;                      // Number of times environment has run
        uint32_t env_refcnt;            // Reference count of kernel contexts using this
        uint32_t env_flags;
+       uint32_t env_entry;
 
 #ifdef __SHARC__
        // held spin-locks
@@ -87,7 +88,7 @@ extern env_t* curenvs[MAX_NUM_CPUS];
 
 void   env_init(void);
 int            env_alloc(env_t *SAFE*SAFE e, envid_t parent_id);
-void   env_init_trapframe(env_t* e);
+void   env_init_trapframe(trapframe_t *tf);
 void   env_set_program_counter(env_t* e, uintptr_t pc);
 void   env_push_ancillary_state(env_t* e);
 void   env_pop_ancillary_state(env_t* e);
index a4f42ff..134d96f 100644 (file)
@@ -27,7 +27,7 @@ struct kfs_entry {
        size_t size;
 };
 
-#define MAX_KFS_FILES 10
+#define MAX_KFS_FILES 20
 extern struct kfs_entry kfs[MAX_KFS_FILES];
 
 ssize_t kfs_lookup_path(char*NTS path);
index e911da9..617c8fd 100644 (file)
@@ -13,6 +13,7 @@
 #include <arch/types.h>
 #include <trap.h>
 #include <atomic.h>
+#include <process.h>
 #include <workqueue.h>
 #include <env.h>
 
 struct per_cpu_info {
        uint32_t lock;
        bool preempt_pending;
+       /* a proc_startcore IPI will run these.  replace with a dispatch map? */
+       struct proc *p_to_run;
+       trapframe_t *SAFE tf_to_pop;
        struct workqueue workqueue;
 };
-extern struct per_cpu_info  per_cpu_info[MAX_NUM_CPUS];
+extern struct per_cpu_info per_cpu_info[MAX_NUM_CPUS];
 extern volatile uint8_t num_cpus;
 
 /* SMP bootup functions */
index 9931c77..7c76abd 100644 (file)
@@ -16,6 +16,7 @@ typedef struct InterruptHandler {
        poly_isr_t isr;
        TV(t) data;
 } handler_t;
+extern handler_t interrupt_handlers[];
 
 void idt_init(void);
 void register_interrupt_handler(handler_t (COUNT(256)table)[], uint8_t int_num,
index cf22d26..bebaa3a 100644 (file)
@@ -44,6 +44,7 @@ KERN_APPFILES := \
                  $(USER_APPS_ROSLIB_DIR)/null \
                  $(USER_APPS_ROSLIB_DIR)/spawn \
                  $(USER_APPS_ROSLIB_DIR)/hello \
+                 $(USER_APPS_ROSLIB_DIR)/mhello \
                  $(USER_APPS_PARLIB_DIR)/channel_test_client \
                  $(USER_APPS_PARLIB_DIR)/channel_test_server \
                  $(USER_APPS_ROSLIB_DIR)/measurements \
index b063137..61b13fa 100644 (file)
@@ -164,7 +164,8 @@ WRITES(e->env_pgdir, e->env_cr3, e->env_procinfo, e->env_procdata)
        for(int i=0; i<PROCINFO_NUM_PAGES; i++) {
                if(page_alloc(&pginfo[i]) < 0)
                        goto env_setup_vm_error;
-               if(page_insert(e->env_pgdir, pginfo[i], (void*SNT)(UINFO + i*PGSIZE), PTE_USER_RO) < 0)
+               if(page_insert(e->env_pgdir, pginfo[i], (void*SNT)(UINFO + i*PGSIZE),
+                              PTE_USER_RO) < 0)
                        goto env_setup_vm_error;
        }
 
@@ -175,7 +176,8 @@ WRITES(e->env_pgdir, e->env_cr3, e->env_procinfo, e->env_procdata)
        for(int i=0; i<PROCDATA_NUM_PAGES; i++) {
                if(page_alloc(&pgdata[i]) < 0)
                        goto env_setup_vm_error;
-               if(page_insert(e->env_pgdir, pgdata[i], (void*SNT)(UDATA + i*PGSIZE), PTE_USER_RW) < 0)
+               if(page_insert(e->env_pgdir, pgdata[i], (void*SNT)(UDATA + i*PGSIZE),
+                              PTE_USER_RW) < 0)
                        goto env_setup_vm_error;
        }
 
@@ -270,6 +272,7 @@ env_alloc(env_t **newenv_store, envid_t parent_id)
        e->env_runs = 0;
        e->env_refcnt = 1;
        e->env_flags = 0;
+       e->env_entry = 0; // cheating.  this really gets set in load_icode
 
 #ifdef __SHARC__
        /* init SharC state */
@@ -278,7 +281,7 @@ env_alloc(env_t **newenv_store, envid_t parent_id)
 
        memset(&e->env_ancillary_state, 0, sizeof(e->env_ancillary_state));
        memset(&e->env_tf, 0, sizeof(e->env_tf));
-       env_init_trapframe(e);
+       env_init_trapframe(&e->env_tf);
 
        /*
         * Initialize the contents of the e->env_procinfo structure
@@ -411,6 +414,7 @@ load_icode(env_t *SAFE e, uint8_t *COUNT(size) binary, size_t size)
        }}
 
        env_set_program_counter(e, elfhdr.e_entry);
+       e->env_entry = elfhdr.e_entry;
 
        // Now map one page for the program's initial stack
        // at virtual address USTACKTOP - PGSIZE.
index 5df42c7..8ef1c4a 100644 (file)
@@ -29,6 +29,7 @@ DECL_PROG(roslib_fptest);
 DECL_PROG(roslib_null);
 DECL_PROG(roslib_spawn);
 DECL_PROG(roslib_hello);
+DECL_PROG(roslib_mhello);
 DECL_PROG(roslib_measurements);
 DECL_PROG(parlib_channel_test_client);
 DECL_PROG(parlib_channel_test_server);
@@ -41,6 +42,7 @@ struct kfs_entry kfs[MAX_KFS_FILES] = {
        KFS_ENTRY(roslib_null)
        KFS_ENTRY(roslib_spawn)
        KFS_ENTRY(roslib_hello)
+       KFS_ENTRY(roslib_mhello)
        KFS_ENTRY(roslib_measurements)
        KFS_ENTRY(parlib_channel_test_client)
        KFS_ENTRY(parlib_channel_test_server)
index 1748662..d7d33e5 100644 (file)
@@ -20,6 +20,7 @@
 #include <testing.h>
 #include <kfs.h>
 #include <stdio.h>
+#include <timing.h>
 
 /*
  * Currently, if you leave this function by way of proc_run (process_workqueue
 void manager(void)
 {
        static uint8_t progress = 0;
-       env_t *envs[256];
+       struct proc *envs[256];
+
+envs[0] = kfs_proc_create(kfs_lookup_path("roslib_mhello"));
+// being proper and all:
+proc_set_state(envs[0], PROC_RUNNABLE_S);
+proc_set_state(envs[0], PROC_RUNNING_S);
+proc_set_state(envs[0], PROC_RUNNABLE_M);
+proc_set_state(envs[0], PROC_RUNNING_M);
+for (int i = 1; i < 8; i++) {
+       per_cpu_info[i].p_to_run = envs[0];
+       //per_cpu_info[i].tf_to_pop = &envs[0]->env_tf; // starts the main core
+}
+// needed to make sure the writes beat the IPI.  could have done it with other
+// mem accesses, like grabbing a lock, and the write would have made it (no wmb() on
+// x86)
+asm volatile("sfence");
+// actually send the IPI to do this
+for (int i = 1; i < 8; i++)
+       send_ipi(i, 0, I_STARTCORE);
+
+udelay(5000000);
+panic("This is okay");
 
        switch (progress++) {
                case 0:
index 39c47a6..1cc59ac 100644 (file)
@@ -221,7 +221,7 @@ void proc_startcore(struct proc *p, trapframe_t *tf) {
         * We also need this to be per trapframe, and not per process...
         */
        env_pop_ancillary_state(p);
-       env_pop_tf(&p->env_tf);
+       env_pop_tf(tf);
 }
 
 /*
index a0edd20..0b90060 100644 (file)
@@ -15,6 +15,7 @@
 #include <string.h>
 #include <testing.h>
 #include <trap.h>
+#include <arch/trap.h>
 #include <process.h>
 #include <syscall.h>
 #include <timing.h>
@@ -23,8 +24,6 @@
 #include <pmap.h>
 #include <page_alloc.h>
 
-#define test_vector 0xeb
-
 #ifdef __i386__
 
 void test_ipi_sending(void)
@@ -32,35 +31,35 @@ void test_ipi_sending(void)
        extern handler_t (COUNT(NUM_INTERRUPT_HANDLERS) interrupt_handlers)[];
        int8_t state = 0;
 
-       register_interrupt_handler(interrupt_handlers, test_vector,
+       register_interrupt_handler(interrupt_handlers, I_TESTING,
                                   test_hello_world_handler, NULL);
        enable_irqsave(&state);
        cprintf("\nCORE 0 sending broadcast\n");
-       send_broadcast_ipi(test_vector);
+       send_broadcast_ipi(I_TESTING);
        udelay(3000000);
        cprintf("\nCORE 0 sending all others\n");
-       send_all_others_ipi(test_vector);
+       send_all_others_ipi(I_TESTING);
        udelay(3000000);
        cprintf("\nCORE 0 sending self\n");
-       send_self_ipi(test_vector);
+       send_self_ipi(I_TESTING);
        udelay(3000000);
        cprintf("\nCORE 0 sending ipi to physical 1\n");
-       send_ipi(0x01, 0, test_vector);
+       send_ipi(0x01, 0, I_TESTING);
        udelay(3000000);
        cprintf("\nCORE 0 sending ipi to physical 2\n");
-       send_ipi(0x02, 0, test_vector);
+       send_ipi(0x02, 0, I_TESTING);
        udelay(3000000);
        cprintf("\nCORE 0 sending ipi to physical 3\n");
-       send_ipi(0x03, 0, test_vector);
+       send_ipi(0x03, 0, I_TESTING);
        udelay(3000000);
        cprintf("\nCORE 0 sending ipi to physical 15\n");
-       send_ipi(0x0f, 0, test_vector);
+       send_ipi(0x0f, 0, I_TESTING);
        udelay(3000000);
        cprintf("\nCORE 0 sending ipi to logical 2\n");
-       send_ipi(0x02, 1, test_vector);
+       send_ipi(0x02, 1, I_TESTING);
        udelay(3000000);
        cprintf("\nCORE 0 sending ipi to logical 1\n");
-       send_ipi(0x01, 1, test_vector);
+       send_ipi(0x01, 1, I_TESTING);
        udelay(3000000);
        cprintf("\nDone!\n");
        disable_irqsave(&state);
@@ -466,13 +465,13 @@ void test_smp_call_functions(void)
 #ifdef __i386__
 void test_lapic_status_bit(void)
 {
-       register_interrupt_handler(interrupt_handlers, test_vector,
+       register_interrupt_handler(interrupt_handlers, I_TESTING,
                                   test_incrementer_handler, &a);
        #define NUM_IPI 100000
        atomic_set(&a,0);
        printk("IPIs received (should be 0): %d\n", a);
        for(int i = 0; i < NUM_IPI; i++) {
-               send_ipi(7, 0, test_vector);
+               send_ipi(7, 0, I_TESTING);
                lapic_wait_to_send();
        }
        // need to wait a bit to let those IPIs get there
@@ -701,7 +700,7 @@ void test_pit(void)
 
        atomic_t waiting;
        atomic_init(&waiting, 1);
-       register_interrupt_handler(interrupt_handlers, test_vector,
+       register_interrupt_handler(interrupt_handlers, I_TESTING,
                                   test_waiting_handler, &waiting);
        while(atomic_read(&waiting))
                cpu_relax();
diff --git a/user/apps/roslib/mhello.c b/user/apps/roslib/mhello.c
new file mode 100644 (file)
index 0000000..9aa3936
--- /dev/null
@@ -0,0 +1,9 @@
+#include <lib.h>
+#include <stdio.h>
+
+int main(int argc, char** argv)
+{
+       cprintf("Multi-Goodbye, world, from PID: %d!\n", sys_getpid());
+       while(1);
+       return 0;
+}
index 2f31fc5..9be10bb 100644 (file)
@@ -25,6 +25,10 @@ vpd:
 .text
 .globl _start
 _start:
+       // See if we are a new core
+       cmpl $1, %eax
+       je new_core
+
        // See if we were started with arguments on the stack
        cmpl $USTACKTOP, %esp
        jne args_exist
@@ -39,3 +43,5 @@ args_exist:
        call libmain
 1:      jmp 1b
 
+new_core:
+2:             jmp 2b