tty: don't use custom kputc; this fixes tty printf()s.
[minix.git] / servers / vm / break.c
blob4f00c9e28600083c7157591c6c434726e9408cd6
1 /* The MINIX model of memory allocation reserves a fixed amount of memory for
2 * the combined text, data, and stack segments. The amount used for a child
3 * process created by FORK is the same as the parent had. If the child does
4 * an EXEC later, the new size is taken from the header of the file EXEC'ed.
6 * The layout in memory consists of the text segment, followed by the data
7 * segment, followed by a gap (unused memory), followed by the stack segment.
8 * The data segment grows upward and the stack grows downward, so each can
9 * take memory from the gap. If they meet, the process must be killed. The
10 * procedures in this file deal with the growth of the data and stack segments.
12 * The entry points into this file are:
13 * do_brk: BRK/SBRK system calls to grow or shrink the data segment
14 * adjust: see if a proposed segment adjustment is allowed
17 #define _SYSTEM 1
19 #include <minix/callnr.h>
20 #include <minix/com.h>
21 #include <minix/config.h>
22 #include <minix/const.h>
23 #include <minix/ds.h>
24 #include <minix/endpoint.h>
25 #include <minix/keymap.h>
26 #include <minix/minlib.h>
27 #include <minix/type.h>
28 #include <minix/ipc.h>
29 #include <minix/sysutil.h>
30 #include <minix/syslib.h>
31 #include <minix/bitmap.h>
33 #include <errno.h>
34 #include <env.h>
36 #include "glo.h"
37 #include "vm.h"
38 #include "proto.h"
39 #include "util.h"
41 #define DATA_CHANGED 1 /* flag value when data segment size changed */
42 #define STACK_CHANGED 2 /* flag value when stack size changed */
44 /*===========================================================================*
45 * do_brk *
46 *===========================================================================*/
47 PUBLIC int do_brk(message *msg)
49 /* Perform the brk(addr) system call.
50 * The parameter, 'addr' is the new virtual address in D space.
52 int proc;
54 if(vm_isokendpt(msg->VMB_ENDPOINT, &proc) != OK) {
55 printf("VM: bogus endpoint VM_BRK %d\n", msg->VMB_ENDPOINT);
56 return EINVAL;
59 return real_brk(&vmproc[proc], (vir_bytes) msg->VMB_ADDR);
62 /*===========================================================================*
63 * adjust *
64 *===========================================================================*/
65 PUBLIC int adjust(rmp, data_clicks, sp)
66 struct vmproc *rmp; /* whose memory is being adjusted? */
67 vir_clicks data_clicks; /* how big is data segment to become? */
68 vir_bytes sp; /* new value of sp */
70 /* See if data and stack segments can coexist, adjusting them if need be.
71 * Memory is never allocated or freed. Instead it is added or removed from the
72 * gap between data segment and stack segment. If the gap size becomes
73 * negative, the adjustment of data or stack fails and ENOMEM is returned.
76 register struct mem_map *mem_sp, *mem_dp;
77 vir_clicks sp_click, gap_base, sp_lower, old_clicks;
78 int changed, r, sp_in_dp;
79 long base_of_stack, sp_delta; /* longs avoid certain problems */
81 mem_dp = &rmp->vm_arch.vm_seg[D]; /* pointer to data segment map */
82 mem_sp = &rmp->vm_arch.vm_seg[S]; /* pointer to stack segment map */
83 changed = 0; /* set when either segment changed */
85 /* See if stack size has gone negative (i.e., sp too close to 0xFFFF...) */
86 base_of_stack = (long) mem_sp->mem_vir + (long) mem_sp->mem_len;
87 sp_click = sp >> CLICK_SHIFT; /* click containing sp */
88 if (sp_click >= base_of_stack)
90 return(ENOMEM); /* sp too high */
93 /* In order to support user-space libraries, processes might change sp to
94 point to somewhere inside the data segment. If that's the case, be careful
95 not to erroneously think that the data and stack have collided. */
96 sp_in_dp = (mem_dp->mem_vir <= sp_click) &&
97 (mem_dp->mem_vir + mem_dp->mem_len >= sp_click);
99 /* Compute size of gap between stack and data segments. */
100 sp_delta = (long) mem_sp->mem_vir - (long) sp_click;
101 sp_lower = ((sp_delta > 0 && !sp_in_dp) ? sp_click : mem_sp->mem_vir);
103 /* Add a safety margin for future stack growth. Impossible to do right. */
104 #define SAFETY_BYTES (384 * sizeof(char *))
105 #define SAFETY_CLICKS ((vir_clicks) (CLICK_CEIL(SAFETY_BYTES) >> CLICK_SHIFT))
106 gap_base = mem_dp->mem_vir + data_clicks + SAFETY_CLICKS;
107 if (sp_lower < gap_base)
109 return(ENOMEM); /* data and stack collided */
112 /* Update data length (but not data orgin) on behalf of brk() system call. */
113 old_clicks = mem_dp->mem_len;
114 if (data_clicks != mem_dp->mem_len) {
115 mem_dp->mem_len = data_clicks;
116 changed |= DATA_CHANGED;
119 /* Update stack length and origin due to change in stack pointer. */
120 if (sp_delta > 0 && !sp_in_dp) {
121 mem_sp->mem_vir -= sp_delta;
122 mem_sp->mem_phys -= sp_delta;
123 mem_sp->mem_len += sp_delta;
124 changed |= STACK_CHANGED;
127 /* Do the new data and stack segment sizes fit in the address space? */
128 r = (rmp->vm_arch.vm_seg[D].mem_vir + rmp->vm_arch.vm_seg[D].mem_len >
129 rmp->vm_arch.vm_seg[S].mem_vir) ? ENOMEM : OK;
131 if(r == OK && (rmp->vm_flags & VMF_HASPT) &&
132 rmp->vm_endpoint != VM_PROC_NR && rmp->vm_heap) {
133 if(old_clicks < data_clicks) {
134 vir_bytes more;
135 more = (data_clicks - old_clicks) << CLICK_SHIFT;
136 if(map_region_extend(rmp, rmp->vm_heap, more) != OK) {
137 printf("VM: brk: map_region_extend failed\n");
138 return ENOMEM;
140 } else if(old_clicks > data_clicks) {
141 vir_bytes less;
142 less = (old_clicks - data_clicks) << CLICK_SHIFT;
143 if(map_region_shrink(rmp->vm_heap, less) != OK) {
144 printf("VM: brk: map_region_shrink failed\n");
145 return ENOMEM;
150 if (r == OK)
151 return(OK);
153 /* New sizes don't fit or require too many page/segment registers. Restore.*/
154 if (changed & DATA_CHANGED) mem_dp->mem_len = old_clicks;
155 if (changed & STACK_CHANGED) {
156 mem_sp->mem_vir += sp_delta;
157 mem_sp->mem_phys += sp_delta;
158 mem_sp->mem_len -= sp_delta;
160 return(ENOMEM);
163 /*===========================================================================*
164 * real_brk *
165 *===========================================================================*/
166 PUBLIC int real_brk(vmp, v)
167 struct vmproc *vmp;
168 vir_bytes v;
170 vir_bytes new_sp;
171 vir_clicks new_clicks;
172 int r;
174 new_clicks = (vir_clicks) (CLICK_CEIL(v) >> CLICK_SHIFT);
175 if (new_clicks < vmp->vm_arch.vm_seg[D].mem_vir) {
176 printf("VM: real_brk failed because new_clicks too high: %d\n",
177 new_clicks);
178 return(ENOMEM);
180 new_clicks -= vmp->vm_arch.vm_seg[D].mem_vir;
181 if ((r=get_stack_ptr(vmp->vm_endpoint, &new_sp)) != OK)
182 panic("couldn't get stack pointer: %d", r);
183 r = adjust(vmp, new_clicks, new_sp);
184 return r;