1 /* $NetBSD: autoconf.c,v 1.30 2023/12/20 15:29:06 thorpej Exp $ */
2
3 /*-
4 * Copyright (c) 2006 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Tim Rightnour
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 /*
33 * Setup the system to run on the current machine.
34 */
35
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: autoconf.c,v 1.30 2023/12/20 15:29:06 thorpej Exp $");
38
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/buf.h>
42 #include <sys/disklabel.h>
43 #include <sys/conf.h>
44 #include <sys/reboot.h>
45 #include <sys/device.h>
46 #include <sys/queue.h>
47
48 #include <machine/pte.h>
49 #include <machine/intr.h>
50
51 #include <dev/pci/pcivar.h>
52 #include <dev/pci/pcidevs.h>
53 #include <dev/scsipi/scsi_all.h>
54 #include <dev/scsipi/scsipi_all.h>
55 #include <dev/scsipi/scsiconf.h>
56 #include <dev/ata/atavar.h>
57 #include <dev/ic/wdcvar.h>
58 #include <machine/isa_machdep.h>
59 #include <dev/isa/isareg.h>
60 #include <prep/pnpbus/pnpbusvar.h>
61
62 #include "opt_nvram.h"
63
64 u_long bootdev = 0; /* should be dev_t, but not until 32 bits */
65 extern char bootpath[256];
66
67 static void findroot(void);
68
69 /*
70 * Determine i/o configuration for a machine.
71 */
72 void
cpu_configure(void)73 cpu_configure(void)
74 {
75 if (config_rootfound("mainbus", NULL) == NULL)
76 panic("configure: mainbus not configured");
77
78 genppc_cpu_configure();
79 }
80
81 void
cpu_rootconf(void)82 cpu_rootconf(void)
83 {
84 findroot();
85
86 aprint_normal("boot device: %s\n",
87 booted_device ? device_xname(booted_device) : "<unknown>");
88
89 rootconf();
90 }
91
92 /*
93 * On the PReP, we do not have the fw-boot-device readable until the NVRAM
94 * device is attached. This occurs rather late in the autoconf process.
95 * We build a OWF-like device node name for each device and store it in
96 * fw-path. Later when trying to figure out our boot device, we will match
97 * it against these strings. This serves a dual purpose, because if we ever
98 * wish to change the bootlist on the machine via the NVRAM, we will have to
99 * plug in these addresses for each device we want to use. The kernel can
100 * then query each device for fw-path, and generate a bootlist string.
101 */
102
103 void
device_register(device_t dev,void * aux)104 device_register(device_t dev, void *aux)
105 {
106 device_t parent;
107 char devpath[256];
108 prop_string_t str1;
109 int n;
110
111 /* Certain devices will *never* be bootable. short circuit them. */
112
113 if (device_is_a(dev, "com") || device_is_a(dev, "attimer") ||
114 device_is_a(dev, "pcppi") || device_is_a(dev, "mcclock") ||
115 device_is_a(dev, "mkclock") || device_is_a(dev, "lpt") ||
116 device_is_a(dev, "pckbc") || device_is_a(dev, "pckbd") ||
117 device_is_a(dev, "vga") || device_is_a(dev, "wsdisplay") ||
118 device_is_a(dev, "wskbd") || device_is_a(dev, "wsmouse") ||
119 device_is_a(dev, "pms") || device_is_a(dev, "cpu"))
120 return;
121
122 if (device_is_a(dev, "pchb")) {
123 struct pci_attach_args *pa = aux;
124 prop_bool_t rav;
125
126 if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_MOT &&
127 PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_MOT_RAVEN) {
128 rav = prop_bool_create(true);
129 KASSERT(rav != NULL);
130 (void) prop_dictionary_set(
131 device_properties(device_parent(dev)),
132 "prep-raven-pchb", rav);
133 prop_object_release(rav);
134 }
135 }
136
137 if (device_is_a(dev, "mainbus")) {
138 str1 = prop_string_create_cstring("/");
139 KASSERT(str1 != NULL);
140 (void) prop_dictionary_set(device_properties(dev),
141 "prep-fw-path-component", str1);
142 prop_object_release(str1);
143 return;
144 }
145 parent = device_parent(dev);
146
147 n = 0;
148 if (device_is_a(dev, "pci")) {
149 if (device_is_a(parent, "ppb"))
150 n = snprintf(devpath, sizeof(devpath), "");
151 else
152 n = snprintf(devpath, sizeof(devpath), "pci@%x",
153 prep_io_space_tag.pbs_offset);
154 }
155 if (device_is_a(parent, "pci")) {
156 struct pci_attach_args *pa = aux;
157
158 n = snprintf(devpath, sizeof(devpath), "pci%x,%x@%x,%x",
159 PCI_VENDOR(pa->pa_id), PCI_PRODUCT(pa->pa_id),
160 pa->pa_device, pa->pa_function);
161 }
162 if (device_is_a(parent, "pnpbus")) {
163 struct pnpbus_dev_attach_args *pna = aux;
164 struct pnpbus_io *io;
165
166 n = snprintf(devpath, sizeof(devpath), "%s@",
167 pna->pna_devid);
168 io = SIMPLEQ_FIRST(&pna->pna_res.io);
169 if (n > sizeof(devpath))
170 n = sizeof(devpath);
171 if (io != NULL)
172 n += snprintf(devpath + n, sizeof(devpath) - n, "%x",
173 io->minbase);
174 }
175
176 if (n > sizeof(devpath))
177 n = sizeof(devpath);
178 /* we can't trust the device tag on the ethernet, because
179 * the spec lies about how it is formed. Therefore we will leave it
180 * blank, and trim the end off any ethernet stuff. */
181 if (device_class(dev) == DV_IFNET)
182 n += snprintf(devpath + n, sizeof(devpath) - n, ":");
183 else if (device_is_a(dev, "cd"))
184 n = snprintf(devpath, sizeof(devpath), "cdrom@");
185 else if (device_class(dev) == DV_DISK)
186 n = snprintf(devpath, sizeof(devpath), "harddisk@");
187 else if (device_class(dev) == DV_TAPE)
188 n = snprintf(devpath, sizeof(devpath), "tape@");
189 else if (device_is_a(dev, "fd"))
190 n = snprintf(devpath, sizeof(devpath), "floppy@");
191
192 if (device_is_a(parent, "scsibus") || device_is_a(parent, "atapibus")) {
193 struct scsipibus_attach_args *sa = aux;
194
195 /* periph_target is target for scsi, drive # for atapi */
196 if (n > sizeof(devpath))
197 n = sizeof(devpath);
198 n += snprintf(devpath + n, sizeof(devpath) - n, "%d",
199 sa->sa_periph->periph_target);
200 if (n > sizeof(devpath))
201 n = sizeof(devpath);
202 if (device_is_a(parent, "scsibus"))
203 n += snprintf(devpath + n, sizeof(devpath) - n, ",%d",
204 sa->sa_periph->periph_lun);
205 } else if (device_is_a(parent, "atabus") ||
206 device_is_a(parent, "pciide")) {
207 struct ata_device *adev = aux;
208
209 if (n > sizeof(devpath))
210 n = sizeof(devpath);
211 n += snprintf(devpath + n, sizeof(devpath) - n, "%d",
212 adev->adev_drv_data->drive);
213 } else if (device_is_a(dev, "fd")) {
214 if (n > sizeof(devpath))
215 n = sizeof(devpath);
216 /* XXX device_unit() abuse */
217 n += snprintf(devpath + n, sizeof(devpath) - n, "%d",
218 device_unit(dev));
219 }
220
221 str1 = prop_string_create_cstring(devpath);
222 KASSERT(str1 != NULL);
223 (void)prop_dictionary_set(device_properties(dev),
224 "prep-fw-path-component", str1);
225 prop_object_release(str1);
226 }
227
228 static void
gen_fwpath(device_t dev)229 gen_fwpath(device_t dev)
230 {
231 device_t parent;
232 prop_string_t str1, str2, str3;
233
234 parent = device_parent(dev);
235 str1 = prop_dictionary_get(device_properties(dev),
236 "prep-fw-path-component");
237 if (str1 == NULL)
238 return;
239 if (parent == NULL) {
240 prop_dictionary_set(device_properties(dev), "prep-fw-path",
241 str1);
242 return;
243 }
244 str2 = prop_dictionary_get(device_properties(parent), "prep-fw-path");
245 if (str2 == NULL) {
246 prop_dictionary_set(device_properties(dev), "prep-fw-path",
247 str1);
248 return;
249 }
250 str3 = prop_string_copy(str2);
251 KASSERT(str3 != NULL);
252 if (!(prop_string_equals_cstring(str3, "/") ||
253 prop_string_equals_cstring(str1, "")))
254 prop_string_append_cstring(str3, "/");
255 if (!prop_string_equals_cstring(str1, "/"))
256 prop_string_append(str3, str1);
257 #if defined(NVRAM_DUMP)
258 printf("%s devpath: %s+%s == %s\n", device_xname(dev),
259 prop_string_cstring_nocopy(str2),
260 prop_string_cstring_nocopy(str1),
261 prop_string_cstring_nocopy(str3));
262 #endif
263 (void)prop_dictionary_set(device_properties(dev),
264 "prep-fw-path", str3);
265 prop_object_release(str3);
266 }
267
268 /*
269 * Generate properties for each device by totaling up its parent device
270 */
271 static void
build_fwpath(void)272 build_fwpath(void)
273 {
274 device_t dev, d;
275 deviter_t di, inner_di;
276 prop_string_t str1;
277
278 /* First, find all the PCI busses */
279 for (dev = deviter_first(&di, DEVITER_F_ROOT_FIRST); dev != NULL;
280 dev = deviter_next(&di)) {
281 if (device_is_a(dev, "pci") || device_is_a(dev, "mainbus") ||
282 device_is_a(dev, "pcib") || device_is_a(dev, "pceb") ||
283 device_is_a(dev, "ppb"))
284 gen_fwpath(dev);
285 }
286 deviter_release(&di);
287
288 /* Now go find the ISA bus and fix it up */
289 for (dev = deviter_first(&di, DEVITER_F_ROOT_FIRST); dev != NULL;
290 dev = deviter_next(&di)) {
291 if (device_is_a(dev, "isa"))
292 gen_fwpath(dev);
293 }
294 deviter_release(&di);
295
296 for (dev = deviter_first(&di, DEVITER_F_ROOT_FIRST); dev != NULL;
297 dev = deviter_next(&di)) {
298 /* skip the ones we already computed above */
299 if (device_is_a(dev, "pci") || device_is_a(dev, "pcib") ||
300 device_is_a(dev, "pceb") || device_is_a(dev, "isa") ||
301 device_is_a(dev, "ppb"))
302 continue;
303 /* patch in the properties for the pnpbus */
304 if (device_is_a(dev, "pnpbus")) {
305 for (d = deviter_first(&inner_di, DEVITER_F_ROOT_FIRST);
306 d != NULL;
307 d = deviter_next(&inner_di)) {
308 if (!device_is_a(d, "isa"))
309 continue;
310 str1 = prop_dictionary_get(device_properties(d),
311 "prep-fw-path");
312 if (str1 == NULL)
313 continue;
314 prop_dictionary_set(device_properties(dev),
315 "prep-fw-path", str1);
316 }
317 deviter_release(&inner_di);
318 } else
319 gen_fwpath(dev);
320 }
321 deviter_release(&di);
322 }
323
324
325 /*
326 * This routine looks at each device, and tries to match them to the bootpath
327 */
328
329 void
findroot(void)330 findroot(void)
331 {
332 device_t d;
333 deviter_t di;
334 char *cp;
335 prop_string_t str;
336 size_t len;
337
338 /* first rebuild all the device paths */
339 build_fwpath();
340
341 /* now trim the ethernet crap off the bootpath */
342 cp = strchr(bootpath, ':');
343 if (cp != NULL) {
344 cp++;
345 *cp = '\0';
346 }
347 len = strlen(bootpath);
348 #if defined(NVRAM_DUMP)
349 printf("Modified bootpath: %s\n", bootpath);
350 #endif
351 for (d = deviter_first(&di, DEVITER_F_ROOT_FIRST);
352 d != NULL;
353 d = deviter_next(&di)) {
354 str = prop_dictionary_get(device_properties(d), "prep-fw-path");
355 if (str == NULL)
356 continue;
357 #if defined(NVRAM_DUMP)
358 printf("dev %s: fw-path: %s\n", device_xname(d),
359 prop_string_cstring_nocopy(str));
360 #endif
361 if (strncmp(prop_string_cstring_nocopy(str), bootpath,
362 len) == 0)
363 break;
364 }
365 deviter_release(&di);
366 if (d != NULL) {
367 booted_device = d;
368 booted_partition = 0; /* XXX ??? */
369 }
370 }
371