/* * Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ /* * @file linux/init.c * @brief Linux libmetal initialization. */ #include #include #include struct metal_state _metal; extern int metal_linux_irq_init(); extern void metal_linux_irq_shutdown(); /** Sort function for page size array. */ static int metal_pagesize_compare(const void *_a, const void *_b) { const struct metal_page_size *a = _a, *b = _b; long diff = a->page_size - b->page_size; return metal_sign(diff); } static int metal_add_page_size(const char *path, int shift, int mmap_flags) { int index = _metal.num_page_sizes; unsigned long size = 1UL << shift; if (index >= MAX_PAGE_SIZES) { metal_log(METAL_LOG_WARNING, "skipped page size %ld - overflow\n", size); return -EOVERFLOW; } if (!path || shift <= 0) { metal_log(METAL_LOG_WARNING, "skipped page size %ld - invalid args\n", size); return -EINVAL; } _metal.page_sizes[index].page_shift = shift; _metal.page_sizes[index].page_size = size; _metal.page_sizes[index].mmap_flags = mmap_flags; strncpy(_metal.page_sizes[index].path, path, PATH_MAX); _metal.num_page_sizes ++; metal_log(METAL_LOG_DEBUG, "added page size %ld @%s\n", size, path); return 0; } static int metal_init_page_sizes(void) { const int max_sizes = MAX_PAGE_SIZES - 1; long sizes[max_sizes]; /* Determine system page size. */ sizes[0] = getpagesize(); if (sizes[0] <= 0) { metal_log(METAL_LOG_ERROR, "failed to get page size\n"); return -ENOSYS; } _metal.page_size = sizes[0]; _metal.page_shift = metal_log2(sizes[0]); metal_add_page_size(_metal.tmp_path, _metal.page_shift, 0); #ifdef HAVE_HUGETLBFS_H #ifndef MAP_HUGE_SHIFT /* System does not support multiple huge page sizes. */ sizes[0] = gethugepagesize(); if (sizes[0] > 0) { metal_add_page_size(hugetlbfs_find_path(), metal_log2(sizes[0]), MAP_HUGETLB); } #else if (gethugepagesize() >= 0) { int i, count; /* System supports multiple huge page sizes. */ count = gethugepagesizes(sizes, max_sizes); for (i = 0; i < count; i++) { int shift = metal_log2(sizes[i]); if ((shift & MAP_HUGE_MASK) != shift) continue; metal_add_page_size( hugetlbfs_find_path_for_size(sizes[i]), shift, (MAP_HUGETLB | (shift << MAP_HUGE_SHIFT))); } } #endif #endif /* Finally sort the resulting array by size. */ qsort(_metal.page_sizes, _metal.num_page_sizes, sizeof(struct metal_page_size), metal_pagesize_compare); return 0; } int metal_sys_init(const struct metal_init_params *params) { static char sysfs_path[SYSFS_PATH_MAX]; const char *tmp_path; unsigned int seed; FILE* urandom; int result; /* Determine sysfs mount point. */ result = sysfs_get_mnt_path(sysfs_path, sizeof(sysfs_path)); if (result) { metal_log(METAL_LOG_ERROR, "failed to get sysfs path (%s)\n", strerror(-result)); return result; } _metal.sysfs_path = sysfs_path; /* Find the temporary directory location. */ tmp_path = getenv("TMPDIR"); if (!tmp_path) tmp_path = "/tmp"; _metal.tmp_path = tmp_path; /* Initialize the pseudo-random number generator. */ urandom = fopen("/dev/urandom", "r"); if (!urandom) { metal_log(METAL_LOG_ERROR, "failed to open /dev/urandom (%s)\n", strerror(errno)); return -errno; } if (fread(&seed, 1, sizeof(seed), urandom) <= 0) { metal_log(METAL_LOG_DEBUG, "Failed fread /dev/urandom\n"); } fclose(urandom); srand(seed); result = metal_init_page_sizes(); if (result < 0) return result; result = metal_linux_bus_init(); if (result < 0) return result; result = open("/proc/self/pagemap", O_RDONLY | O_CLOEXEC); if (result < 0) { metal_log(METAL_LOG_DEBUG, "Failed pagemap open - %s\n", strerror(errno)); } _metal.pagemap_fd = result; metal_unused(params); /* Initialize IRQ handling */ metal_linux_irq_init(); return 0; } void metal_sys_finish(void) { /* Shutdown IRQ handling */ metal_linux_irq_shutdown(); metal_linux_bus_finish(); close(_metal.pagemap_fd); }