Moved BIOS mmap cleanup to shared x86 code. Call from both x86_64 and x86_32 startup...
authorSimon Gerber <simon.gerber@inf.ethz.ch>
Wed, 29 Oct 2014 13:40:03 +0000 (14:40 +0100)
committerSimon Gerber <simon.gerber@inf.ethz.ch>
Wed, 29 Oct 2014 14:03:45 +0000 (15:03 +0100)
kernel/arch/x86/startup_x86.c
kernel/arch/x86_32/startup_arch.c
kernel/arch/x86_64/page_mappings_arch.c
kernel/arch/x86_64/startup_arch.c
kernel/include/arch/x86/startup_x86.h

index f461d7e..b51cbc7 100644 (file)
@@ -233,6 +233,247 @@ void create_module_caps(struct spawn_state *st)
     }
 }
 
+void cleanup_bios_regions(char *mmap_addr, char **new_mmap_addr,
+                          uint32_t *new_mmap_length)
+{
+    assert(new_mmap_addr);
+    assert(new_mmap_length);
+#define PRINT_REGIONS(map, map_length) do {\
+        for(char * printcur = map; printcur < map + map_length;) {\
+            struct multiboot_mmap * printcurmmap = (struct multiboot_mmap * SAFE)TC(printcur);\
+            printf("\t0x%08"PRIx64" - 0x%08"PRIx64" Type: %"PRIu32" Length: 0x%"PRIx64"\n", printcurmmap->base_addr, printcurmmap->base_addr + printcurmmap->length, printcurmmap->type, printcurmmap->length);\
+            printcur += printcurmmap->size + 4;\
+        }\
+    } while (0)
+
+    printf("Raw MMAP from BIOS\n");
+    PRINT_REGIONS(mmap_addr, glbl_core_data->mmap_length);
+
+    // normalize memory regions
+    lpaddr_t clean_base = bsp_alloc_phys(glbl_core_data->mmap_length);
+    char *clean_mmap_addr = (char *)local_phys_to_mem(clean_base);
+    uint32_t clean_mmap_length = glbl_core_data->mmap_length;
+    memcpy(clean_mmap_addr, mmap_addr, glbl_core_data->mmap_length);
+
+    // first of all, sort regions by base address
+    // yes, it's a bubble sort, but the dataset is small and usually in the right order
+    bool swapped;
+    do {
+        swapped = false;
+
+        for(char * cur = clean_mmap_addr; cur < clean_mmap_addr + clean_mmap_length;) {
+            struct multiboot_mmap * curmmap = (struct multiboot_mmap * SAFE)TC(cur);
+            if (cur + curmmap->size + 4 >= clean_mmap_addr + clean_mmap_length)
+                break; // do not try to move this check into the forloop as entries do not have to be the same length
+
+            struct multiboot_mmap * nextmmap = (struct multiboot_mmap * SAFE)TC(cur + curmmap->size + 4);
+
+            if (nextmmap->base_addr < curmmap->base_addr ||
+                (nextmmap->base_addr == curmmap->base_addr && nextmmap->length > curmmap->length)) {
+                // swap
+                assert(curmmap->size == 20); // FIXME: The multiboot specification does not require this size
+                assert(nextmmap->size == 20);
+
+                struct multiboot_mmap tmp;
+                tmp = *curmmap;
+                *curmmap = *nextmmap;
+                *nextmmap = tmp;
+
+                swapped = true;
+            }
+
+            cur += curmmap->size + 4;
+        }
+    } while(swapped);
+
+    printf("Sorted MMAP\n");
+    PRINT_REGIONS(clean_mmap_addr, clean_mmap_length);
+
+    // now merge consecutive memory regions of the same or lower type
+    for(char * cur = clean_mmap_addr; cur < clean_mmap_addr + clean_mmap_length;) {
+        struct multiboot_mmap * curmmap = (struct multiboot_mmap * SAFE)TC(cur);
+        if (cur + curmmap->size + 4 >= clean_mmap_addr + clean_mmap_length)
+            break; // do not try to move this check into the forloop as entries do not have to be the same length
+
+        struct multiboot_mmap * nextmmap = (struct multiboot_mmap * SAFE)TC(cur + curmmap->size + 4);
+
+        /* On some machines (brie1) the IOAPIC region is only 1kB.
+         * Currently we're not able to map regions that are <4kB so we
+         * make sure that every region (if there is no problematic overlap)
+         * is at least BASE_PAGE_SIZEd (==4kB) here.
+         */
+        if ((curmmap->length < BASE_PAGE_SIZE) && (curmmap->base_addr + BASE_PAGE_SIZE <= nextmmap->base_addr)) {
+            curmmap->length = BASE_PAGE_SIZE;
+        }
+
+#define DISCARD_NEXT_MMAP do {\
+    uint32_t discardsize = nextmmap->size + 4;\
+    memmove(cur + curmmap->size + 4, cur + curmmap->size + 4 + discardsize, clean_mmap_length - (cur - clean_mmap_addr) - curmmap->size - 4 - discardsize);\
+    clean_mmap_length -= discardsize;\
+    } while (0)
+
+#define BUBBLE_NEXT_MMAP do {\
+    for (char * bubblecur = cur + curmmap->size + 4; bubblecur < clean_mmap_addr + clean_mmap_length;){\
+        struct multiboot_mmap * bubblecur_mmap = (struct multiboot_mmap * SAFE)TC(bubblecur);\
+        if (bubblecur + bubblecur_mmap->size + 4 >= clean_mmap_addr + clean_mmap_length)\
+            break;\
+        struct multiboot_mmap * bubblenext_mmap = (struct multiboot_mmap * SAFE)TC(bubblecur + bubblecur_mmap->size + 4);\
+        if (bubblenext_mmap->base_addr < bubblecur_mmap->base_addr || (bubblecur_mmap->base_addr == bubblenext_mmap->base_addr && bubblenext_mmap->length > bubblecur_mmap->length)) {\
+            struct multiboot_mmap bubbletmp; bubbletmp = *bubblecur_mmap; *bubblecur_mmap = *bubblenext_mmap; *bubblenext_mmap = bubbletmp;\
+        } else break;\
+    }} while(0)
+
+
+        bool reduced = false;
+        do {
+            reduced = false;
+
+            if (curmmap->base_addr == nextmmap->base_addr) {
+                // regions start at the same location
+                if (curmmap->length == nextmmap->length) {
+                    // trivial case. They are the same. Choose higher type and discard next
+                    curmmap->type = max(curmmap->type, nextmmap->type);
+
+                    DISCARD_NEXT_MMAP;
+
+                    reduced = true;
+                    continue;
+                } else {
+                    // next region is smaller (we sorted that way)
+                    if (nextmmap->type <= curmmap->type) {
+                        // next regions type is the same or smaller. discard
+                        DISCARD_NEXT_MMAP;
+
+                        reduced = true;
+                        continue;
+                    } else {
+                        // next regions type is higher, so it gets priority
+                        // change type of current region and shrink next
+                        uint32_t tmptype = curmmap->type;
+                        uint64_t newlength = curmmap->length - nextmmap->length;
+                        curmmap->type = nextmmap->type;
+                        curmmap->length = nextmmap->length;
+                        nextmmap->type = tmptype;
+                        nextmmap->base_addr += nextmmap->length;
+                        nextmmap->length = newlength;
+
+                        // now we need to bubble next to the right place to restore order
+                        BUBBLE_NEXT_MMAP;
+
+                        reduced = true;
+                        continue;
+                    }
+                }
+            }
+
+            // regions overlap
+            if (nextmmap->base_addr > curmmap->base_addr && nextmmap->base_addr < curmmap->base_addr + curmmap->length) {
+                // same type
+                if (curmmap->type == nextmmap->type) {
+                    // simple. just extend if necessary and discard next
+                    if (nextmmap->base_addr + nextmmap->length > curmmap->base_addr + curmmap->length)
+                        curmmap->length = (nextmmap->base_addr + nextmmap->length) - curmmap->base_addr;
+
+                    DISCARD_NEXT_MMAP;
+
+                    reduced = true;
+                    continue;
+                } else {
+                    // type is not the same
+                    if (nextmmap->base_addr + nextmmap->length < curmmap->base_addr + curmmap->length) {
+                        // there is a chunk at the end. create a new region
+                        struct multiboot_mmap tmpmmap;
+                        tmpmmap.size = 20;
+                        tmpmmap.base_addr = nextmmap->base_addr + nextmmap->length;
+                        tmpmmap.length = (curmmap->base_addr + curmmap->length) - (nextmmap->base_addr + nextmmap->length);
+                        tmpmmap.type = curmmap->type;
+
+                        // move everything to make room
+                        assert(clean_mmap_length + tmpmmap.length + 4 < BOOTINFO_SIZE);
+                        memmove(cur + curmmap->size + 4 + tmpmmap.size + 4, cur + curmmap->size + 4, clean_mmap_length - ((cur - clean_mmap_addr) + curmmap->size + 4));
+                        clean_mmap_length += tmpmmap.size + 4;
+
+                        // insert new
+                        *nextmmap = tmpmmap;
+
+                        // restore order
+                        BUBBLE_NEXT_MMAP;
+
+                        reduced = true;
+                    }
+
+                    // after the previous step, the next region either ends
+                    // at the same location as the current or is longer
+                    uint64_t overlap = (curmmap->base_addr + curmmap->length) - nextmmap->base_addr;
+
+                    if (curmmap-> type > nextmmap->type) {
+                        // current has priority, shrink next and extend current
+                        nextmmap->length -= overlap;
+                        nextmmap->base_addr += overlap;
+                        curmmap->length += overlap;
+
+                        if (nextmmap->length == 0)
+                            DISCARD_NEXT_MMAP;
+
+                        reduced = true;
+                        continue;
+                    } else {
+                        // next has priority, shrink current and extend next
+                        nextmmap->length += overlap;
+                        nextmmap->base_addr -= overlap;
+                        curmmap->length -= overlap;
+
+                        reduced = true;
+                        continue;
+                    }
+                }
+            }
+        } while (reduced);
+
+        cur += curmmap->size + 4;
+
+#undef DISCARD_NEXT_MMAP
+#undef BUBBLE_NEXT_MMAP
+    }
+
+    printf("Preprocessed MMAP\n");
+    PRINT_REGIONS(clean_mmap_addr, clean_mmap_length);
+
+    // we can only map pages. Therefore page align regions
+    for(char * cur = clean_mmap_addr; cur < clean_mmap_addr + clean_mmap_length;) {
+        struct multiboot_mmap * curmmap = (struct multiboot_mmap * SAFE)TC(cur);
+        if (cur + curmmap->size + 4 >= clean_mmap_addr + clean_mmap_length)
+            break; // do not try to move this check into the forloop as entries do not have to be the same length
+
+        struct multiboot_mmap * nextmmap = (struct multiboot_mmap * SAFE)TC(cur + curmmap->size + 4);
+
+        if (nextmmap->base_addr & BASE_PAGE_MASK) {
+            uint64_t offset = nextmmap->base_addr - ((nextmmap->base_addr >> BASE_PAGE_BITS) << BASE_PAGE_BITS);
+
+            // round in favour of higher type
+            if (curmmap->type > nextmmap->type) {
+                curmmap->length += BASE_PAGE_SIZE - offset;
+                nextmmap->base_addr += BASE_PAGE_SIZE - offset;
+                nextmmap->length -= BASE_PAGE_SIZE - offset;
+            } else {
+                curmmap->length -= offset;
+                nextmmap->base_addr -= offset;
+                nextmmap->length += offset;
+            }
+        }
+
+        cur += curmmap->size + 4;
+    }
+
+    printf("Pagealigned MMAP\n");
+    PRINT_REGIONS(clean_mmap_addr, clean_mmap_length);
+
+#undef PRINT_REGIONS
+    *new_mmap_addr = clean_mmap_addr;
+    *new_mmap_length = clean_mmap_length;
+    return;
+}
+
 // XXX from serial.c
 extern int serial_portbase;
 
