* Copyright (c) 1982, 1986, 1989 Regents of the University of California.
* Redistribution is only permitted until one year after the first shipment
* of 4.4BSD by the Regents. Otherwise, redistribution and use in source and
* binary forms are permitted provided that: (1) source distributions retain
* this entire copyright notice and comment, and (2) distributions including
* binaries display the following acknowledgement: This product includes
* software developed by the University of California, Berkeley and its
* contributors'' in the documentation or other materials provided with the
* distribution and in all advertising materials mentioning features or use
* of this software. Neither the name of the University nor the names of
* its contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
* @(#)kern_mman.c 7.18 (Berkeley) 6/30/90
#include "machine/mtpr.h"
* The MMAP code here is temporary; it provides support
* only for mmaping devices such as frame buffers.
* All to be different next time...
struct mapmemops mmapops
= {
(int (*)())0, (int (*)())0, (int (*)())0, (int (*)())0
/* Not yet implemented */
/* Not yet implemented */
getpagesize(p
, uap
, retval
)
register struct file
*fp
;
register struct pte
*pte
;
int error
, fv
, lv
, pm
, (*mapfun
)();
if (error
= getvnode(u
.u_ofile
, uap
->fd
, &fp
))
vp
= (struct vnode
*)fp
->f_data
;
mapfun
= cdevsw
[major(dev
)].d_mmap
;
if (((int)uap
->addr
& CLOFSET
) || (uap
->pos
& CLOFSET
) ||
uap
->len
<= 0 || (uap
->len
& CLOFSET
))
if ((uap
->prot
& PROT_WRITE
) && (fp
->f_flag
&FWRITE
) == 0)
if ((uap
->prot
& PROT_READ
) && (fp
->f_flag
&FREAD
) == 0)
if (uap
->share
!= MAP_SHARED
)
for (off
= 0; off
< uap
->len
; off
+= NBPG
)
if ((*mapfun
)(dev
, uap
->pos
+off
, uap
->prot
) == -1)
return (EINVAL
); /* Needs translation */
* Allocate a descriptor for this region and expand page
if (uap
->prot
& PROT_WRITE
) {
pm
= PG_URKR
|PG_FOD
|PG_V
;
error
= mmalloc(p
, uap
->fd
, &uap
->addr
, uap
->len
, off
, &mmapops
, &mp
);
* Can't use mmmapin() because of args to map function.
dpte
= dptopte(p
, u
.u_dsize
);
for (off
= 0; off
< uap
->len
; off
+= NBPG
) {
if ((off
&CLOFSET
) == 0 && pte
< dpte
)
p
->p_rssize
-= vmemfree(pte
, CLSIZE
);
pte
->pg_pfnum
= (*mapfun
)(dev
, uap
->pos
+off
, uap
->prot
);
newptes(vtopte(p
, fv
), fv
, btoc(uap
->len
));
u
.u_pofile
[uap
->fd
] |= UF_MAPPED
;
/* Not yet implemented */
register struct mapmem
*mp
;
if (((int)uap
->addr
& CLOFSET
) ||
uap
->len
<= 0 || (uap
->len
& CLOFSET
))
* Locate region mapping this range. If found, unmap it.
eaddr
= uap
->addr
+ uap
->len
- 1;
for (mp
= u
.u_mmap
; mp
; mp
= mp
->mm_next
)
if (mp
->mm_ops
== &mmapops
&&
uap
->addr
>= mp
->mm_uva
&& eaddr
< mp
->mm_uva
+mp
->mm_size
)
* If no other range has this descriptor mapped, mark it as unmapped.
for (mp
= u
.u_mmap
; mp
; mp
= mp
->mm_next
)
u
.u_pofile
[fd
] &= ~UF_MAPPED
;
struct proc
*p
= u
.u_procp
; /* XXX */
register struct mapmem
*mp
, **mpp
;
for (mp
= *mpp
; mp
; mp
= *mpp
) {
if (mp
->mm_ops
== &mmapops
&& mp
->mm_id
== fd
) {
u
.u_pofile
[fd
] &= ~UF_MAPPED
;
/* Not yet implemented */
/* Not yet implemented */
/* Not yet implemented */
register segsz_t n
, d
, ds
;
n
= btoc(uap
->nsiz
) - dptov(p
, 0);
* since we can't pass a -ve argument for the difference to chksize,
* if d is negative, make ds equal to the final value and clear d.
* keep the real difference in n for later use in expand.
if ((n
= d
= clrnd(n
- u
.u_dsize
)) < 0) {
if (ctob(ds
+ d
) > u
.u_rlimit
[RLIMIT_DATA
].rlim_cur
)
chksize((u_int
)u
.u_tsize
, (u_int
)ds
, (u_int
)d
, (u_int
)u
.u_ssize
))
* If change would conflict with any mapped memory segment
if (u
.u_mmap
&& n
!= 0) {
low
= (caddr_t
) ctob(dptov(p
, ds
));
high
= low
+ ctob((n
< 0) ? -n
: n
);
if (mmclash(u
.u_mmap
, low
, high
))
if (error
= swpexpand(ds
+ d
, u
.u_ssize
, &u
.u_dmap
, &u
.u_smap
))
if (p
->p_mmsize
&& (p
->p_mmsize
-= n
) < 0)
* Macros for clearing a page's reference bits.
#define uncache(pte) /* XXX */
#define CLRREF(pte, c, p, i) { \
c = &cmap[pgtocm(pte->pg_pfnum)]; \
distpte(p->p_textp, i, pte); \
#define CLRREF(pte, c, p, i) { \
c = &cmap[pgtocm(pte->pg_pfnum)]; \
distpte(p->p_textp, i, pte); \
ovadvise(rp
, uap
, retval
)
register struct proc
*rp
;
int oanom
= rp
->p_flag
& SUANOM
;
register struct pte
*pte
;
trace(TR_VADVISE
, uap
->anom
, rp
->p_pid
);
rp
->p_flag
&= ~(SSEQL
|SUANOM
);
if ((oanom
&& (rp
->p_flag
& SUANOM
) == 0) || uap
->anom
== VA_FLUSH
) {
for (i
= 0; i
< rp
->p_dsize
; i
+= CLSIZE
) {
/* don't do mmap pages */
if (pte
->pg_v
&& !pte
->pg_fod
)
if (uap
->anom
== VA_FLUSH
) { /* invalidate all pages */
for (i
= 1; i
< rp
->p_ssize
; i
+= CLSIZE
) {
for (i
= 0; i
< rp
->p_tsize
; i
+= CLSIZE
) {
#if defined(vax) || defined(tahoe)
* Grow the stack to include the SP; true return if successful.
* Clients do not care about the cause of the error.
if (sp
>= USRSTACK
-ctob(u
.u_ssize
))
si
= clrnd(btoc((USRSTACK
-sp
)) - u
.u_ssize
+ SINCR
);
if (ctob(si
) > u
.u_rlimit
[RLIMIT_STACK
].rlim_cur
)
if (error
= chksize((u_int
)u
.u_tsize
, (u_int
)u
.u_dsize
, (u_int
)0,
if (error
= swpexpand(u
.u_dsize
, u
.u_ssize
+ si
, &u
.u_dmap
, &u
.u_smap
))
* Called from vpassvm() after full context has been passed from fup to tup.
* Always called in the context of the parent. NOTE: routines should NOT
register struct mapmem
*mp
;
tup
->u_mmap
= fup
->u_mmap
;
fup
->u_mmap
= (struct mapmem
*) 0;
for (mp
= tup
->u_mmap
; mp
; mp
= mp
->mm_next
)
if (mp
->mm_ops
->mm_vfork
)
(*mp
->mm_ops
->mm_vfork
)(mp
, fup
, tup
);
* Called from procdup() for both parent and child. If in parent
* we need to duplicate mapped memory regions. In both parent and
* child, we call object specific routine.
register struct mapmem
*mp
, **mpp
;
for (mp
= pup
->u_mmap
; mp
; mp
= mp
->mm_next
)
(*mp
->mm_ops
->mm_fork
)(mp
, 0);
for (mp
= *mpp
; mp
; mp
= *mpp
) {
(*mp
->mm_ops
->mm_fork
)(mp
, 1);
error
= mmexpand(u
.u_procp
);
* Its not clear that having a seperate exec routine is useful since
* exec frees the address space immediately afterwards. We probably
* need a post-exec hook to reestablish any mappings that persist
register struct mapmem
*mp
, **mpp
;
for (mp
= *mpp
; mp
; mp
= *mpp
) {
error
= (*mp
->mm_ops
->mm_exec
)(mp
);
if (error1
= mmexpand(p
))
* Called from exit just before releasing address space.
* We always reclaim resources regardless of what the object routine does.
register struct mapmem
*mp
, **mpp
;
for (mp
= *mpp
; mp
; mp
= *mpp
) {
error
= (*mp
->mm_ops
->mm_exit
)(mp
);
if (error1
= mmexpand(p
))
* Called from core just before dumping process image to core file.
* Used to unmap regions which cannot be dumped; e.g. a region mapping
* hardware registers which are write-only or must be accessed as bytes.
register struct mapmem
*mp
, **mpp
;
int error
= 0, error1
, changed
= 0;
for (mp
= *mpp
; mp
; mp
= *mpp
) {
if ((mp
->mm_prot
& MM_NOCORE
) == 0) {
error
= (*mp
->mm_ops
->mm_exit
)(mp
);
if (changed
&& (error1
= mmexpand(p
)))
* Duplicate mapped memory regions in a forked process.
* XXX child may wind up short a few regions if not enough resources.
register struct mapmem
*pmp
, *cmp
;
register struct pte
*ppte
, *cpte
;
* First duplicate the mmap chain
* Now duplicate user address space that vmdup() won't do
* i.e. mapped regions outside of data segment.
ppte
= dptopte(pu
->u_procp
, pu
->u_procp
->p_dsize
);
cpte
= dptopte(cu
->u_procp
, cu
->u_procp
->p_dsize
);
for (count
= pu
->u_procp
->p_mmsize
; count
; count
--) {
if (ppte
->pg_fod
&& ppte
->pg_v
)
*(int *)cpte
= *(int *)ppte
;
cu
->u_procp
->p_flag
|= SPTECHG
;
mmalloc(p
, id
, uvap
, size
, prot
, ops
, mpp
)
register struct mapmem
*mp
;
if (size
<= 0 || (size
& CLOFSET
))
* A uva of zero means to map at our discretion.
* Our strategy is to place the segment at the max of:
* - the current data + mapped memory size
* - the default data size limit
* (if it will fit within the MAXDSIZ limit)
* If this is the first mapped memory region beyond the data
* segment we round to a MMSEG boundary to allow for data
uva
= ctob(dptov(p
, u
.u_dsize
+ p
->p_mmsize
));
uva2
= ctob(dptov(p
, btoc(DFLDSIZ
)));
uva2
= ((uva2
+ (MMSEG
-1)) & ~(MMSEG
-1));
uva2
+ size
< ctob(dptov(p
, btoc(MAXDSIZ
))))
else if (p
->p_mmsize
== 0)
uva
= ((uva
+ (MMSEG
-1)) & ~(MMSEG
-1));
* Impose necessary constraints on address.
if ((uva
& CLOFSET
) || uva
< ctob(dptov(p
, 0)) ||
uva
+size
>= ctob(sptov(p
, u
.u_ssize
)))
if (mmclash(u
.u_mmap
, (caddr_t
)uva
, (caddr_t
)uva
+size
))
* Finally, allocate and initialize descriptor and expand
* user address space as necessary.
mp
->mm_uva
= (caddr_t
) uva
;
if (error
= mmexpand(p
)) {
register struct mapmem
*mp
;
register struct mapmem
*cmp
, **mpp
;
* Remove region from chain
for (cmp
= *mpp
; cmp
; cmp
= *mpp
) {
register struct mapmem
*mp
;
register struct pte
*pte
;
* Verify that range can be mapped
for (off
= 0; off
< mp
->mm_size
; off
+= NBPG
)
if ((*mapfunc
)(mp
, off
) == -1)
* Now verify that region is in range
lv
= btop(mp
->mm_uva
+ mp
->mm_size
- 1);
lv
>= dptov(p
, u
.u_dsize
+ p
->p_mmsize
))
* Finally, do the mapping.
pm
= PG_URKR
|PG_FOD
|PG_V
;
dpte
= dptopte(p
, u
.u_dsize
);
for (off
= 0; off
< mp
->mm_size
; off
+= NBPG
) {
if ((off
&CLOFSET
) == 0 && pte
< dpte
)
p
->p_rssize
-= vmemfree(pte
, CLSIZE
);
pte
->pg_pfnum
= (*mapfunc
)(mp
, off
);
newptes(vtopte(p
, fv
), (u_int
)fv
, (int)btoc(mp
->mm_size
));
register struct mapmem
*mp
;
register struct pte
*pte
;
lv
= btop(mp
->mm_uva
+ mp
->mm_size
- 1);
lv
>= dptov(p
, u
.u_dsize
+ p
->p_mmsize
))
dpte
= dptopte(p
, u
.u_dsize
);
for (off
= 0; off
< mp
->mm_size
; off
+= NBPG
) {
if ((off
& CLOFSET
) == 0)
p
->p_rssize
-= vmemfree(pte
, CLSIZE
);
*(int *)pte
= (PG_UW
|PG_FOD
);
((struct fpte
*)pte
)->pg_fileno
= PG_FZERO
;
newptes(vtopte(p
, fv
), (u_int
)fv
, (int)btoc(mp
->mm_size
));
register int szpt
, change
;
* Get new mmsize based on existing regions and use
* that to calculate change in page table size.
mmrange(u
.u_mmap
, (caddr_t
*)0, &high
);
nsize
= btop(high
) - dptov(p
, u
.u_dsize
) + 1;
* Ensure data + mapped memory fits within maximum data limit.
* This is possibly a little restrictive, but it helps keep
(ctob(oms
+change
) > u
.u_rlimit
[RLIMIT_DATA
].rlim_max
||
ctob(u
.u_dsize
+oms
+change
) > u
.u_rlimit
[RLIMIT_DATA
].rlim_max
))
* Expand page table if necessary.
* Note that ptexpand takes care of flushing the translation buffer.
#if defined(hp300) || defined(i386)
szpt
= ptsize(p
) - u
.u_pcb
.pcb_szpt
;
ptexpand(szpt
, u
.u_dsize
, oms
, u
.u_ssize
);
setp0lr(u
.u_pcb
.pcb_p0lr
+ change
);
#if defined(vax) || defined(tahoe)
szpt
= (u
.u_pcb
.pcb_p1br
+ (u
.u_pcb
.pcb_p1lr
&~PME_CLR
)) -
(u
.u_pcb
.pcb_p0br
+ (u
.u_pcb
.pcb_p0lr
&~AST_CLR
));
szpt
= (u
.u_pcb
.pcb_p2br
+ u
.u_pcb
.pcb_p2lr
) -
(u
.u_pcb
.pcb_p0br
+ u
.u_pcb
.pcb_p0lr
);
ptexpand(clrnd(ctopt(change
- szpt
)), u
.u_dsize
, oms
, u
.u_ssize
);
* We need to do this because there may be bogus (yet technically
* valid) ptes above the old p0lr value. This can happen if the
* data segment has shrunk in the past leaving such ptes behind.
* There is no need to invalidate such ptes at that time since the
* length register will prevent their use. We are safe on the HPs
* because we do invalidate old ptes in setp0lr() when shrinking.
bpte
= u
.u_pcb
.pcb_p0br
+ (u
.u_pcb
.pcb_p0lr
&~AST_CLR
);
bpte
= u
.u_pcb
.pcb_p0br
+ u
.u_pcb
.pcb_p0lr
;
bzero((caddr_t
)bpte
, change
* sizeof(struct pte
));
/* avoid side-effects of setp0lr */
change
+= u
.u_pcb
.pcb_p0lr
&~ AST_CLR
;
change
+= u
.u_pcb
.pcb_p0lr
;
register struct mapmem
*mp
;
register caddr_t low
, high
, top
;
if (low
== 0 || mp
->mm_uva
< low
)
top
= mp
->mm_uva
+ mp
->mm_size
- 1;
if (high
== 0 || top
> high
)
register struct mapmem
*mp
;
if (ha
> mp
->mm_uva
&& la
< mp
->mm_uva
+ mp
->mm_size
)