bootmain.c revision 1.3.4.2 1 1.3.4.2 nathanw /* $NetBSD: bootmain.c,v 1.3.4.2 2002/01/08 00:28:45 nathanw Exp $ */
2 1.3.4.2 nathanw
3 1.3.4.2 nathanw /*-
4 1.3.4.2 nathanw * Copyright (c) 1993, 1994 Takumi Nakamura.
5 1.3.4.2 nathanw * Copyright (c) 1999, 2000 Itoh Yasufumi.
6 1.3.4.2 nathanw * Copyright (c) 2001 Minoura Makoto.
7 1.3.4.2 nathanw *
8 1.3.4.2 nathanw * Redistribution and use in source and binary forms, with or without
9 1.3.4.2 nathanw * modification, are permitted provided that the following conditions
10 1.3.4.2 nathanw * are met:
11 1.3.4.2 nathanw * 1. Redistributions of source code must retain the above copyright
12 1.3.4.2 nathanw * notice, this list of conditions and the following disclaimer.
13 1.3.4.2 nathanw * 2. Redistributions in binary form must reproduce the above copyright
14 1.3.4.2 nathanw * notice, this list of conditions and the following disclaimer in the
15 1.3.4.2 nathanw * documentation and/or other materials provided with the distribution.
16 1.3.4.2 nathanw * 3. All advertising materials mentioning features or use of this software
17 1.3.4.2 nathanw * must display the following acknowledgement:
18 1.3.4.2 nathanw * This product includes software developed by Takumi Nakamura.
19 1.3.4.2 nathanw * 4. Neither the name of the University nor the names of its contributors
20 1.3.4.2 nathanw * may be used to endorse or promote products derived from this software
21 1.3.4.2 nathanw * without specific prior written permission.
22 1.3.4.2 nathanw *
23 1.3.4.2 nathanw * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 1.3.4.2 nathanw * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 1.3.4.2 nathanw * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 1.3.4.2 nathanw * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 1.3.4.2 nathanw * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 1.3.4.2 nathanw * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 1.3.4.2 nathanw * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 1.3.4.2 nathanw * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 1.3.4.2 nathanw * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 1.3.4.2 nathanw * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 1.3.4.2 nathanw * SUCH DAMAGE.
34 1.3.4.2 nathanw */
35 1.3.4.2 nathanw
36 1.3.4.2 nathanw #include <sys/param.h>
37 1.3.4.2 nathanw #include <sys/reboot.h>
38 1.3.4.2 nathanw #include <sys/types.h>
39 1.3.4.2 nathanw #include <sys/exec_aout.h>
40 1.3.4.2 nathanw #include <ufs/ufs/dinode.h>
41 1.3.4.2 nathanw #include <ufs/ffs/fs.h>
42 1.3.4.2 nathanw #include <ufs/ufs/dir.h>
43 1.3.4.2 nathanw #include <machine/bootinfo.h>
44 1.3.4.2 nathanw #ifdef SCSI_ADHOC_BOOTPART
45 1.3.4.2 nathanw #include <machine/disklabel.h>
46 1.3.4.2 nathanw #endif
47 1.3.4.2 nathanw
48 1.3.4.2 nathanw #include "boot_ufs.h"
49 1.3.4.2 nathanw #include "readufs.h"
50 1.3.4.2 nathanw #include "exec_image.h"
51 1.3.4.2 nathanw #include "../../x68k/iodevice.h"
52 1.3.4.2 nathanw #define IODEVbase ((volatile struct IODEVICE *)PHYS_IODEV)
53 1.3.4.2 nathanw
54 1.3.4.2 nathanw /* for debug; */
55 1.3.4.2 nathanw unsigned int startregs[16];
56 1.3.4.2 nathanw
57 1.3.4.2 nathanw #ifdef SCSI_ADHOC_BOOTPART
58 1.3.4.2 nathanw static int get_scsi_part (void);
59 1.3.4.2 nathanw #endif
60 1.3.4.2 nathanw #ifdef BOOT_DEBUG
61 1.3.4.2 nathanw static int get_scsi_host_adapter (char *);
62 1.3.4.2 nathanw #else
63 1.3.4.2 nathanw static int get_scsi_host_adapter (void);
64 1.3.4.2 nathanw #endif
65 1.3.4.2 nathanw
66 1.3.4.2 nathanw #ifdef BOOT_DEBUG
67 1.3.4.2 nathanw void print_hex (unsigned int, int);
68 1.3.4.2 nathanw #endif
69 1.3.4.2 nathanw
70 1.3.4.2 nathanw static int load_file (const char*, unsigned int, struct exec *);
71 1.3.4.2 nathanw static int load_file_ino (ino_t, const char*, unsigned int, struct exec *);
72 1.3.4.2 nathanw
73 1.3.4.2 nathanw void bootufs (void) __attribute__ ((__noreturn__));
74 1.3.4.2 nathanw
75 1.3.4.2 nathanw #ifdef BOOT_DEBUG
76 1.3.4.2 nathanw void
77 1.3.4.2 nathanw print_hex(x, l)
78 1.3.4.2 nathanw unsigned int x; /* */
79 1.3.4.2 nathanw int l; /* */
80 1.3.4.2 nathanw {
81 1.3.4.2 nathanw
82 1.3.4.2 nathanw if (l > 0) {
83 1.3.4.2 nathanw print_hex(x >> 4, l - 1);
84 1.3.4.2 nathanw x &= 0x0F;
85 1.3.4.2 nathanw if (x > 9)
86 1.3.4.2 nathanw x += 7;
87 1.3.4.2 nathanw B_PUTC((unsigned int) '0' + x);
88 1.3.4.2 nathanw }
89 1.3.4.2 nathanw }
90 1.3.4.2 nathanw #endif
91 1.3.4.2 nathanw
92 1.3.4.2 nathanw #ifdef SCSI_ADHOC_BOOTPART
93 1.3.4.2 nathanw /*
94 1.3.4.2 nathanw * get partition # from partition start position
95 1.3.4.2 nathanw */
96 1.3.4.2 nathanw
97 1.3.4.2 nathanw #define NPART 15
98 1.3.4.2 nathanw #define PARTTBL_TOP ((unsigned)4) /* pos of part inf in 512byte-blocks */
99 1.3.4.2 nathanw #define MAXPART 6
100 1.3.4.2 nathanw const unsigned char partition_conv[MAXPART + 1] = { 0, 1, 3, 4, 5, 6, 7 };
101 1.3.4.2 nathanw
102 1.3.4.2 nathanw static int
103 1.3.4.2 nathanw get_scsi_part()
104 1.3.4.2 nathanw {
105 1.3.4.2 nathanw struct {
106 1.3.4.2 nathanw u_int32_t magic; /* 0x5836384B ("X68K") */
107 1.3.4.2 nathanw u_int32_t parttotal;
108 1.3.4.2 nathanw u_int32_t diskblocks;
109 1.3.4.2 nathanw u_int32_t diskblocks2; /* backup? */
110 1.3.4.2 nathanw struct dos_partition parttbl[NPART];
111 1.3.4.2 nathanw unsigned char formatstr[256];
112 1.3.4.2 nathanw unsigned char rest[512];
113 1.3.4.2 nathanw } partbuf;
114 1.3.4.2 nathanw int i;
115 1.3.4.2 nathanw u_int32_t part_top;
116 1.3.4.2 nathanw
117 1.3.4.2 nathanw #ifdef BOOT_DEBUG
118 1.3.4.2 nathanw B_PRINT("seclen: ");
119 1.3.4.2 nathanw print_hex(SCSI_BLKLEN, 8); /* 0: 256, 1: 512, 2: 1024 */
120 1.3.4.2 nathanw B_PRINT(", topsec: ");
121 1.3.4.2 nathanw print_hex(SCSI_PARTTOP, 8); /* partition top in sector */
122 1.3.4.2 nathanw #endif
123 1.3.4.2 nathanw /*
124 1.3.4.2 nathanw * read partition table
125 1.3.4.2 nathanw */
126 1.3.4.2 nathanw RAW_READ0(&partbuf, PARTTBL_TOP, sizeof partbuf);
127 1.3.4.2 nathanw
128 1.3.4.2 nathanw part_top = SCSI_PARTTOP >> (2 - SCSI_BLKLEN);
129 1.3.4.2 nathanw for (i = 0; i < MAXPART; i++)
130 1.3.4.2 nathanw if ((u_int32_t) partbuf.parttbl[i].dp_start == part_top)
131 1.3.4.2 nathanw goto found;
132 1.3.4.2 nathanw
133 1.3.4.2 nathanw BOOT_ERROR("Can't boot from this partition");
134 1.3.4.2 nathanw /* NOTREACHED */
135 1.3.4.2 nathanw found:
136 1.3.4.2 nathanw #ifdef BOOT_DEBUG
137 1.3.4.2 nathanw B_PRINT("; sd");
138 1.3.4.2 nathanw B_PUTC(ID + '0'); /* SCSI ID (not NetBSD unit #) */
139 1.3.4.2 nathanw B_PUTC((unsigned int) partition_conv[i] + 'a');
140 1.3.4.2 nathanw B_PRINT("\r\n");
141 1.3.4.2 nathanw #endif
142 1.3.4.2 nathanw return partition_conv[i];
143 1.3.4.2 nathanw }
144 1.3.4.2 nathanw #endif /* SCSI_ADHOC_BOOTPART */
145 1.3.4.2 nathanw
146 1.3.4.2 nathanw /*
147 1.3.4.2 nathanw * Check the type of SCSI interface
148 1.3.4.2 nathanw */
149 1.3.4.2 nathanw #ifdef BOOT_DEBUG
150 1.3.4.2 nathanw static int
151 1.3.4.2 nathanw get_scsi_host_adapter(devstr)
152 1.3.4.2 nathanw char *devstr;
153 1.3.4.2 nathanw #else
154 1.3.4.2 nathanw static int
155 1.3.4.2 nathanw get_scsi_host_adapter(void)
156 1.3.4.2 nathanw #endif
157 1.3.4.2 nathanw {
158 1.3.4.2 nathanw char *bootrom;
159 1.3.4.2 nathanw int ha;
160 1.3.4.2 nathanw
161 1.3.4.2 nathanw #ifdef BOOT_DEBUG
162 1.3.4.2 nathanw B_PRINT(" at ");
163 1.3.4.2 nathanw *(int *)devstr = '/' << 24 | 's' << 16 | 'p' << 8 | 'c';
164 1.3.4.2 nathanw *(int *)(devstr + 4) = '@' << 24 | '0' << 16 | '/' << 8 | 's';
165 1.3.4.2 nathanw *(int *)(devstr + 8) = 'd' << 24 | '@' << 16 | '0' << 8 | ',';
166 1.3.4.2 nathanw *(int *)(devstr + 12) = '0' << 24 | ':' << 16 | 'a' << 8 | '\0';
167 1.3.4.2 nathanw #endif
168 1.3.4.2 nathanw
169 1.3.4.2 nathanw bootrom = (char *) (BOOT_INFO & 0x00ffffe0);
170 1.3.4.2 nathanw /*
171 1.3.4.2 nathanw * bootrom+0x24 "SCSIIN" ... Internal SCSI (spc@0)
172 1.3.4.2 nathanw * "SCSIEX" ... External SCSI (spc@1 or mha@0)
173 1.3.4.2 nathanw */
174 1.3.4.2 nathanw if (*(u_short *)(bootrom + 0x24 + 4) == 0x494e) { /* "IN" */
175 1.3.4.2 nathanw #ifdef BOOT_DEBUG
176 1.3.4.2 nathanw B_PRINT("spc0");
177 1.3.4.2 nathanw #endif
178 1.3.4.2 nathanw ha = (X68K_BOOT_SCSIIF_SPC << 4) | 0;
179 1.3.4.2 nathanw } else if (badbaddr(&IODEVbase->io_exspc.bdid)) {
180 1.3.4.2 nathanw #ifdef BOOT_DEBUG
181 1.3.4.2 nathanw B_PRINT("mha0");
182 1.3.4.2 nathanw #endif
183 1.3.4.2 nathanw ha = (X68K_BOOT_SCSIIF_MHA << 4) | 0;
184 1.3.4.2 nathanw #ifdef BOOT_DEBUG
185 1.3.4.2 nathanw *(int *)devstr = '/' << 24 | 'm' << 16 | 'h' << 8 | 'a';
186 1.3.4.2 nathanw #endif
187 1.3.4.2 nathanw } else {
188 1.3.4.2 nathanw #ifdef BOOT_DEBUG
189 1.3.4.2 nathanw B_PRINT("spc1");
190 1.3.4.2 nathanw #endif
191 1.3.4.2 nathanw ha = (X68K_BOOT_SCSIIF_SPC << 4) | 1;
192 1.3.4.2 nathanw #ifdef BOOT_DEBUG
193 1.3.4.2 nathanw devstr[5] = '1';
194 1.3.4.2 nathanw #endif
195 1.3.4.2 nathanw }
196 1.3.4.2 nathanw
197 1.3.4.2 nathanw return ha;
198 1.3.4.2 nathanw }
199 1.3.4.2 nathanw
200 1.3.4.2 nathanw static int
201 1.3.4.2 nathanw load_file(path, addr, header)
202 1.3.4.2 nathanw const char *path;
203 1.3.4.2 nathanw unsigned int addr;
204 1.3.4.2 nathanw struct exec *header;
205 1.3.4.2 nathanw {
206 1.3.4.2 nathanw
207 1.3.4.2 nathanw return load_file_ino(ufs_lookup_path(path), path, addr, header);
208 1.3.4.2 nathanw }
209 1.3.4.2 nathanw
210 1.3.4.2 nathanw static int
211 1.3.4.2 nathanw load_file_ino(ino, fn, addr, header)
212 1.3.4.2 nathanw ino_t ino;
213 1.3.4.2 nathanw const char *fn; /* for message only */
214 1.3.4.2 nathanw unsigned int addr;
215 1.3.4.2 nathanw struct exec *header;
216 1.3.4.2 nathanw {
217 1.3.4.2 nathanw struct dinode dinode;
218 1.3.4.2 nathanw
219 1.3.4.2 nathanw /* look-up the file */
220 1.3.4.2 nathanw if (ino == 0 || ufs_get_inode(ino, &dinode)) {
221 1.3.4.2 nathanw B_PRINT(fn);
222 1.3.4.2 nathanw B_PRINT(": not found\r\n");
223 1.3.4.2 nathanw return 0;
224 1.3.4.2 nathanw }
225 1.3.4.2 nathanw
226 1.3.4.2 nathanw ufs_read(&dinode, (void *)addr, 0, sizeof(struct exec));
227 1.3.4.2 nathanw memcpy(header, (void *)addr, sizeof(struct exec));
228 1.3.4.2 nathanw
229 1.3.4.2 nathanw if ((N_GETMAGIC(*header) != OMAGIC) ||
230 1.3.4.2 nathanw (N_GETMID(*header) != MID_M68K)) {
231 1.3.4.2 nathanw B_PRINT(fn);
232 1.3.4.2 nathanw B_PRINT(": inappropriate format");
233 1.3.4.2 nathanw return 0;
234 1.3.4.2 nathanw }
235 1.3.4.2 nathanw
236 1.3.4.2 nathanw /* read text and data */
237 1.3.4.2 nathanw ufs_read(&dinode, ((char *)addr)-sizeof(struct exec), 0,/* XXX */
238 1.3.4.2 nathanw header->a_text+header->a_data);
239 1.3.4.2 nathanw
240 1.3.4.2 nathanw /* clear out bss */
241 1.3.4.2 nathanw memset((char*) addr + header->a_text+header->a_data,
242 1.3.4.2 nathanw 0, header->a_bss);
243 1.3.4.2 nathanw
244 1.3.4.2 nathanw /* PLANNED: fallback NMAGIC loader for the kernel. */
245 1.3.4.2 nathanw
246 1.3.4.2 nathanw /* return the image size. */
247 1.3.4.2 nathanw return header->a_text+header->a_data+header->a_bss;
248 1.3.4.2 nathanw }
249 1.3.4.2 nathanw
250 1.3.4.2 nathanw
251 1.3.4.2 nathanw void
252 1.3.4.2 nathanw bootufs(void)
253 1.3.4.2 nathanw {
254 1.3.4.2 nathanw int bootdev;
255 1.3.4.2 nathanw #ifdef BOOT_DEBUG
256 1.3.4.2 nathanw int i;
257 1.3.4.2 nathanw char bootdevstr[16];
258 1.3.4.2 nathanw #endif
259 1.3.4.2 nathanw struct exec header;
260 1.3.4.2 nathanw int size;
261 1.3.4.2 nathanw extern const char bootprog_name[], bootprog_rev[];
262 1.3.4.2 nathanw
263 1.3.4.2 nathanw #ifdef BOOT_DEBUG
264 1.3.4.2 nathanw /* for debug; */
265 1.3.4.2 nathanw for (i = 0; i < 16; i++) {
266 1.3.4.2 nathanw print_hex(startregs[i], 8);
267 1.3.4.2 nathanw B_PRINT((i & 7) == 7 ? "\r\n" : " ");
268 1.3.4.2 nathanw }
269 1.3.4.2 nathanw #endif
270 1.3.4.2 nathanw
271 1.3.4.2 nathanw B_PRINT(bootprog_name);
272 1.3.4.2 nathanw B_PRINT(" rev."); B_PRINT(bootprog_rev);
273 1.3.4.2 nathanw B_PRINT("\r\n");
274 1.3.4.2 nathanw
275 1.3.4.2 nathanw /*
276 1.3.4.2 nathanw * get boot device
277 1.3.4.2 nathanw */
278 1.3.4.2 nathanw if (BINF_ISFD(&BOOT_INFO)) {
279 1.3.4.2 nathanw /* floppy */
280 1.3.4.2 nathanw #ifdef BOOT_DEBUG
281 1.3.4.2 nathanw *(int *)bootdevstr = ('f' << 24 | 'd' << 16 | '@' << 8 | '0') +
282 1.3.4.2 nathanw (BOOT_INFO & 3);
283 1.3.4.2 nathanw bootdevstr[4] = '\0';
284 1.3.4.2 nathanw #endif
285 1.3.4.2 nathanw bootdev = X68K_MAKEBOOTDEV(X68K_MAJOR_FD, BOOT_INFO & 3,
286 1.3.4.2 nathanw (FDSECMINMAX.minsec.N == 3) ? 0 : 2);
287 1.3.4.2 nathanw } else {
288 1.3.4.2 nathanw /* SCSI */
289 1.3.4.2 nathanw int part, ha;
290 1.3.4.2 nathanw
291 1.3.4.2 nathanw #ifdef SCSI_ADHOC_BOOTPART
292 1.3.4.2 nathanw if (SCSI_PARTTOP == 0)
293 1.3.4.2 nathanw part = 0;
294 1.3.4.2 nathanw else
295 1.3.4.2 nathanw part = get_scsi_part();
296 1.3.4.2 nathanw #else
297 1.3.4.2 nathanw part = 0; /* sd?a only */
298 1.3.4.2 nathanw #endif
299 1.3.4.2 nathanw #ifndef BOOT_DEBUG
300 1.3.4.2 nathanw ha = get_scsi_host_adapter();
301 1.3.4.2 nathanw #else
302 1.3.4.2 nathanw ha = get_scsi_host_adapter(bootdevstr);
303 1.3.4.2 nathanw bootdevstr[10] = '0' + (ID & 7);
304 1.3.4.2 nathanw bootdevstr[14] = 'a' + part;
305 1.3.4.2 nathanw #endif
306 1.3.4.2 nathanw bootdev = X68K_MAKESCSIBOOTDEV(X68K_MAJOR_SD, ha >> 4, ha & 15,
307 1.3.4.2 nathanw ID & 7, 0, part);
308 1.3.4.2 nathanw }
309 1.3.4.2 nathanw #ifdef BOOT_DEBUG
310 1.3.4.2 nathanw B_PRINT("boot device: ");
311 1.3.4.2 nathanw B_PRINT(bootdevstr);
312 1.3.4.2 nathanw #endif
313 1.3.4.2 nathanw B_PRINT("\r\n");
314 1.3.4.2 nathanw
315 1.3.4.2 nathanw /* initialize filesystem code */
316 1.3.4.2 nathanw if (ufs_init()) {
317 1.3.4.2 nathanw BOOT_ERROR("bogus super block: "
318 1.3.4.2 nathanw "");
319 1.3.4.2 nathanw /* NOTREACHED */
320 1.3.4.2 nathanw }
321 1.3.4.2 nathanw #if defined(BOOT_DEBUG) && defined(USE_FFS) && defined(USE_LFS)
322 1.3.4.2 nathanw B_PRINT("file system: ");
323 1.3.4.2 nathanw B_PUTC(ufs_info.fstype == UFSTYPE_FFS ?
324 1.3.4.2 nathanw (unsigned int) 'F' : (unsigned int) 'L');
325 1.3.4.2 nathanw B_PRINT("FS\r\n");
326 1.3.4.2 nathanw #endif
327 1.3.4.2 nathanw
328 1.3.4.2 nathanw #ifdef BOOT_DEBUG
329 1.3.4.2 nathanw B_PRINT("\r\nlooking up secondary boot... ");
330 1.3.4.2 nathanw #endif
331 1.3.4.2 nathanw
332 1.3.4.2 nathanw /*
333 1.3.4.2 nathanw * Look for the 2nd stage boot.
334 1.3.4.2 nathanw */
335 1.3.4.2 nathanw
336 1.3.4.2 nathanw /* Try "boot" first */
337 1.3.4.2 nathanw size = load_file("boot", BOOT_TEXTADDR, &header);
338 1.3.4.2 nathanw #ifdef BOOT_DEBUG
339 1.3.4.2 nathanw B_PRINT("done.\r\n");
340 1.3.4.2 nathanw #endif
341 1.3.4.2 nathanw if (size > 0)
342 1.3.4.2 nathanw exec_image(BOOT_TEXTADDR, /* image loaded at */
343 1.3.4.2 nathanw BOOT_TEXTADDR, /* image executed at */
344 1.3.4.2 nathanw header.a_entry, /* entry point */
345 1.3.4.2 nathanw size, /* image size */
346 1.3.4.2 nathanw bootdev, RB_SINGLE); /* arguments */
347 1.3.4.2 nathanw
348 1.3.4.2 nathanw B_PRINT("can't load the secondary bootstrap.;"
349 1.3.4.2 nathanw "trying /netbsd...\r\n");
350 1.3.4.2 nathanw
351 1.3.4.2 nathanw /* fallback to /netbsd. */
352 1.3.4.2 nathanw /* always fails since NMAGIC loader is not yet implemented. */
353 1.3.4.2 nathanw
354 1.3.4.2 nathanw size = load_file("netbsd", 0x6000, &header);
355 1.3.4.2 nathanw if (size > 0) {
356 1.3.4.2 nathanw if (*((short *)(0x6000 + header.a_entry - 2)) != 0) {
357 1.3.4.2 nathanw B_PRINT("boot interface of /netbsd is too new!\r\n");
358 1.3.4.2 nathanw goto fail;
359 1.3.4.2 nathanw }
360 1.3.4.2 nathanw exec_image(0x6000, /* image loaded at */
361 1.3.4.2 nathanw 0, /* image executed at */
362 1.3.4.2 nathanw header.a_entry, /* entry point */
363 1.3.4.2 nathanw size, /* image size */
364 1.3.4.2 nathanw bootdev, RB_SINGLE); /* arguments */
365 1.3.4.2 nathanw /* NOTREACHED */
366 1.3.4.2 nathanw }
367 1.3.4.2 nathanw
368 1.3.4.2 nathanw fail:
369 1.3.4.2 nathanw BOOT_ERROR("can't load the secondary bootstrap nor the kernel.");
370 1.3.4.2 nathanw /* NOTREACHED */
371 1.3.4.2 nathanw }
372