~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Minix Cross Reference
Minix/kernel/sunprotect.c


  1 static char rcsid[] = "$Id: sunprotect.c,v 1.6 1997/06/18 19:28:47 paul Exp $";
  2 
  3 /*
  4  * This file contains the majority of the code for handling smx memory
  5  * protection, and also deals with mapping and unmapping of user processes
  6  * to and from the low memory addresses at which they have been linked to
  7  * execute. 
  8  *
  9  * There are three levels of protection: level 0 ('none') - no protection;
 10  * level 1 ('half') - r-x text segments for kernel, mm, fs and init
 11  * in "physical" memory, and for the mapped text segment of the current
 12  * user process; level 2 ('full') - full protection.  With full protection,
 13  * a process may not access smx memory outside its own address space,
 14  * with the exception of a small part of the kernel (eg. the layer 1
 15  * interrupt stack). Level 1 is the default.  Level 2 slows execution down
 16  * a lot, but is valuable in tracking down certain type of memory
 17  * corruption bugs. 
 18  *
 19  * The main entry points are:
 20  *      protect_init:           initialise protection info
 21  *      mem_released:           called when a process' addr space is released
 22  *      set_protect:            set protection on a region
 23  *
 24  *      entering_kernel:        called when switching to kernel
 25  *      leaving_kernel:         called when switching from kernel.
 26  *            Associated functions are mprotect, kernel_on, kernel_off,
 27  *                              set_process_prot, map_process, unmap_process
 28  *
 29  *      zeroclicks:             wrapper for the real zeroclicks
 30  *      copyclicks:             wrapper for the real copyclicks
 31  *      phys_copy:              wrapper for the real phys_copy
 32  *
 33  * These wrapper functions look after enabling access before the copy
 34  * and disabling access after the copy.
 35  */
 36  
 37 #include "kernel.h"
 38 #include "const.h"
 39 #include <minix/com.h>
 40 #include <sun/syscall.h>
 41 #include "assert.h"
 42 
 43 #include "proc.h"
 44 
 45 /*
 46  * For protection purposes, the size of the kernel stack must be a
 47  * multiple of the click size.
 48  */
 49 #if K_STACK_BYTES % CLICK_SIZE != 0 
 50 error "The kernel stack is not a multiple of the click size" 
 51 #endif 
 52 
 53 #define max(x,y) ((x) > (y) ? (x) : (y))
 54 
 55 /* SunOS's protection codes for mprotect(2) */
 56 
 57 #define PROT_READ        0x1       /* page can be read */
 58 #define PROT_WRITE       0x2       /* page can be written */
 59 #define PROT_EXEC        0x4       /* page can be executed */
 60 #define PROT_NONE        0x0       /* page can not be accessed */
 61 #define PROT_ALL         (PROT_READ | PROT_WRITE | PROT_EXEC)
 62 #define PROT_READ_EXEC   (PROT_READ | PROT_EXEC)
 63 
 64 
 65 /*
 66  * The information in (see below) is used to partition the kernel address
 67  * space into separate areas, each of which has some protection mode
 68  * when execution is outside the kernel and full protection is enabled.
 69  * Details of each area are held in a prot_info struct.
 70  */
 71 struct prot_info {
 72     phys_bytes begin;      /* Beginning of area */
 73     int len;               /* Length of area */
 74     int prot;              /* Permissions on the area */
 75 };
 76 
 77 /*
 78  * In the worst case, we need 2 * NUM_SEGS + 1 prot_info structs.
 79  * This occurs when there are gaps before and after each of the
 80  * areas that is to remain accessible.
 81  */
 82 #define SEGMENTS (11)
 83 
 84 
 85 /*
 86  * The protect_info structure contains all information needed
 87  * to protect and de-protect the kernel.  It must remain
 88  * readable when execution is outside the kernel so that when
 89  * a signal occurs the information needed to turn on access to the
 90  * kernel is available.  prot_info specifies what the kernel
 91  * protections should be with execution outside the kernel.
 92  */
 93 struct protect_info {
 94     int prot_lev;
 95     phys_bytes kernel_base;
 96     phys_bytes kernel_text;
 97     phys_bytes kernel_data;
 98     struct prot_info prot_info[SEGMENTS];   
 99     int numsegs;                    /* number of prot_info entries in use */
100     int in_kernel;                  /* is execution in the kernel? */
101 } pinfo;
102 
103 /*
104  * Current user proc mapped into low virtual memory.
105  * Used to avoid mapping a process in that is already mapped in.
106  */
107 static struct proc *mapped_proc = 0;
108 
109 
110 /*
111  * We need to know about these function addresses to set kernel protections
112  * when execution is outside the kernel.
113  */
114 extern int SunOS();
115 extern void SunOSend(), mpx_start(), mpx_end();
116 static void protect_start(void);
117 static void protect_end(void);
118 
119 /*
120  * A seg_info struct records protection information for an address
121  * range.  The segs array specifies all areas that must remain
122  * accessible in some way when execution is outside the kernel.  All
123  * stack, data and text needed to get to the point where kernel_on has
124  * finished its work must be available.  This means that the following
125  * must be accessible: the interrupt stack, the interrupt handler
126  * (SunOSsig), the SunOS function that performs system calls, the
127  * relevant code in this file, and the pinfo structure.  The fewer of
128  * these segments the faster context switching in full protection will
129  * be.  About the only thing that would be quite easy to do would be
130  * to have the relevant functions from mpx.c and this file in a single
131  * .c file.
132  */
133 struct seg_info {
134         phys_bytes begin, end;
135         int prot;
136 };
137 
138 struct seg_info segs[] = {
139         {0, 0, PROT_ALL},      /* entry for kernel stack */
140         {(phys_bytes)protect_start, (phys_bytes)protect_end, PROT_READ_EXEC},
141         {(phys_bytes)mpx_start, (phys_bytes)mpx_end, PROT_READ_EXEC},
142         {(phys_bytes)SunOS, (phys_bytes)SunOSend, PROT_READ_EXEC},
143         {(phys_bytes)&pinfo, (phys_bytes)&pinfo + sizeof(pinfo), PROT_READ},
144 };
145 
146 #define SEGS_SIZE (sizeof(segs) / sizeof(struct seg_info))
147 
148 
149 /*
150  * Local functions.
151  */
152 static void mprotect(phys_bytes addr, int len, int prot);
153 static void kernel_on(void);
154 static void kernel_off(void);
155 static void set_process_prot(struct proc *p,
156                              int text_prot, int data_stack_prot);
157 static void map_process(struct proc *p);
158 static void unmap_process(struct proc *p);
159 
160 
161 /*
162  * Function: protect_init
163  * Parameters: kernel_base - virtual address that the kernel begins at
164  *             kernel_text - kernel text segment size (in clicks)
165  *             kernel_data - kernel data segment size (in clicks)
166  *             mem_clicks - total smx "physical" memory (in clocks)
167  *             level - protection level (0, 1 or 2).
168  *
169  * Called during system bootstrapping.  Data structures and memory protection
170  * are initialised according to the protection level specified.
171  */
172 void protect_init(phys_bytes kernel_base, phys_clicks kernel_text,
173                   phys_clicks kernel_data, phys_clicks mem_clicks,
174                   int level)
175 {
176     phys_bytes kernel_end;      /* first click after kernel */
177     phys_bytes next_click;
178     int i, j;
179     register struct proc *rp;
180     
181     pinfo.prot_lev = level;
182     debug_str("Protection level ");
183     debug_int(pinfo.prot_lev);
184     debug_char('\n');
185 
186     if (pinfo.prot_lev == FULL_PROT) {
187         /*
188          * Full protection.  Start by recording details of the kernel
189          * address space (needed when enabling access to the kernel.
190          */
191         pinfo.kernel_base = kernel_base;
192         pinfo.kernel_text = kernel_text << CLICK_SHIFT;
193         pinfo.kernel_data = kernel_data << CLICK_SHIFT;
194 
195         pinfo.in_kernel = 1;
196         kernel_end = pinfo.kernel_base + pinfo.kernel_text + pinfo.kernel_data;
197         
198         /*
199          * Update details in segs.  Details of the kernel stack are
200          * added, and all entries are aligned to click boundaries
201          */                     
202         segs[0].end = upclick(getksp());
203         segs[0].begin = segs[0].end - K_STACK_BYTES;
204         for (i = 0 ; i < SEGS_SIZE ; i++) {
205             segs[i].begin = downclick(segs[i].begin);
206             segs[i].end = upclick(segs[i].end);
207         }
208         
209         /*
210          * Sort segments by beginning address.  Segments need to be sorted
211          * for the next step to work.
212          */
213         {
214             int done = 0;
215             struct seg_info temp;
216             
217             while (!done) {
218                 done = 1;
219                 for (i = 0; i < SEGS_SIZE - 1; i++) {
220                     if (segs[i].begin > segs[i+1].begin) {
221                         temp = segs[i];
222                         segs[i] = segs[i+1];
223                         segs[i+1] = temp;
224                         done = 0;
225                     }
226                 }
227             }
228         }
229         
230         /*
231          * The entries in segs cover only parts of the kernel address
232          * space.  The entries in pinfo.prot_info collectively cover
233          * the entire kernel address space, so that when access to the
234          * kernel is disabled we have information on what to do for all
235          * kernel areas.  We now generate pinfo.prot_info from segs.
236          * This is basically a copying operating, with extra entries
237          * added to pinfo.prot_info to cover gaps in segs, and overlapping
238          * segs entries merged.
239          */
240         j = -1;      /* j is the index into pinfo */
241         next_click = pinfo.kernel_base;
242         for (i = 0 ; i < SEGS_SIZE ; i++) {
243             /*
244              * Each time around the loop deal with an element of segs.
245              */
246             if (segs[i].begin > next_click) {
247                 /*
248                  * There's a gap between this special segment
249                  * and the previous one - fill it with a no go area.
250                  */
251                 j++;
252                 pinfo.prot_info[j].begin = next_click;
253                 pinfo.prot_info[j].len = segs[i].begin - next_click;
254                 pinfo.prot_info[j].prot = PROT_NONE;
255             }
256 
257             if (segs[i].begin < next_click) {
258                 /*
259                  * An overlap - merge into a single segment,
260                  * with protection as weak as required.
261                  */
262                 pinfo.prot_info[j].len = max(pinfo.prot_info[j].len,
263                                        segs[i].end - pinfo.prot_info[j].begin);
264                 pinfo.prot_info[j].prot |= segs[i].prot;
265             } else {
266                 /*
267                  * No overlap - create a new segment
268                  */
269                 j++;
270                 pinfo.prot_info[j].begin = segs[i].begin;
271                 pinfo.prot_info[j].len = segs[i].end - segs[i].begin;
272                 pinfo.prot_info[j].prot = segs[i].prot;
273             }
274             next_click = pinfo.prot_info[j].begin + pinfo.prot_info[j].len;
275         }
276         
277         /*
278          * Is there a gap after the last segs entry?
279          */
280         if (next_click != kernel_end) {
281             j++;
282             pinfo.prot_info[j].begin = next_click;
283             pinfo.prot_info[j].len = kernel_end - next_click;
284             pinfo.prot_info[j].prot = PROT_NONE;
285         }
286 
287         pinfo.numsegs = j + 1;
288         assert(pinfo.numsegs < SEGMENTS);
289         
290         /*
291          * Dump the pinfo struct if debugging is on
292          */
293         debug_str("kbase: "); debug_int(pinfo.kernel_base);
294         debug_str("\nktext: "); debug_int(pinfo.kernel_text);
295         debug_str("\nkdata: "); debug_int(pinfo.kernel_data);
296         for (i=0 ; i < pinfo.numsegs ; i++) {
297             debug_str("\nbegin: "); debug_int(pinfo.prot_info[i].begin);
298             debug_str("\nlen: "); debug_int(pinfo.prot_info[i].len);
299             debug_str("\nprot: "); debug_int(pinfo.prot_info[i].prot);
300         }
301         debug_char('\n');
302 
303         /*
304          * Now that the data structures are set up, setup the current
305          * protection so that there is normal access to the kernel, and
306          * no access to the rest of the SunOS Minix address space.
307          */
308         kernel_on();
309         mprotect(kernel_end, (mem_clicks << CLICK_SHIFT) -
310                  (pinfo.kernel_text + pinfo.kernel_data), PROT_NONE);
311                  
312     } else if (pinfo.prot_lev == HALF_PROT) {
313         /* 
314          * For half protection, we need to make the kernel, MM, FS, INET and
315          * init text segments read-exec only.
316          */
317      
318         mprotect(kernel_base,  kernel_text << CLICK_SHIFT, PROT_READ_EXEC);
319         for (rp = BEG_SERV_ADDR; rp <= BEG_USER_ADDR; rp++){
320             mprotect(rp->p_map[T].mem_phys << CLICK_SHIFT,
321                      rp->p_map[T].mem_len << CLICK_SHIFT,
322                      PROT_READ_EXEC);
323         }
324     } /* prot_lev == NO_PROT means no protection; do nothing. */
325 }
326 
327 
328 /*
329  * Function: mem_released
330  * Parameter: p - process whose memory is being released.
331  * Returns: nothing
332  *
333  * Called when the address space of a process is about to be released
334  * (process has called exec or exit).  This gives the protection/mapping
335  * module a chance to remove any cached information.
336  */
337 void mem_released(struct proc *p)
338 {
339     unmap_process(p);
340     if (gwin_proc == p) {
341         gwin_proc = 0;
342     }
343 }
344 
345 
346 /*
347  * Function: set_protect
348  * Parameters: start - starting address of the area whose protection is to
349  *                     be changed.
350  *             len - length of area.
351  *             protection to assign to area.
352  *
353  * Set the protection for an area of memory.  The area might not be
354  * click aligned, so the alignment must be performed before mprotect can be
355  * called.
356  */
357 void set_protect(phys_bytes start, int len, int prot)
358 {
359     phys_bytes prot_start;
360     int prot_len;
361     
362     /*
363      * Only for full protection.  In lower level protection the only areas
364      * of "physical" memory that are (at half protection) not writable are
365      * the text segments of kernel, mm, fs and init, which are loaded by
366      * the bootstrap and then never changed.
367      */
368     if (pinfo.prot_lev == FULL_PROT) {
369         /*
370          * If the area is part of the kernel then just quietly ignore this
371          * request as the required access should already be possible.
372          */
373         if (start > pinfo.kernel_base && start < pinfo.kernel_base +
374             pinfo.kernel_text + pinfo.kernel_data) {
375             return;
376         }
377         
378         /*
379          * Click align the area whose protection is to be changed.
380          */
381         prot_start = downclick(start);
382         prot_len = upclick(start + len) - prot_start;
383         mprotect(prot_start, prot_len, prot);
384     }
385 }
386 
387 
388 /*
389  * DO NOT MOVE THIS FUNCTION---IT MARKS THE BEGINNING OF THE AREA OF CODE
390  * THAT MUST BE EXECUTABLE WHEN EXECUTION IS OUTSIDE THE KERNEL.
391  */
392 static void protect_start(void) {}
393 
394 
395 /*
396  * Function: mprotect
397  *
398  * This is a wrapper for the SunOS mprotect(2) system call.  Errors
399  * are reported to the console.
400  */
401 static void mprotect(phys_bytes addr, int len, int prot)
402 {
403     typedef char *caddr_t;
404 
405     if (SunOS(SYS_mprotect, (caddr_t) addr, len, prot) == -1) {
406         printk("mprotect failed: address 0x%x, len 0x%x, prot 0%o\n",
407                addr, len, prot);
408     }
409 }
410 
411 
412 /*
413  * Function: entering_kernel
414  *
415  * Called by SunOSsig to turn on full access to the kernel so that a
416  * SunOS signal can be handled.
417  */
418 void entering_kernel(void)
419 {
420     /*
421      * Note - only need to change protection if full protection is enabled
422      * and we were outside the kernel to start with.
423      */
424     if (pinfo.prot_lev == FULL_PROT && !pinfo.in_kernel) {
425         kernel_on();
426         pinfo.in_kernel = 1;
427 
428         /*
429          * mm and fs are executed "in place" in "physical" memory (to avoid
430          * mapping and unmapping costs.  We need to disable access to their
431          * portions of physical memory.  For user processes, the physical
432          * memory containing the process has PROT_NONE access during,
433          * so no changes in "physical" memory protection are needed.  Instead,
434          * the user process is unmapped from low memory where it has been
435          * executing.  This is not strictly necessary, as these mappings will
436          * be over-ridden by the next user process is mapped in, but it gives
437          * some added protection against rogue memory accesses.
438          */
439         if (isuserp(proc_ptr)) {
440             unmap_process(proc_ptr);
441         } else {
442             set_process_prot(proc_ptr, PROT_NONE, PROT_NONE);
443         }
444     }
445     else unmap_process(proc_ptr);
446 }
447 
448 
449 /*
450  * Function: leaving_kernel
451  *
452  * Called just prior to leaving layer 1 and resuming an smx process
453  * in layers 2-4.  If execution is switching to a user process, then
454  * that process is mapped into low memory.  If full protection is enabled,
455  * leaving_kernel makes the kernel address space as inaccessible as
456  * possible.
457  */
458 void leaving_kernel(void)
459 {
460     if (isuserp(proc_ptr)) {
461         map_process(proc_ptr);
462     }
463     
464     if (pinfo.prot_lev == FULL_PROT) {
465         /*
466          * Note - only change protection if execution is leaving the kernel,
467          * and full protection is enabled.  If switching to a layer 2 task
468          * then execution is remaining withing the kernel.
469          */
470         if (!istaskp(proc_ptr) && !isidlehardware(proc_number(proc_ptr))) {
471             pinfo.in_kernel = 0;
472             if (!isuserp(proc_ptr)) {
473                 /*
474                  * mm, fs and inet are executed in place (i.e. are not mapped
475                  * into low memory), so the address space must be made
476                  * accessible for them to execute there.
477                  */
478                 set_process_prot(proc_ptr, PROT_READ_EXEC, PROT_ALL);
479             }
480             kernel_off();
481         }
482     }
483 }
484 
485 
486 /*
487  * Function: kernel_on
488  *
489  * Makes the kernel text segment r-x, the data segment rwx.
490  */
491 static void kernel_on(void)
492 {
493     mprotect(pinfo.kernel_base, pinfo.kernel_text, PROT_READ_EXEC);
494     mprotect(pinfo.kernel_base + pinfo.kernel_text, pinfo.kernel_data,
495              PROT_ALL);
496 }
497 
498 
499 /*
500  * Function: kernel_off
501  *
502  * Prepare for leaving the kernel---change protection as per pinfo.
503  * Areas to be left r-x and rwx are assumed to have the correct
504  * protection already.
505  */
506 static void kernel_off(void)
507 {
508     int i;
509 
510     for (i = 0 ; i < pinfo.numsegs ; i++) {
511         if (pinfo.prot_info[i].prot != PROT_READ_EXEC &&
512             pinfo.prot_info[i].prot != PROT_ALL) {
513             mprotect(pinfo.prot_info[i].begin, pinfo.prot_info[i].len,
514                      pinfo.prot_info[i].prot);
515         }
516     }
517 }
518 
519 
520 /*
521  * Counterpart to protect_start (see its comment for more info).
522  */
523 static void protect_end(void) {}
524 
525 
526 /*
527  * Function: set_process_prot
528  * Parameters: p - process whose "physical" memory protection is to change
529  *             text_prot - access rights to give to the text segment of p
530  *             data_prot - access rights to give to the data/stack/gap
531  *                         segment of p.
532  *
533  * Set the address space protection for the specified server.
534  */
535 static void set_process_prot(struct proc *p,
536                              int text_prot, int data_stack_prot)
537 {
538     mprotect(p->p_map[T].mem_phys << CLICK_SHIFT,
539              p->p_map[T].mem_len << CLICK_SHIFT, text_prot);
540     /*
541      * Assume data, gap, and stack are contiguous
542      */
543     assert(!isuserp(p));   /* User processes have separate D and S segs */
544     mprotect(p->p_map[D].mem_phys << CLICK_SHIFT,
545              (p->p_map[S].mem_phys << CLICK_SHIFT) -
546              (p->p_map[D].mem_phys << CLICK_SHIFT) +
547              (p->p_map[S].mem_len << CLICK_SHIFT), data_stack_prot);
548 }
549 
550 
551 /*
552  * Function: map_process
553  * Parameter: p - process to map into low address space
554  *
555  * Setup mappings for the text, data and stack segments for process p.
556  * If the prot_lev is NO_PROT then the text segment is mapped rwx; otherwise
557  * it is mapped r-x.  "Physical" memory is in fact a mapped file open
558  * on RAM_FD.  The offset into the file of a "physical" address can be
559  * determined by subtracting the address of the start of the kernel
560  * from the "physical" address (the kernel starts at offset 0 in the mapped
561  * file).
562  */
563 static void map_process(struct proc *p)
564 {
565     if (mapped_proc == p) return;    /* Already there! */
566     if (mapped_proc) {
567         unmap_process(mapped_proc);
568     }
569 
570     /*
571      * Setup mappings.  The flags 0x11 are MAP_SHARED | MAP_FIXED.
572      */
573     if (SunOS(SYS_mmap, p->p_map[T].mem_vir << CLICK_SHIFT,
574               p->p_map[T].mem_len << CLICK_SHIFT,
575               pinfo.prot_lev == NO_PROT ? PROT_ALL : PROT_READ_EXEC,
576               0x11, RAM_FD,
577               (p->p_map[T].mem_phys << CLICK_SHIFT) - code_base) == -1) {
578         panic("map_process: text segment mapping failed", errno);
579     }
580             
581     if (SunOS(SYS_mmap, p->p_map[D].mem_vir << CLICK_SHIFT,
582               p->p_map[D].mem_len << CLICK_SHIFT,
583               PROT_ALL, 0x11, RAM_FD,
584               (p->p_map[D].mem_phys << CLICK_SHIFT) - code_base) == -1) {
585         panic("map_process: data segment mapping failed", errno);
586     }
587 
588     if (SunOS(SYS_mmap, p->p_map[S].mem_vir << CLICK_SHIFT,
589               p->p_map[S].mem_len << CLICK_SHIFT,
590               PROT_ALL, 0x11, RAM_FD,
591               (p->p_map[S].mem_phys << CLICK_SHIFT) - code_base) == -1) {
592         panic("map_process: stack segment mapping failed", errno);
593     }
594 
595     mapped_proc = p;
596 }
597     
598 
599 /*
600  * Function: unmap_process
601  * Parameter: p - process to unmap from low address space
602  *
603  * If p is the currently mapped process, then its mappings are removed.
604  * When unmap_process is called from entering_kernel, then p should
605  * always be the mapped process.  When unmap_process is called from
606  * mem_released, then this won't always be the case.
607  */
608 static void unmap_process(struct proc *p)
609 {
610     if (p != mapped_proc) return;
611 
612     /*
613      * Remove mapping for a user process
614      */
615     if (SunOS(SYS_munmap, p->p_map[T].mem_vir << CLICK_SHIFT,
616               p->p_map[T].mem_len << CLICK_SHIFT) == -1) {
617         panic("unmap_process: text segment unmapping failed", errno);
618     }
619             
620     if (SunOS(SYS_munmap, p->p_map[D].mem_vir << CLICK_SHIFT,
621               p->p_map[D].mem_len << CLICK_SHIFT) == -1) {
622         panic("unmap_process: data segment unmapping failed", errno);
623     }
624     
625     if (SunOS(SYS_munmap, p->p_map[S].mem_vir << CLICK_SHIFT,
626               p->p_map[S].mem_len << CLICK_SHIFT) == -1) {
627         panic("unmap_process: stack segment unmapping failed", errno);
628     }
629 
630     mapped_proc = 0;
631 }
632 
633 
634 /*
635  * The following are wrappers for the various copying and zeroing functions.
636  * The wrappers ensure that the areas to be read and written are actually
637  * accessible.
638  */
639 
640 void zeroclicks(phys_clicks addr, phys_clicks numclicks)
641 {
642     void real_zeroclicks(phys_clicks dest, phys_clicks nclicks);
643 
644     set_protect(addr << CLICK_SHIFT, numclicks << CLICK_SHIFT, PROT_WRITE);
645     real_zeroclicks(addr, numclicks);
646     set_protect(addr << CLICK_SHIFT, numclicks << CLICK_SHIFT, PROT_NONE);
647 }
648 
649 
650 void copyclicks(phys_clicks src, phys_clicks dest, phys_clicks numclicks)
651 {
652     void real_copyclicks(phys_clicks src, phys_clicks dest,
653                          phys_clicks nclicks);
654 
655     set_protect(src << CLICK_SHIFT, numclicks << CLICK_SHIFT, PROT_READ);
656     set_protect(dest << CLICK_SHIFT, numclicks << CLICK_SHIFT, PROT_WRITE);
657     real_copyclicks(src, dest, numclicks);
658     set_protect(src << CLICK_SHIFT, numclicks << CLICK_SHIFT, PROT_NONE);
659     set_protect(dest << CLICK_SHIFT, numclicks << CLICK_SHIFT, PROT_NONE);
660 }
661 
662 
663 void phys_copy(phys_bytes src, phys_bytes dest, phys_bytes len)
664 {
665     void real_phys_copy(phys_bytes src, phys_bytes dest, phys_bytes n);
666 
667     set_protect(src, len, PROT_READ);
668     set_protect(dest, len, PROT_WRITE);
669     real_phys_copy(src, dest, len);
670     set_protect(src, len, PROT_NONE);
671     set_protect(dest, len, PROT_NONE);
672 }
673 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.