xref: /linux/arch/um/os-Linux/mem.c (revision 0d71832e3004a0833938cc28a096823cd55b8e79)
15134d8feSJeff Dike /*
25134d8feSJeff Dike  * Copyright (C) 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
35134d8feSJeff Dike  * Licensed under the GPL
45134d8feSJeff Dike  */
55134d8feSJeff Dike 
60f80bc85SJeff Dike #include <stdio.h>
70f80bc85SJeff Dike #include <stddef.h>
85134d8feSJeff Dike #include <stdlib.h>
90f80bc85SJeff Dike #include <unistd.h>
100f80bc85SJeff Dike #include <errno.h>
110f80bc85SJeff Dike #include <fcntl.h>
125134d8feSJeff Dike #include <string.h>
13fb967eccSLiu Aleaxander #include <sys/stat.h>
140f80bc85SJeff Dike #include <sys/mman.h>
15*0d71832eSTristan Schmelcher #include <sys/vfs.h>
16*0d71832eSTristan Schmelcher #include <linux/magic.h>
1737185b33SAl Viro #include <init.h>
1837185b33SAl Viro #include <os.h>
190f80bc85SJeff Dike 
20*0d71832eSTristan Schmelcher /* Set by make_tempfile() during early boot. */
210f80bc85SJeff Dike static char *tempdir = NULL;
220f80bc85SJeff Dike 
23*0d71832eSTristan Schmelcher /* Check if dir is on tmpfs. Return 0 if yes, -1 if no or error. */
24*0d71832eSTristan Schmelcher static int __init check_tmpfs(const char *dir)
250f80bc85SJeff Dike {
26*0d71832eSTristan Schmelcher 	struct statfs st;
270f80bc85SJeff Dike 
28*0d71832eSTristan Schmelcher 	printf("Checking if %s is on tmpfs...", dir);
29*0d71832eSTristan Schmelcher 	if (statfs(dir, &st) < 0) {
30*0d71832eSTristan Schmelcher 		printf("%s\n", strerror(errno));
31*0d71832eSTristan Schmelcher 	} else if (st.f_type != TMPFS_MAGIC) {
32*0d71832eSTristan Schmelcher 		printf("no\n");
3374735341STristan Schmelcher 	} else {
34966a082fSRob Landley 		printf("OK\n");
35*0d71832eSTristan Schmelcher 		return 0;
3674735341STristan Schmelcher 	}
37*0d71832eSTristan Schmelcher 	return -1;
3874735341STristan Schmelcher }
3974735341STristan Schmelcher 
40*0d71832eSTristan Schmelcher /*
41*0d71832eSTristan Schmelcher  * Choose the tempdir to use. We want something on tmpfs so that our memory is
42*0d71832eSTristan Schmelcher  * not subject to the host's vm.dirty_ratio. If a tempdir is specified in the
43*0d71832eSTristan Schmelcher  * environment, we use that even if it's not on tmpfs, but we warn the user.
44*0d71832eSTristan Schmelcher  * Otherwise, we try common tmpfs locations, and if no tmpfs directory is found
45*0d71832eSTristan Schmelcher  * then we fall back to /tmp.
46*0d71832eSTristan Schmelcher  */
47*0d71832eSTristan Schmelcher static char * __init choose_tempdir(void)
48*0d71832eSTristan Schmelcher {
49*0d71832eSTristan Schmelcher 	static const char * const vars[] = {
50*0d71832eSTristan Schmelcher 		"TMPDIR",
51*0d71832eSTristan Schmelcher 		"TMP",
52*0d71832eSTristan Schmelcher 		"TEMP",
53*0d71832eSTristan Schmelcher 		NULL
54*0d71832eSTristan Schmelcher 	};
55*0d71832eSTristan Schmelcher 	static const char fallback_dir[] = "/tmp";
56*0d71832eSTristan Schmelcher 	static const char * const tmpfs_dirs[] = {
57*0d71832eSTristan Schmelcher 		"/dev/shm",
58*0d71832eSTristan Schmelcher 		fallback_dir,
59*0d71832eSTristan Schmelcher 		NULL
60*0d71832eSTristan Schmelcher 	};
61*0d71832eSTristan Schmelcher 	int i;
62*0d71832eSTristan Schmelcher 	const char *dir;
63*0d71832eSTristan Schmelcher 
64*0d71832eSTristan Schmelcher 	printf("Checking environment variables for a tempdir...");
65*0d71832eSTristan Schmelcher 	for (i = 0; vars[i]; i++) {
66*0d71832eSTristan Schmelcher 		dir = getenv(vars[i]);
67*0d71832eSTristan Schmelcher 		if ((dir != NULL) && (*dir != '\0')) {
68*0d71832eSTristan Schmelcher 			printf("%s\n", dir);
69*0d71832eSTristan Schmelcher 			if (check_tmpfs(dir) >= 0)
70*0d71832eSTristan Schmelcher 				goto done;
71*0d71832eSTristan Schmelcher 			else
72*0d71832eSTristan Schmelcher 				goto warn;
73*0d71832eSTristan Schmelcher 		}
74*0d71832eSTristan Schmelcher 	}
75*0d71832eSTristan Schmelcher 	printf("none found\n");
76*0d71832eSTristan Schmelcher 
77*0d71832eSTristan Schmelcher 	for (i = 0; tmpfs_dirs[i]; i++) {
78*0d71832eSTristan Schmelcher 		dir = tmpfs_dirs[i];
79*0d71832eSTristan Schmelcher 		if (check_tmpfs(dir) >= 0)
80*0d71832eSTristan Schmelcher 			goto done;
81966a082fSRob Landley 	}
82966a082fSRob Landley 
83*0d71832eSTristan Schmelcher 	dir = fallback_dir;
84*0d71832eSTristan Schmelcher warn:
85*0d71832eSTristan Schmelcher 	printf("Warning: tempdir %s is not on tmpfs\n", dir);
86*0d71832eSTristan Schmelcher done:
87*0d71832eSTristan Schmelcher 	/* Make a copy since getenv results may not remain valid forever. */
88*0d71832eSTristan Schmelcher 	return strdup(dir);
89*0d71832eSTristan Schmelcher }
90*0d71832eSTristan Schmelcher 
91*0d71832eSTristan Schmelcher /*
92*0d71832eSTristan Schmelcher  * Create an unlinked tempfile in a suitable tempdir. template must be the
93*0d71832eSTristan Schmelcher  * basename part of the template with a leading '/'.
94*0d71832eSTristan Schmelcher  */
95*0d71832eSTristan Schmelcher static int __init make_tempfile(const char *template)
960f80bc85SJeff Dike {
9787276f72SPaolo 'Blaisorblade' Giarrusso 	char *tempname;
980f80bc85SJeff Dike 	int fd;
990f80bc85SJeff Dike 
100*0d71832eSTristan Schmelcher 	if (tempdir == NULL) {
101*0d71832eSTristan Schmelcher 		tempdir = choose_tempdir();
102*0d71832eSTristan Schmelcher 		if (tempdir == NULL) {
103*0d71832eSTristan Schmelcher 			fprintf(stderr, "Failed to choose tempdir: %s\n",
104*0d71832eSTristan Schmelcher 				strerror(errno));
105*0d71832eSTristan Schmelcher 			return -1;
106*0d71832eSTristan Schmelcher 		}
107*0d71832eSTristan Schmelcher 	}
108*0d71832eSTristan Schmelcher 
109*0d71832eSTristan Schmelcher 	tempname = malloc(strlen(tempdir) + strlen(template) + 1);
11011a7ac23SJim Meyering 	if (tempname == NULL)
11111a7ac23SJim Meyering 		return -1;
11287276f72SPaolo 'Blaisorblade' Giarrusso 
1130f80bc85SJeff Dike 	strcpy(tempname, tempdir);
114*0d71832eSTristan Schmelcher 	strcat(tempname, template);
1150f80bc85SJeff Dike 	fd = mkstemp(tempname);
1160f80bc85SJeff Dike 	if (fd < 0) {
1170f80bc85SJeff Dike 		fprintf(stderr, "open - cannot create %s: %s\n", tempname,
1180f80bc85SJeff Dike 			strerror(errno));
11987276f72SPaolo 'Blaisorblade' Giarrusso 		goto out;
1200f80bc85SJeff Dike 	}
121*0d71832eSTristan Schmelcher 	if (unlink(tempname) < 0) {
1220f80bc85SJeff Dike 		perror("unlink");
1232a6d0ac1SDavidlohr Bueso 		goto close;
1240f80bc85SJeff Dike 	}
12587276f72SPaolo 'Blaisorblade' Giarrusso 	free(tempname);
12681999a01SJeff Dike 	return fd;
1272a6d0ac1SDavidlohr Bueso close:
1282a6d0ac1SDavidlohr Bueso 	close(fd);
12987276f72SPaolo 'Blaisorblade' Giarrusso out:
13087276f72SPaolo 'Blaisorblade' Giarrusso 	free(tempname);
13187276f72SPaolo 'Blaisorblade' Giarrusso 	return -1;
1320f80bc85SJeff Dike }
1330f80bc85SJeff Dike 
134*0d71832eSTristan Schmelcher #define TEMPNAME_TEMPLATE "/vm_file-XXXXXX"
1350f80bc85SJeff Dike 
1365134d8feSJeff Dike static int __init create_tmp_file(unsigned long long len)
1370f80bc85SJeff Dike {
1380f80bc85SJeff Dike 	int fd, err;
1390f80bc85SJeff Dike 	char zero;
1400f80bc85SJeff Dike 
141*0d71832eSTristan Schmelcher 	fd = make_tempfile(TEMPNAME_TEMPLATE);
1425134d8feSJeff Dike 	if (fd < 0)
1430f80bc85SJeff Dike 		exit(1);
1440f80bc85SJeff Dike 
1450f80bc85SJeff Dike 	err = fchmod(fd, 0777);
1460f80bc85SJeff Dike 	if (err < 0) {
147512b6fb1SJeff Dike 		perror("fchmod");
1480f80bc85SJeff Dike 		exit(1);
1490f80bc85SJeff Dike 	}
1500f80bc85SJeff Dike 
1515134d8feSJeff Dike 	/*
1525134d8feSJeff Dike 	 * Seek to len - 1 because writing a character there will
153190f4939SJeff Dike 	 * increase the file size by one byte, to the desired length.
154190f4939SJeff Dike 	 */
155190f4939SJeff Dike 	if (lseek64(fd, len - 1, SEEK_SET) < 0) {
156512b6fb1SJeff Dike 		perror("lseek64");
1570f80bc85SJeff Dike 		exit(1);
1580f80bc85SJeff Dike 	}
1590f80bc85SJeff Dike 
1600f80bc85SJeff Dike 	zero = 0;
1610f80bc85SJeff Dike 
162a61f334fSJeff Dike 	err = write(fd, &zero, 1);
1630f80bc85SJeff Dike 	if (err != 1) {
164a61f334fSJeff Dike 		perror("write");
1650f80bc85SJeff Dike 		exit(1);
1660f80bc85SJeff Dike 	}
1670f80bc85SJeff Dike 
16881999a01SJeff Dike 	return fd;
1690f80bc85SJeff Dike }
1700f80bc85SJeff Dike 
17136e45463SJeff Dike int __init create_mem_file(unsigned long long len)
1720f80bc85SJeff Dike {
1730f80bc85SJeff Dike 	int err, fd;
1740f80bc85SJeff Dike 
17502dea087SJeff Dike 	fd = create_tmp_file(len);
1760f80bc85SJeff Dike 
177512b6fb1SJeff Dike 	err = os_set_exec_close(fd);
1780f80bc85SJeff Dike 	if (err < 0) {
1790f80bc85SJeff Dike 		errno = -err;
1800f80bc85SJeff Dike 		perror("exec_close");
1810f80bc85SJeff Dike 	}
18281999a01SJeff Dike 	return fd;
1830f80bc85SJeff Dike }
184966a082fSRob Landley 
18536e45463SJeff Dike void __init check_tmpexec(void)
186966a082fSRob Landley {
187966a082fSRob Landley 	void *addr;
188966a082fSRob Landley 	int err, fd = create_tmp_file(UM_KERN_PAGE_SIZE);
189966a082fSRob Landley 
190966a082fSRob Landley 	addr = mmap(NULL, UM_KERN_PAGE_SIZE,
191966a082fSRob Landley 		    PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, fd, 0);
192966a082fSRob Landley 	printf("Checking PROT_EXEC mmap in %s...", tempdir);
193966a082fSRob Landley 	if (addr == MAP_FAILED) {
194966a082fSRob Landley 		err = errno;
195*0d71832eSTristan Schmelcher 		printf("%s\n", strerror(err));
196c9a3072dSWANG Cong 		close(fd);
197966a082fSRob Landley 		if (err == EPERM)
198966a082fSRob Landley 			printf("%s must be not mounted noexec\n", tempdir);
199966a082fSRob Landley 		exit(1);
200966a082fSRob Landley 	}
201966a082fSRob Landley 	printf("OK\n");
202966a082fSRob Landley 	munmap(addr, UM_KERN_PAGE_SIZE);
203966a082fSRob Landley 
204966a082fSRob Landley 	close(fd);
205966a082fSRob Landley }
206