index da8fa7f..f54d791 100644 (file)
@@ -163,6 +163,11 @@ static void create_phys_caps(lpaddr_t init_alloc_addr)
     char *mmap_addr = MBADDR_ASSTRING(glbl_core_data->mmap_addr);
     genpaddr_t last_end_addr = 0;
 
+    char *clean_mmap_addr;
+    uint32_t clean_mmap_length;
+    cleanup_bios_regions(mmap_addr, &clean_mmap_addr, &clean_mmap_length);
+
+
     for(char *m = mmap_addr; m < mmap_addr + glbl_core_data->mmap_length;) {
         struct multiboot_mmap *mmap = (struct multiboot_mmap * SAFE)TC(m);
 
index 6b2ccc4..6d1ceb1 100644 (file)
@@ -306,7 +306,7 @@ static inline void read_pt_entry(struct capability *pgtable, size_t slot,
     }
 }
 
-__attribute__(("unused"))
+__attribute__((unused))
 static inline lvaddr_t get_leaf_ptable_for_vaddr(genvaddr_t vaddr)
 {
     lvaddr_t root_pt = local_phys_to_mem(dcb_current->vspace);
index 079bc9a..765676e 100644 (file)
@@ -154,238 +154,9 @@ static void create_phys_caps(lpaddr_t init_alloc_addr)
     char *mmap_addr = MBADDR_ASSTRING(glbl_core_data->mmap_addr);
     lpaddr_t last_end_addr = 0;
 
-#define PRINT_REGIONS(map, map_length) do {\
-        for(char * printcur = map; printcur < map + map_length;) {\
-            struct multiboot_mmap * printcurmmap = (struct multiboot_mmap * SAFE)TC(printcur);\
-            printf("\t0x%08lx - 0x%08lx Type: %d Length: 0x%lx\n", printcurmmap->base_addr, printcurmmap->base_addr + printcurmmap->length, printcurmmap->type, printcurmmap->length);\
-            printcur += printcurmmap->size + 4;\
-        }\
-    } while (0)
-
-    printf("Raw MMAP from BIOS\n");
-    PRINT_REGIONS(mmap_addr, glbl_core_data->mmap_length);
-
-    // normalize memory regions
-    char *clean_mmap_addr = MBADDR_ASSTRING(init_alloc_addr); // FIXME: Hack!!!! TODO: properly get memory form somewhere
-    assert(glbl_core_data->mmap_length < BOOTINFO_SIZE);
-    uint32_t clean_mmap_length = glbl_core_data->mmap_length;
-
-    memcpy(clean_mmap_addr, mmap_addr, glbl_core_data->mmap_length);
-
-    // first of all, sort regions by base address
-    // yes, it's a bubble sort, but the dataset is small and usually in the right order
-    bool swapped;
-    do {
-        swapped = false;
-
-        for(char * cur = clean_mmap_addr; cur < clean_mmap_addr + clean_mmap_length;) {
-            struct multiboot_mmap * curmmap = (struct multiboot_mmap * SAFE)TC(cur);
-            if (cur + curmmap->size + 4 >= clean_mmap_addr + clean_mmap_length)
-                break; // do not try to move this check into the forloop as entries do not have to be the same length
-
-            struct multiboot_mmap * nextmmap = (struct multiboot_mmap * SAFE)TC(cur + curmmap->size + 4);
-
-            if (nextmmap->base_addr < curmmap->base_addr ||
-                (nextmmap->base_addr == curmmap->base_addr && nextmmap->length > curmmap->length)) {
-                // swap
-                assert(curmmap->size == 20); // FIXME: The multiboot specification does not require this size
-                assert(nextmmap->size == 20);
-
-                struct multiboot_mmap tmp;
-                tmp = *curmmap;
-                *curmmap = *nextmmap;
-                *nextmmap = tmp;
-
-                swapped = true;
-            }
-
-            cur += curmmap->size + 4;
-        }
-    } while(swapped);
-
-    printf("Sorted MMAP\n");
-    PRINT_REGIONS(clean_mmap_addr, clean_mmap_length);
-
-    // now merge consecutive memory regions of the same or lower type
-    for(char * cur = clean_mmap_addr; cur < clean_mmap_addr + clean_mmap_length;) {
-        struct multiboot_mmap * curmmap = (struct multiboot_mmap * SAFE)TC(cur);
-        if (cur + curmmap->size + 4 >= clean_mmap_addr + clean_mmap_length)
-            break; // do not try to move this check into the forloop as entries do not have to be the same length
-
-        struct multiboot_mmap * nextmmap = (struct multiboot_mmap * SAFE)TC(cur + curmmap->size + 4);
-
-        /* On some machines (brie1) the IOAPIC region is only 1kB.
-         * Currently we're not able to map regions that are <4kB so we
-         * make sure that every region (if there is no problematic overlap)
-         * is at least BASE_PAGE_SIZEd (==4kB) here.
-         */
-        if ((curmmap->length < BASE_PAGE_SIZE) && (curmmap->base_addr + BASE_PAGE_SIZE <= nextmmap->base_addr)) {
-            curmmap->length = BASE_PAGE_SIZE;
-        }
-
-#define DISCARD_NEXT_MMAP do {\
-    uint32_t discardsize = nextmmap->size + 4;\
-    memmove(cur + curmmap->size + 4, cur + curmmap->size + 4 + discardsize, clean_mmap_length - (cur - clean_mmap_addr) - curmmap->size - 4 - discardsize);\
-    clean_mmap_length -= discardsize;\
-    } while (0)
-
-#define BUBBLE_NEXT_MMAP do {\
-    for (char * bubblecur = cur + curmmap->size + 4; bubblecur < clean_mmap_addr + clean_mmap_length;){\
-        struct multiboot_mmap * bubblecur_mmap = (struct multiboot_mmap * SAFE)TC(bubblecur);\
-        if (bubblecur + bubblecur_mmap->size + 4 >= clean_mmap_addr + clean_mmap_length)\
-            break;\
-        struct multiboot_mmap * bubblenext_mmap = (struct multiboot_mmap * SAFE)TC(bubblecur + bubblecur_mmap->size + 4);\
-        if (bubblenext_mmap->base_addr < bubblecur_mmap->base_addr || (bubblecur_mmap->base_addr == bubblenext_mmap->base_addr && bubblenext_mmap->length > bubblecur_mmap->length)) {\
-            struct multiboot_mmap bubbletmp; bubbletmp = *bubblecur_mmap; *bubblecur_mmap = *bubblenext_mmap; *bubblenext_mmap = bubbletmp;\
-        } else break;\
-    }} while(0)
-
-
-        bool reduced = false;
-        do {
-            reduced = false;
-
-            if (curmmap->base_addr == nextmmap->base_addr) {
-                // regions start at the same location
-                if (curmmap->length == nextmmap->length) {
-                    // trivial case. They are the same. Choose higher type and discard next
-                    curmmap->type = max(curmmap->type, nextmmap->type);
-
-                    DISCARD_NEXT_MMAP;
-
-                    reduced = true;
-                    continue;
-                } else {
-                    // next region is smaller (we sorted that way)
-                    if (nextmmap->type <= curmmap->type) {
-                        // next regions type is the same or smaller. discard
-                        DISCARD_NEXT_MMAP;
-
-                        reduced = true;
-                        continue;
-                    } else {
-                        // next regions type is higher, so it gets priority
-                        // change type of current region and shrink next
-                        uint32_t tmptype = curmmap->type;
-                        uint64_t newlength = curmmap->length - nextmmap->length;
-                        curmmap->type = nextmmap->type;
-                        curmmap->length = nextmmap->length;
-                        nextmmap->type = tmptype;
-                        nextmmap->base_addr += nextmmap->length;
-                        nextmmap->length = newlength;
-
-                        // now we need to bubble next to the right place to restore order
-                        BUBBLE_NEXT_MMAP;
-
-                        reduced = true;
-                        continue;
-                    }
-                }
-            }
-
-            // regions overlap
-            if (nextmmap->base_addr > curmmap->base_addr && nextmmap->base_addr < curmmap->base_addr + curmmap->length) {
-                // same type
-                if (curmmap->type == nextmmap->type) {
-                    // simple. just extend if necessary and discard next
-                    if (nextmmap->base_addr + nextmmap->length > curmmap->base_addr + curmmap->length)
-                        curmmap->length = (nextmmap->base_addr + nextmmap->length) - curmmap->base_addr;
-
-                    DISCARD_NEXT_MMAP;
-
-                    reduced = true;
-                    continue;
-                } else {
-                    // type is not the same
-                    if (nextmmap->base_addr + nextmmap->length < curmmap->base_addr + curmmap->length) {
-                        // there is a chunk at the end. create a new region
-                        struct multiboot_mmap tmpmmap;
-                        tmpmmap.size = 20;
-                        tmpmmap.base_addr = nextmmap->base_addr + nextmmap->length;
-                        tmpmmap.length = (curmmap->base_addr + curmmap->length) - (nextmmap->base_addr + nextmmap->length);
-                        tmpmmap.type = curmmap->type;
-
-                        // move everything to make room
-                        assert(clean_mmap_length + tmpmmap.length + 4 < BOOTINFO_SIZE);
-                        memmove(cur + curmmap->size + 4 + tmpmmap.size + 4, cur + curmmap->size + 4, clean_mmap_length - ((cur - clean_mmap_addr) + curmmap->size + 4));
-                        clean_mmap_length += tmpmmap.size + 4;
-
-                        // insert new
-                        *nextmmap = tmpmmap;
-
-                        // restore order
-                        BUBBLE_NEXT_MMAP;
-
-                        reduced = true;
-                    }
-
-                    // after the previous step, the next region either ends
-                    // at the same location as the current or is longer
-                    uint64_t overlap = (curmmap->base_addr + curmmap->length) - nextmmap->base_addr;
-
-                    if (curmmap-> type > nextmmap->type) {
-                        // current has priority, shrink next and extend current
-                        nextmmap->length -= overlap;
-                        nextmmap->base_addr += overlap;
-                        curmmap->length += overlap;
-
-                        if (nextmmap->length == 0)
-                            DISCARD_NEXT_MMAP;
-
-                        reduced = true;
-                        continue;
-                    } else {
-                        // next has priority, shrink current and extend next
-                        nextmmap->length += overlap;
-                        nextmmap->base_addr -= overlap;
-                        curmmap->length -= overlap;
-
-                        reduced = true;
-                        continue;
-                    }
-                }
-            }
-        } while (reduced);
-
-        cur += curmmap->size + 4;
-
-#undef DISCARD_NEXT_MMAP
-#undef BUBBLE_NEXT_MMAP
-    }
-
-    printf("Preprocessed MMAP\n");
-    PRINT_REGIONS(clean_mmap_addr, clean_mmap_length);
-
-    // we can only map pages. Therefore page align regions
-    for(char * cur = clean_mmap_addr; cur < clean_mmap_addr + clean_mmap_length;) {
-        struct multiboot_mmap * curmmap = (struct multiboot_mmap * SAFE)TC(cur);
-        if (cur + curmmap->size + 4 >= clean_mmap_addr + clean_mmap_length)
-            break; // do not try to move this check into the forloop as entries do not have to be the same length
-
-        struct multiboot_mmap * nextmmap = (struct multiboot_mmap * SAFE)TC(cur + curmmap->size + 4);
-
-        if (nextmmap->base_addr & BASE_PAGE_MASK) {
-            uint64_t offset = nextmmap->base_addr - ((nextmmap->base_addr >> BASE_PAGE_BITS) << BASE_PAGE_BITS);
-
-            // round in favour of higher type
-            if (curmmap->type > nextmmap->type) {
-                curmmap->length += BASE_PAGE_SIZE - offset;
-                nextmmap->base_addr += BASE_PAGE_SIZE - offset;
-                nextmmap->length -= BASE_PAGE_SIZE - offset;
-            } else {
-                curmmap->length -= offset;
-                nextmmap->base_addr -= offset;
-                nextmmap->length += offset;
-            }
-        }
-
-        cur += curmmap->size + 4;
-    }
-
-    printf("Pagealigned MMAP\n");
-    PRINT_REGIONS(clean_mmap_addr, clean_mmap_length);
-
-#undef PRINT_REGIONS
+    char *clean_mmap_addr;
+    uint32_t clean_mmap_length;
+    cleanup_bios_regions(mmap_addr, &clean_mmap_addr, &clean_mmap_length);
 
     for(char *m = clean_mmap_addr; m < clean_mmap_addr + clean_mmap_length;) {
         struct multiboot_mmap *mmap = (struct multiboot_mmap * SAFE)TC(m);
index 6c118f9..8d179e2 100644 (file)
@@ -31,6 +31,8 @@ errval_t startup_map_init(lvaddr_t vbase, lpaddr_t base, size_t size,
 errval_t startup_alloc_init(void *state, genvaddr_t gvbase, size_t size,
                             uint32_t flags, void **ret);
 void create_module_caps(struct spawn_state *st);
+void cleanup_bios_regions(char *mmap_addr, char **new_mmap_addr,
+                          uint32_t *new_mmap_length);
 
 struct dcb *spawn_bsp_init(const char *name, alloc_phys_func alloc_phys);
 struct dcb *spawn_app_init(struct x86_core_data *core_data,