1/*
2 *  memory_tests.c.c
3 *  xnu_quick_test
4 *
5 *  Created by Jerry Cottingham on 4/12/05.
6 *  Copyright 2005 Apple Computer Inc. All rights reserved.
7 *
8 */
9
10#include "tests.h"
11#include <mach/mach.h>
12
13extern char  g_target_path[ PATH_MAX ];
14
15/*
16 * static to localize to this compilation unit; volatile to avoid register
17 * optimization which would prevent modification by a signal handler.
18 */
19static volatile int	my_err;
20
21void
22bus_handler(int sig, siginfo_t *si, void *mcontext)
23{
24	/* Reset global error value when we see a SIGBUS */
25	if (sig == SIGBUS) {
26		_exit(0);
27	}
28}
29
30/*  **************************************************************************************************************
31 *	Test madvise, mincore, minherit, mlock, mlock, mmap, mprotect, msync, munmap system calls.
32 *	todo - see if Francois has better versions of these tests...
33 *  **************************************************************************************************************
34 */
35int memory_tests( void * the_argp )
36{
37	int			my_page_size, my_status;
38	int			my_fd = -1;
39	char *		my_pathp = NULL;
40	char *		my_bufp = NULL;
41	char *		my_addr = NULL;
42	char *		my_test_page_p = NULL;
43	ssize_t		my_result;
44	pid_t		my_pid, my_wait_pid;
45	kern_return_t   my_kr;
46	struct sigaction	my_sa;
47
48        my_kr = vm_allocate((vm_map_t) mach_task_self(), (vm_address_t*)&my_pathp, PATH_MAX, VM_FLAGS_ANYWHERE);
49        if(my_kr != KERN_SUCCESS){
50                printf( "vm_allocate failed with error %d - \"%s\" \n", errno, strerror( errno) );
51                goto test_failed_exit;
52        }
53
54	*my_pathp = 0x00;
55	strcat( my_pathp, &g_target_path[0] );
56	strcat( my_pathp, "/" );
57
58	/* create a test file */
59	my_err = create_random_name( my_pathp, 1 );
60	if ( my_err != 0 ) {
61		goto test_failed_exit;
62	}
63
64	my_page_size = getpagesize( );
65	my_kr = vm_allocate((vm_map_t) mach_task_self(), (vm_address_t*)&my_test_page_p, my_page_size, VM_FLAGS_ANYWHERE);
66        if(my_kr != KERN_SUCCESS){
67                printf( "vm_allocate failed with error %d - \"%s\" \n", errno, strerror( errno) );
68                goto test_failed_exit;
69        }
70
71	*my_test_page_p = 0x00;
72	strcat( my_test_page_p, "parent data" );
73
74	/* test minherit - share a page with child, add to the string in child then
75	 * check for modification after child terminates.
76	 */
77	my_err = minherit( my_test_page_p, my_page_size, VM_INHERIT_SHARE );
78	if ( my_err == -1 ) {
79		printf( "minherit failed with error %d - \"%s\" \n", errno, strerror( errno) );
80		goto test_failed_exit;
81	}
82
83	/*
84	 * spin off a child process that we will use for testing.
85	 */
86	my_pid = fork( );
87	if ( my_pid == -1 ) {
88		printf( "fork failed with errno %d - %s \n", errno, strerror( errno ) );
89		goto test_failed_exit;
90	}
91	if ( my_pid == 0 ) {
92		/*
93		 * child process...
94		 */
95		strcat( my_test_page_p, " child data" );
96
97		/* create a test file in page size chunks */
98       		my_kr = vm_allocate((vm_map_t) mach_task_self(), (vm_address_t*)&my_bufp, (my_page_size * 10), VM_FLAGS_ANYWHERE);
99	        if(my_kr != KERN_SUCCESS){
100        	        printf( "vm_allocate failed with error %d - \"%s\" \n", errno, strerror( errno) );
101			my_err = -1;
102                	goto exit_child;
103        	}
104
105		/* test madvise on anonymous memory */
106		my_err = madvise(my_bufp, (my_page_size * 10), MADV_WILLNEED);
107		if ( my_err == -1 ) {
108			printf("madvise WILLNEED on anon memory failed with error %d - \"%s\" \n", errno, strerror( errno ) );
109			my_err = -1;
110			goto exit_child;
111		}
112
113		memset( my_bufp, 'j', (my_page_size * 10) );
114		my_fd = open( my_pathp, O_RDWR, 0 );
115		if ( my_fd == -1 ) {
116			printf( "open call failed with error %d - \"%s\" \n", errno, strerror( errno) );
117			my_err = -1;
118			goto exit_child;
119		}
120
121		/* test madvise on anonymous memory */
122		my_err = madvise(my_bufp, (my_page_size * 10), MADV_DONTNEED);
123		if ( my_err == -1 ) {
124			printf("madvise DONTNEED on anon memory failed with error %d - \"%s\" \n", errno, strerror( errno ) );
125			my_err = -1;
126			goto exit_child;
127		}
128
129		my_result = write( my_fd, my_bufp, (my_page_size * 10) );
130		if ( my_result == -1 ) {
131			printf( "write call failed with error %d - \"%s\" \n", errno, strerror( errno) );
132			my_err = -1;
133			goto exit_child;
134		}
135
136		/* map the file into memory */
137		my_addr = (char *) mmap( NULL, (my_page_size * 2), (PROT_READ | PROT_WRITE), (MAP_FILE | MAP_SHARED), my_fd, 0 );
138		if ( my_addr == (char *) -1 ) {
139			printf( "mmap call failed with error %d - \"%s\" \n", errno, strerror( errno) );
140			my_err = -1;
141			goto exit_child;
142		}
143
144		/* make sure we got the right data mapped */
145		if ( *my_addr != 'j' || *(my_addr + my_page_size) != 'j' ) {
146			printf( "did not map in correct data \n" );
147			my_err = -1;
148			goto exit_child;
149		}
150
151		/* test madvise */
152		my_err = madvise( my_addr, (my_page_size * 2), MADV_WILLNEED );
153		if ( my_err == -1 ) {
154			printf( "madvise WILLNEED call failed with error %d - \"%s\" \n", errno, strerror( errno) );
155			my_err = -1;
156			goto exit_child;
157		}
158
159		my_err = madvise( my_addr, (my_page_size * 2), MADV_DONTNEED );
160		if ( my_err == -1 ) {
161			printf( "madvise DONTNEED call failed with error %d - \"%s\" \n", errno, strerror( errno) );
162			my_err = -1;
163			goto exit_child;
164		}
165
166		/* test mincore, mlock, mlock */
167		my_err = mlock( my_addr, my_page_size );
168		if ( my_err == -1 ) {
169			printf( "mlock call failed with error %d - \"%s\" \n", errno, strerror( errno) );
170			my_err = -1;
171			goto exit_child;
172		}
173
174		/* mybufp is about to be reused, so test madvise on anonymous memory */
175		my_err = madvise(my_bufp, (my_page_size * 10), MADV_FREE);
176		if ( my_err == -1 ) {
177			printf("madvise FREE on anon memory failed with error %d - \"%s\" \n", errno, strerror( errno ) );
178			my_err = -1;
179			goto exit_child;
180		}
181
182		my_err = mincore( my_addr, 1, my_bufp );
183		if ( my_err == -1 ) {
184			printf( "mincore call failed with error %d - \"%s\" \n", errno, strerror( errno) );
185			my_err = -1;
186			goto exit_child;
187		}
188		/* page my_addr is in should be resident after mlock */
189		if ( (*my_bufp & MINCORE_INCORE) == 0 ) {
190			printf( "mincore call failed to find resident page \n" );
191			my_err = -1;
192			goto exit_child;
193		}
194
195		my_err = munlock( my_addr, my_page_size );
196		if ( my_err == -1 ) {
197			printf( "munlock call failed with error %d - \"%s\" \n", errno, strerror( errno) );
198			my_err = -1;
199			goto exit_child;
200		}
201
202		/* modify first page then use msync to push data out */
203		memset( my_addr, 'x', my_page_size );
204		my_err = msync( my_addr, my_page_size, (MS_SYNC | MS_INVALIDATE) );
205		if ( my_err == -1 ) {
206			printf( "msync call failed with error %d - \"%s\" \n", errno, strerror( errno) );
207			my_err = -1;
208			goto exit_child;
209		}
210
211		/* test madvise */
212		my_err = madvise( my_addr, (my_page_size * 2), MADV_DONTNEED );
213		if ( my_err == -1 ) {
214			printf( "madvise DONTNEED call failed with error %d - \"%s\" \n", errno, strerror( errno) );
215			my_err = -1;
216			goto exit_child;
217		}
218
219		/* test madvise */
220		my_err = madvise( my_addr, (my_page_size * 2), MADV_FREE );
221		if ( my_err == -1 ) {
222			printf( "madvise FREE call failed with error %d - \"%s\" \n", errno, strerror( errno) );
223			my_err = -1;
224			goto exit_child;
225		}
226
227		/* verify that the file was updated */
228		lseek( my_fd, 0, SEEK_SET );
229		bzero( (void *)my_bufp, my_page_size );
230		my_result = read( my_fd, my_bufp, my_page_size );
231		if ( my_result == -1 ) {
232			printf( "read call failed with error %d - \"%s\" \n", errno, strerror( errno) );
233			my_err = -1;
234			goto exit_child;
235		}
236		if ( *my_bufp != 'x' ) {
237			printf( "msync did not flush correct data \n" );
238			my_err = -1;
239			goto exit_child;
240		}
241
242		/* unmap our test page */
243		my_err = munmap( my_addr, (my_page_size * 2) );
244		if ( my_err == -1 ) {
245			printf( "munmap call failed with error %d - \"%s\" \n", errno, strerror( errno) );
246			my_err = -1;
247			goto exit_child;
248		}
249		my_addr = NULL;
250
251		/* map the file into memory again for mprotect test  */
252		my_addr = (char *) mmap( NULL, (my_page_size * 2), (PROT_READ | PROT_WRITE), (MAP_FILE | MAP_SHARED), my_fd, 0 );
253		if ( my_addr == (char *) -1 ) {
254			printf( "mmap call failed with error %d - \"%s\" \n", errno, strerror( errno) );
255			my_err = -1;
256			goto exit_child;
257		}
258		*my_addr = 'a';
259
260
261
262		/* test mprotect - change protection to only PROT_READ */
263		my_err = mprotect( my_addr, my_page_size, PROT_READ );
264		if ( my_err == -1 ) {
265			printf( "mprotect call failed with error %d - \"%s\" \n", errno, strerror( errno) );
266			my_err = -1;
267			goto exit_child;
268		}
269
270		my_sa.sa_sigaction = bus_handler;
271		my_sa.sa_flags = SA_SIGINFO | SA_RESETHAND;
272		if ((my_err = sigaction(SIGBUS, &my_sa, NULL)) != 0) {
273			printf("sigaction call failed with error %d - \"%s\" \n", errno, strerror( errno) );
274			my_err = -1;
275			goto exit_child;
276		}
277
278		my_err = -1;	/* default to error out if we do NOT trigger a SIGBUS */
279
280		*my_addr = 'z'; /* should cause SIGBUS signal (we look for this at child termination within the parent) */
281
282		/* NOTREACHED */
283
284		printf("Expected SIGBUS signal, got nothing!\n");
285		my_err = -1;
286exit_child:
287		exit( my_err );
288	}
289
290	/* parent process -
291	 * we should get no error if the child has completed all tests successfully
292	 */
293	my_wait_pid = wait4( my_pid, &my_status, 0, NULL );
294	if ( my_wait_pid == -1 ) {
295		printf( "wait4 failed with errno %d - %s \n", errno, strerror( errno ) );
296		goto test_failed_exit;
297	}
298
299	/* wait4 should return our child's pid when it exits */
300	if ( my_wait_pid != my_pid ) {
301		printf( "wait4 did not return child pid - returned %d should be %d \n", my_wait_pid, my_pid );
302		goto test_failed_exit;
303	}
304
305	/* If we did not exit cleanly, report it
306	 */
307	if ( !WIFEXITED( my_status ) || (WEXITSTATUS( my_status ) != 0)) {
308		printf( "wait4 returned child died of status - 0x%08X \n", my_status );
309		goto test_failed_exit;
310	}
311
312	/* make sure shared page got modified in child */
313	if ( strcmp( my_test_page_p, "parent data child data" ) != 0 ) {
314		printf( "minherit did not work correctly - shared page looks wrong \n" );
315		goto test_failed_exit;
316	}
317	my_err = 0;
318	goto test_passed_exit;
319
320test_failed_exit:
321	my_err = -1;
322
323test_passed_exit:
324	if ( my_pathp != NULL ) {
325		remove( my_pathp );
326		vm_deallocate(mach_task_self(), (vm_address_t)my_pathp, PATH_MAX);
327	 }
328	 if ( my_test_page_p != NULL ) {
329		vm_deallocate(mach_task_self(), (vm_address_t)my_test_page_p, my_page_size);
330	 }
331	return( my_err );
332}
333
334