Commit | Line | Data |
---|---|---|
920dae64 AT |
1 | \ ========== Copyright Header Begin ========================================== |
2 | \ | |
3 | \ Hypervisor Software File: checkpt.fth | |
4 | \ | |
5 | \ Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. | |
6 | \ | |
7 | \ - Do no alter or remove copyright notices | |
8 | \ | |
9 | \ - Redistribution and use of this software in source and binary forms, with | |
10 | \ or without modification, are permitted provided that the following | |
11 | \ conditions are met: | |
12 | \ | |
13 | \ - Redistribution of source code must retain the above copyright notice, | |
14 | \ this list of conditions and the following disclaimer. | |
15 | \ | |
16 | \ - Redistribution in binary form must reproduce the above copyright notice, | |
17 | \ this list of conditions and the following disclaimer in the | |
18 | \ documentation and/or other materials provided with the distribution. | |
19 | \ | |
20 | \ Neither the name of Sun Microsystems, Inc. or the names of contributors | |
21 | \ may be used to endorse or promote products derived from this software | |
22 | \ without specific prior written permission. | |
23 | \ | |
24 | \ This software is provided "AS IS," without a warranty of any kind. | |
25 | \ ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, | |
26 | \ INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A | |
27 | \ PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN | |
28 | \ MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR | |
29 | \ ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR | |
30 | \ DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN | |
31 | \ OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR | |
32 | \ FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE | |
33 | \ DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, | |
34 | \ ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF | |
35 | \ SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. | |
36 | \ | |
37 | \ You acknowledge that this software is not designed, licensed or | |
38 | \ intended for use in the design, construction, operation or maintenance of | |
39 | \ any nuclear facility. | |
40 | \ | |
41 | \ ========== Copyright Header End ============================================ | |
42 | id: @(#)checkpt.fth 1.4 05/04/08 22:16:13 | |
43 | purpose: | |
44 | copyright: Copyright 2005 Sun Microsystems, Inc. All Rights Reserved | |
45 | copyright: Use is subject to license terms. | |
46 | ||
47 | \ Checkpt | |
48 | \ | |
49 | \ a checkpt is like a setjmp/longjmp in C. (man setjmp) | |
50 | \ | |
51 | \ push-checkpt : Create an exception frame and push it onto the exception | |
52 | \ stack | |
53 | ||
54 | \ pop-checkpt : Dispose the current exception frame. | |
55 | \ | |
56 | \ The catch and throw are special cases of checkpts. | |
57 | \ | |
58 | \ One of the difference between a push-checkpt and a 'catch' is that | |
59 | \ the catch calls the thing it is trying to catch, a checkpoint does | |
60 | \ not have this restriction. Looking at the tail end of 'catch' in | |
61 | \ the normal execution case it pops the handler, which is the exact | |
62 | \ equivalent of 'pop-checkpt'. | |
63 | \ | |
64 | \ The primary difference is that you can mark a checkpoint at an arbitrary | |
65 | \ point and 'recover' much like a 'catch' does except that you are no | |
66 | \ longer on the same descending call frame (e.g., return stack). | |
67 | \ | |
68 | \ So, consider | |
69 | \ : xxx cmn-error[ ..-1 throw ... ]cmn-end ; | |
70 | \ : yyy ['] xxx catch ; | |
71 | \ | |
72 | \ The throw destroys the error frame by returning control to the 'catch'. | |
73 | \ | |
74 | \ Now if cmn-error[ was implemented: | |
75 | \ | |
76 | \ : (]cmn-end) ... ; | |
77 | \ : cmn-error[ | |
78 | \ push-checkpt ?dup if | |
79 | \ " [truncated]" (]cmn-end) | |
80 | \ throw | |
81 | \ else | |
82 | \ <start the message> | |
83 | \ then | |
84 | \ ; | |
85 | \ | |
86 | \ : ]cmn-end (]cmn-end) pop-checkpt ; | |
87 | \ | |
88 | \ What we have done is ensure that an error inside an error message | |
89 | \ frame is constrained in a recoverable manner and the original 'throw' | |
90 | \ is propogated to the catch in yyy. | |
91 | \ | |
92 | \ The magic is that 'cmn-error[' returned to its caller normally and the | |
93 | \ caller xxx drove on, but on ERROR the code jumped back into the cmn-error[ | |
94 | \ routine which then had to throw. If it had not then the '-1 throw' would | |
95 | \ execute again (assuming no other disasterous stack effects). | |
96 | \ | |
97 | \ To be a good citizen you should only 'recover' from an error and | |
98 | \ not propogate a throw if you know that the throw code is yours (as | |
99 | \ in the example). | |
100 | \ | |
101 | \ In general it is bad practice to return from the routine that | |
102 | \ established the check point (ie the routine that called 'push-checkpt'), | |
103 | \ though a maximum unnest of /check-stack stack elements will permit this | |
104 | \ to work. | |
105 | \ | |
106 | \ IT IS BEST *NOT* TO RETURN FROM THE CHECKPOINT FRAME; | |
107 | \ THOUGH IT WILL WORK IN MANY CASES | |
108 | \ | |
109 | \ This is a similar restriction to setjmp/longjmp in C. | |
110 | \ | |
111 | \ Frame Starts Frame Ends | |
112 | \ push-checkpt pop-checkpt, throw | |
113 | \ catch throw | |
114 | \ | |
115 | ||
116 | headers | |
117 | [ifdef] KERNEL | |
118 | nuser checkpt \ most recent checkpoint | |
119 | nuser checkbase \ frames | |
120 | nuser checktrack \ tracker | |
121 | nuser checkalloc \ counter | |
122 | nuser checkmax \ max frames | |
123 | nuser checknested | |
124 | also meta also definitions \ setup metacompiler magic | |
125 | [else] | |
126 | variable checkpt | |
127 | variable checkbase | |
128 | variable checktrack | |
129 | variable checkalloc | |
130 | variable checkmax | |
131 | variable checknested | |
132 | [then] | |
133 | ||
134 | [ifdef] miniforth? | |
135 | h# 18 constant /check-max \ max outstanding catch frames | |
136 | [else] | |
137 | h# 80 constant /check-max \ frames alloc'd after dynamic heap | |
138 | [then] | |
139 | h# 10 constant /check-crit \ frames alloc'd from critical heap | |
140 | headerless | |
141 | 8 constant /check-stack \ How many elements to preserve | |
142 | \ sized for known catch stack usage | |
143 | struct | |
144 | /n field >check-prev \ previous frame | |
145 | /n field >check-ip \ checkpt IP | |
146 | /n field >check-sp \ checkpt DS pointer | |
147 | /n field >check-rp \ checkpt RS pointer | |
148 | /l field >check-myself \ my-self | |
149 | /l field >check-age | |
150 | /check-stack /n * field >check-ds \ a chunk of the DS | |
151 | /check-stack /n * field >check-rs \ a chunk of the RS | |
152 | constant /check-frame | |
153 | ||
154 | [ifdef] KERNEL | |
155 | previous previous definitions \ back to kernel | |
156 | ||
157 | \ create the kernel side of these routines, using the structure | |
158 | \ created in the host metacompiler to form the offsets. | |
159 | ||
160 | : >check-prev [ 0 >check-prev ] literal + ; | |
161 | : >check-myself [ 0 >check-myself ] literal + ; | |
162 | : >check-age [ 0 >check-age ] literal + ; | |
163 | ||
164 | headers | |
165 | 0 value my-self | |
166 | headerless | |
167 | [then] | |
168 | ||
169 | \ initial 16 frames are allocated from the critical heap | |
170 | : init-checkpt | |
171 | checkpt off | |
172 | checkalloc off | |
173 | checknested off | |
174 | /check-crit checkmax ! | |
175 | /check-crit dup alloc-mem dup checktrack ! ( va ) | |
176 | swap erase ( len va ) | |
177 | /check-crit /check-frame * /n + ( sz ) | |
178 | alloc-mem /n 1- + /n 1- invert and checkbase ! ( ) | |
179 | ; | |
180 | ||
181 | \ dynamic heap is installed before we call into this so that the frame alloc | |
182 | \ requirements can be satisfied by the expanded heap made available. | |
183 | \ the alloc-mem calls will cause checkpt frames to be alloc/free'd due to | |
184 | \ calls to 'catch', relying on a coherent state in the checkpt variables, | |
185 | \ so the expanded allocations must not be swapped in until after they complete. | |
186 | \ | |
187 | : alloc-checkpt | |
188 | \ alloc max checktrack and copy critical heap checktrack | |
189 | checktrack @ >r r@ ( oldt ) ( r: t ) | |
190 | /check-max dup alloc-mem ( oldt len newt ) | |
191 | dup rot erase swap ( newt oldt ) | |
192 | /check-crit bounds do i c@ over c! 1+ loop ( newt' ) | |
193 | /check-crit - ( newt ) | |
194 | ||
195 | \ alloc max checkpt frames and copy critical heap frames | |
196 | checkbase @ >r r@ tuck ( oldb newt oldb ) ( r: t b ) | |
197 | /check-max /check-frame * /n + ( oldb newt oldb len ) | |
198 | alloc-mem /n 1- + /n 1- invert and ( oldb newt oldb newb ) | |
199 | swap /check-crit /check-frame * bounds ( oldb newt newb hi lo ) | |
200 | 2dup /check-frame + 2>r ( oldb newt newb hi lo ) | |
201 | do i @ over ! na1+ /n +loop ( oldb newt newb' ) | |
202 | /check-crit /check-frame * - ( oldb newt newb ) | |
203 | ||
204 | \ having copied the old frames to the new now patch the >check-prev ptrs | |
205 | \ start at second frame of old and new frames as first frame has null ptr | |
206 | 2 pick over /check-frame + 2r> do ( oldb newt newb oldb newb' ) | |
207 | over i @ swap - 3 pick + over ! ( oldb newt newb oldb newb'' ) | |
208 | /check-frame + /check-frame +loop ( oldb newt newb oldb newb'' ) | |
209 | 2drop ( oldb newt newb ) | |
210 | ||
211 | \ now swap in expanded frame allocations after fully initialized | |
212 | \ update checkpt to current frame in newly allocate frames | |
213 | checkbase ! checktrack ! ( oldb ) | |
214 | checkpt @ ?dup if ( oldb cur ) | |
215 | swap - checkbase @ + checkpt ! ( ) | |
216 | else ( oldb ) | |
217 | drop ( ) | |
218 | then ( ) | |
219 | /check-max checkmax ! ( ) ( r: t b ) | |
220 | ||
221 | r> /check-crit /check-frame * free-mem ( ) ( r: t ) | |
222 | r> /check-crit free-mem ( ) | |
223 | ; | |
224 | ||
225 | \ | |
226 | \ returns false the first time it is called (by push-checkpt) | |
227 | \ returns the throw code when the saved state is restored by restore-checkpt | |
228 | \ so that push-checkpt can distinguish between the initial save and a throw | |
229 | \ the saved IP will be the symbol after save-checkpt in push-checkpt | |
230 | \ the top /check-stack elements of both stacks are preserved | |
231 | \ | |
232 | code save-checkpt ( frame -- 0 ) | |
233 | ip tos 0 >check-ip nput | |
234 | \ copy the data stack | |
235 | /check-stack /n* scr move | |
236 | sp sc1 move \ saved SP | |
237 | rp sc2 move \ saved RP | |
238 | tos 0 >check-ds sc3 add | |
239 | tos 0 >check-rs sc4 add | |
240 | begin | |
241 | scr /n scr subcc | |
242 | sp sc5 pop | |
243 | sc5 sc3 scr nput | |
244 | rp sc6 pop | |
245 | 0= until | |
246 | sc6 sc4 scr nput | |
247 | ||
248 | sp tos 0 >check-sp nput | |
249 | rp tos 0 >check-rp nput | |
250 | ||
251 | sc1 sp move \ Restore SP | |
252 | sc2 rp move \ Restore RP | |
253 | ||
254 | %g0 tos move | |
255 | c; | |
256 | ||
257 | \ This works by restoring the return and data stack pointers and | |
258 | \ /check-stack worth of data from the last checkpt frame, | |
259 | \ restoring the IP and then setting tos to the throw code. | |
260 | \ | |
261 | \ the effect is to restart the execution at the symbol following the | |
262 | \ save-checkpt call in push-checkpt. | |
263 | \ | |
264 | code restore-checkpt ( code frame -- code ) | |
265 | sp sc7 pop | |
266 | tos 0 >check-ip ip nget | |
267 | tos 0 >check-sp sp nget | |
268 | tos 0 >check-rp rp nget | |
269 | ||
270 | /check-stack /n* scr move | |
271 | tos 0 >check-ds sc1 add | |
272 | tos 0 >check-rs sc2 add | |
273 | %g0 sc3 move | |
274 | begin | |
275 | scr /n scr subcc | |
276 | sc1 sc3 sc5 nget | |
277 | sc5 sp push | |
278 | sc2 sc3 sc6 nget | |
279 | sc6 rp push | |
280 | 0= until | |
281 | sc3 /n sc3 add | |
282 | ||
283 | sc7 tos move | |
284 | c; | |
285 | ||
286 | \ | |
287 | \ We free 16 frames - and hope this is enough to go interactive again | |
288 | \ may be useful to increase for debugging | |
289 | \ we only get here if we ran out of frames | |
290 | \ | |
291 | : free-oldest-frames ( -- ) | |
292 | h# 10 0 do | |
293 | checkalloc @ 0 checkmax @ 0 ?do ( age n ) | |
294 | over ( age n age ) | |
295 | checkbase @ i /check-frame * + ( age n age ptr ) | |
296 | >check-age l@ ( age n age age-2 ) | |
297 | tuck > ( age n age-2 old? ) | |
298 | checktrack @ i + c@ 0<> ( age n age-2 old? used? ) | |
299 | and if ( age n age-2 ) | |
300 | nip nip i leave ( age' i ) | |
301 | else ( age n age-2 ) | |
302 | drop ( age n ) | |
303 | then ( age' i ) | |
304 | loop ( age n ) | |
305 | checktrack @ + 0 swap c! drop ( ) | |
306 | loop | |
307 | ; | |
308 | ||
309 | \ We track individual allocs so that on an error (no more frames) | |
310 | \ we can find the 'oldest' and try to reuse them. This will (hopefully) | |
311 | \ give some insight into what the latest sequence of failures was. | |
312 | \ | |
313 | : alloc-frame ( -- n ) | |
314 | 1 checkalloc +! ( ) | |
315 | -1 checktrack @ checkmax @ bounds ?do ( -1 ) | |
316 | i c@ 0= if ( -1 ) | |
317 | i c! ( ) | |
318 | i checktrack @ - leave ( n ) | |
319 | then ( n ) | |
320 | loop ( n ) | |
321 | dup 0< if | |
322 | free-oldest-frames ( ) | |
323 | ." FATAL: no exception frames available, " | |
324 | checknested @ checknested on if | |
325 | ." NESTED ERRORs, going interactive" cr | |
326 | begin interact again | |
327 | else | |
328 | ." forcing misaligned trap" cr | |
329 | -1 @ | |
330 | then | |
331 | then | |
332 | /check-frame * checkbase @ + ( n ) | |
333 | checkalloc @ over >check-age l! ( n ) | |
334 | ; | |
335 | ||
336 | : free-frame ( ptr -- ) | |
337 | 0 swap checkbase @ - /check-frame / checktrack @ + c! | |
338 | ; | |
339 | ||
340 | : (free-checkpt) ( frame -- ) | |
341 | dup >check-prev @ checkpt ! ( frame ) | |
342 | free-frame ( ) | |
343 | ; | |
344 | ||
345 | headers | |
346 | ||
347 | \ free all allocated frames. used to wipe out frames when reentering | |
348 | \ obp (l1-a, halt, exception, etc). the allocated frames are stale and | |
349 | \ we prefer to begin with a complete set. otherwise, frames will | |
350 | \ slowly leak as we exit/reenter forth (go, breakpoint, etc). | |
351 | : reset-checkpts ( -- ) | |
352 | checktrack @ checkmax @ erase | |
353 | checkpt off | |
354 | ; | |
355 | ||
356 | \ Dispose the current exception frame. | |
357 | \ | |
358 | : pop-checkpt ( -- ) checkpt @ ?dup if (free-checkpt) then ; | |
359 | ||
360 | \ If a non-zero throw is done, then we unwind the current checkpoint | |
361 | \ | |
362 | : throw ( n -- ) ?dup if checkpt @ ?dup if restore-checkpt then then ; | |
363 | ||
364 | \ | |
365 | \ Create an exception frame and push it onto the exception stack | |
366 | \ | |
367 | : push-checkpt ( ??? -- ??? code ) | |
368 | alloc-frame >r ( ??? ) | |
369 | checkpt @ r@ >check-prev ! ( ??? ) | |
370 | my-self r@ >check-myself l! ( ??? ) | |
371 | r@ save-checkpt dup if ( ??? code ) | |
372 | r> ( ??? code frame ) | |
373 | dup >check-myself l@ is my-self ( ??? code frame ) | |
374 | (free-checkpt) ( ??? code ) | |
375 | else ( ??? 0 ) | |
376 | r> checkpt ! ( ??? 0 ) | |
377 | then ( ??? code ) | |
378 | ; | |
379 | ||
380 | : catch ( ??? acf -- code ) | |
381 | push-checkpt ?dup if | |
382 | nip | |
383 | else | |
384 | execute pop-checkpt 0 | |
385 | then | |
386 | ; | |
387 | ||
388 | chain: init | |
389 | init-checkpt | |
390 | ; |