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