1 /*
2 * Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 /*
8 * @file linux/init.c
9 * @brief Linux libmetal initialization.
10 */
11
12 #include <sys/types.h>
13
14 #include <metal/sys.h>
15 #include <metal/utilities.h>
16
17 struct metal_state _metal;
18
19 extern int metal_linux_irq_init(void);
20 extern void metal_linux_irq_shutdown(void);
21
22 /** Sort function for page size array. */
metal_pagesize_compare(const void * _a,const void * _b)23 static int metal_pagesize_compare(const void *_a, const void *_b)
24 {
25 const struct metal_page_size *a = _a, *b = _b;
26 long diff = a->page_size - b->page_size;
27
28 return metal_sign(diff);
29 }
30
metal_add_page_size(const char * path,int shift,int mmap_flags)31 static int metal_add_page_size(const char *path, int shift, int mmap_flags)
32 {
33 int index = _metal.num_page_sizes;
34 unsigned long size = 1UL << shift;
35
36 if (index >= MAX_PAGE_SIZES) {
37 metal_log(METAL_LOG_WARNING, "skipped page size %ld - overflow\n",
38 size);
39 return -EOVERFLOW;
40 }
41
42 if (!path || shift <= 0) {
43 metal_log(METAL_LOG_WARNING, "skipped page size %ld - invalid args\n",
44 size);
45 return -EINVAL;
46 }
47
48 _metal.page_sizes[index].page_shift = shift;
49 _metal.page_sizes[index].page_size = size;
50 _metal.page_sizes[index].mmap_flags = mmap_flags;
51 strncpy(_metal.page_sizes[index].path, path, PATH_MAX);
52 _metal.num_page_sizes++;
53
54 metal_log(METAL_LOG_DEBUG, "added page size %ld @%s\n", size, path);
55
56 return 0;
57 }
58
metal_init_page_sizes(void)59 static int metal_init_page_sizes(void)
60 {
61 const int max_sizes = MAX_PAGE_SIZES - 1;
62 long sizes[max_sizes];
63
64 /* Determine system page size. */
65 sizes[0] = getpagesize();
66 if (sizes[0] <= 0) {
67 metal_log(METAL_LOG_ERROR, "failed to get page size\n");
68 return -EINVAL;
69 }
70 _metal.page_size = sizes[0];
71 _metal.page_shift = metal_log2(sizes[0]);
72 metal_add_page_size(_metal.tmp_path, _metal.page_shift, 0);
73
74 #ifdef HAVE_HUGETLBFS_H
75 #ifndef MAP_HUGE_SHIFT
76 /* System does not support multiple huge page sizes. */
77 sizes[0] = gethugepagesize();
78 if (sizes[0] > 0) {
79 metal_add_page_size(hugetlbfs_find_path(),
80 metal_log2(sizes[0]),
81 MAP_HUGETLB);
82 }
83 #else
84 if (gethugepagesize() >= 0) {
85 int i, count;
86
87 /* System supports multiple huge page sizes. */
88 count = gethugepagesizes(sizes, max_sizes);
89 for (i = 0; i < count; i++) {
90 int shift = metal_log2(sizes[i]);
91
92 if ((shift & MAP_HUGE_MASK) != shift)
93 continue;
94 metal_add_page_size(
95 hugetlbfs_find_path_for_size(sizes[i]),
96 shift, (MAP_HUGETLB |
97 (shift << MAP_HUGE_SHIFT)));
98 }
99 }
100 #endif
101 #endif
102
103 /* Finally sort the resulting array by size. */
104 qsort(_metal.page_sizes, _metal.num_page_sizes,
105 sizeof(struct metal_page_size), metal_pagesize_compare);
106
107 return 0;
108 }
109
metal_sys_init(const struct metal_init_params * params)110 int metal_sys_init(const struct metal_init_params *params)
111 {
112 const char *tmp_path;
113 unsigned int seed;
114 FILE *urandom;
115 int result;
116
117 /* Find the temporary directory location. */
118 tmp_path = getenv("TMPDIR");
119 if (!tmp_path)
120 tmp_path = "/tmp";
121 _metal.tmp_path = tmp_path;
122
123 /* Initialize the pseudo-random number generator. */
124 urandom = fopen("/dev/urandom", "r");
125 if (!urandom) {
126 metal_log(METAL_LOG_ERROR, "failed to open /dev/urandom (%s)\n",
127 strerror(errno));
128 return -errno;
129 }
130 if (fread(&seed, 1, sizeof(seed), urandom) <= 0) {
131 metal_log(METAL_LOG_DEBUG, "Failed fread /dev/urandom\n");
132 }
133 fclose(urandom);
134 srand(seed);
135
136 result = metal_init_page_sizes();
137 if (result < 0)
138 return result;
139
140 result = metal_linux_bus_init();
141 if (result < 0)
142 return result;
143
144 result = open("/proc/self/pagemap", O_RDONLY | O_CLOEXEC);
145 if (result < 0) {
146 metal_log(METAL_LOG_DEBUG, "Failed pagemap open - %s\n",
147 strerror(errno));
148 }
149 _metal.pagemap_fd = result;
150
151 metal_unused(params);
152
153 /* Initialize IRQ handling */
154 metal_linux_irq_init();
155 return 0;
156 }
157
metal_sys_finish(void)158 void metal_sys_finish(void)
159 {
160
161 /* Shutdown IRQ handling */
162 metal_linux_irq_shutdown();
163 metal_linux_bus_finish();
164 close(_metal.pagemap_fd);
165
166 }
167