source: mainline/uspace/app/sysinst/sysinst.c@ 9b95b964

Last change on this file since 9b95b964 was 9b95b964, checked in by Jiri Svoboda <jiri@…>, 15 months ago

Array of strings sys_dirs must be NULL-terminated

  • Property mode set to 100644
File size: 12.2 KB
Line 
1/*
2 * Copyright (c) 2024 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup sysinst
30 * @{
31 */
32/** @file System installer.
33 *
34 * Install the operating system onto a disk device. Note that this only works
35 * on ia32/amd64 with Grub platform 'pc'.
36 */
37
38#include <block.h>
39#include <byteorder.h>
40#include <capa.h>
41#include <errno.h>
42#include <fdisk.h>
43#include <loc.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <str.h>
47#include <str_error.h>
48#include <vfs/vfs.h>
49#include <vol.h>
50
51#include "futil.h"
52#include "grub.h"
53#include "rdimg.h"
54#include "volume.h"
55
56/** Device to install to
57 *
58 * Note that you cannot simply change this, because the installation
59 * device is hardcoded in core.img. If you wanted to install to another
60 * device, you must build your own core.img (e.g. using tools/grub/mkimage.sh
61 * and modifying tools/grub/load.cfg, supplying the device to boot from
62 * in Grub notation).
63 */
64#define DEFAULT_DEV_0 "devices/\\hw\\sys\\00:01.1\\c0d0"
65#define DEFAULT_DEV_1 "devices/\\hw\\sys\\00:01.0\\ata1\\c0d0"
66//#define DEFAULT_DEV "devices/\\hw\\pci0\\00:01.2\\uhci_rh\\usb01_a1\\mass-storage0\\l0"
67/** Volume label for the new file system */
68#define INST_VOL_LABEL "HelenOS"
69/** Mount point of system partition when running installed system */
70#define INST_VOL_MP "/w"
71
72#define MOUNT_POINT "/inst"
73
74/** HelenOS live CD volume label */
75#define CD_VOL_LABEL "HelenOS-CD"
76/** XXX Should get this from the volume server */
77#define CD_MOUNT_POINT "/vol/" CD_VOL_LABEL
78
79#define BOOT_FILES_SRC CD_MOUNT_POINT
80#define BOOT_BLOCK_IDX 0 /* MBR */
81
82static const char *default_devs[] = {
83 DEFAULT_DEV_0,
84 DEFAULT_DEV_1,
85 NULL
86};
87
88static const char *sys_dirs[] = {
89 "/cfg",
90 "/data",
91 NULL
92};
93
94/** Check the if the destination device exists.
95 *
96 * @param dev Disk device
97 *
98 * @return EOK on success or an error code
99 */
100static errno_t sysinst_check_dev(const char *dev)
101{
102 service_id_t sid;
103 errno_t rc;
104
105 rc = loc_service_get_id(dev, &sid, 0);
106 if (rc != EOK)
107 return rc;
108
109 (void)sid;
110 return EOK;
111}
112
113/** Label the destination device.
114 *
115 * @param dev Disk device to label
116 * @param psvc_id Place to store service ID of the created partition
117 *
118 * @return EOK on success or an error code
119 */
120static errno_t sysinst_label_dev(const char *dev, service_id_t *psvc_id)
121{
122 fdisk_t *fdisk;
123 fdisk_dev_t *fdev;
124 fdisk_part_t *part;
125 fdisk_part_spec_t pspec;
126 fdisk_part_info_t pinfo;
127 capa_spec_t capa;
128 service_id_t sid;
129 errno_t rc;
130
131 printf("sysinst_label_dev(): get service ID '%s'\n", dev);
132 rc = loc_service_get_id(dev, &sid, 0);
133 if (rc != EOK)
134 return rc;
135
136 printf("sysinst_label_dev(): open device\n");
137
138 rc = fdisk_create(&fdisk);
139 if (rc != EOK) {
140 printf("Error initializing fdisk.\n");
141 return rc;
142 }
143
144 rc = fdisk_dev_open(fdisk, sid, &fdev);
145 if (rc != EOK) {
146 printf("Error opening device.\n");
147 return rc;
148 }
149
150 printf("sysinst_label_dev(): create mount directory\n");
151
152 rc = vfs_link_path(MOUNT_POINT, KIND_DIRECTORY, NULL);
153 if (rc != EOK)
154 return rc;
155
156 printf("sysinst_label_dev(): create label\n");
157
158 rc = fdisk_label_create(fdev, lt_mbr);
159 if (rc != EOK) {
160 printf("Error creating label: %s.\n", str_error(rc));
161 return rc;
162 }
163
164 printf("sysinst_label_dev(): create partition\n");
165
166 rc = fdisk_part_get_max_avail(fdev, spc_pri, &capa);
167 if (rc != EOK) {
168 printf("Error getting available capacity: %s.\n", str_error(rc));
169 return rc;
170 }
171
172 fdisk_pspec_init(&pspec);
173 pspec.capacity = capa;
174 pspec.pkind = lpk_primary;
175 pspec.fstype = fs_ext4; /* Cannot be changed without modifying core.img */
176 pspec.mountp = MOUNT_POINT;
177 pspec.label = INST_VOL_LABEL;
178
179 rc = fdisk_part_create(fdev, &pspec, &part);
180 if (rc != EOK) {
181 printf("Error creating partition.\n");
182 return rc;
183 }
184
185 rc = fdisk_part_get_info(part, &pinfo);
186 if (rc != EOK) {
187 printf("Error getting partition information.\n");
188 return rc;
189 }
190
191 printf("sysinst_label_dev(): OK\n");
192 *psvc_id = pinfo.svc_id;
193 return EOK;
194}
195
196/** Set up system volume structure.
197 *
198 * @return EOK on success or an error code
199 */
200static errno_t sysinst_setup_sysvol(void)
201{
202 errno_t rc;
203 char *path = NULL;
204 const char **cp;
205 int rv;
206
207 cp = sys_dirs;
208 while (*cp != NULL) {
209 rv = asprintf(&path, "%s%s", MOUNT_POINT, *cp);
210 if (rv < 0) {
211 rc = ENOMEM;
212 goto error;
213 }
214
215 rc = vfs_link_path(path, KIND_DIRECTORY, NULL);
216 if (rc != EOK) {
217 printf("Error creating directory '%s'.\n", path);
218 goto error;
219 }
220
221 free(path);
222 path = NULL;
223 ++cp;
224 }
225
226 free(path);
227 path = NULL;
228
229 return EOK;
230error:
231 if (path != NULL)
232 free(path);
233 return rc;
234}
235
236/** Copy boot files.
237 *
238 * @return EOK on success or an error code
239 */
240static errno_t sysinst_copy_boot_files(void)
241{
242 errno_t rc;
243
244 printf("sysinst_copy_boot_files(): copy bootloader files\n");
245 rc = futil_rcopy_contents(BOOT_FILES_SRC, MOUNT_POINT);
246 if (rc != EOK)
247 return rc;
248
249 printf("sysinst_copy_boot_files(): OK\n");
250 return EOK;
251}
252
253/** Set up configuration in the initial RAM disk.
254 *
255 * @return EOK on success or an error code
256 */
257static errno_t sysinst_customize_initrd(void)
258{
259 errno_t rc;
260 rd_img_t *rd = NULL;
261 char *rdpath = NULL;
262 char *path = NULL;
263 vol_volumes_t *volumes = NULL;
264 vol_volume_t *volume = NULL;
265 int rv;
266
267 rc = rd_img_open(MOUNT_POINT "/boot/initrd.img", &rdpath, &rd);
268 if (rc != EOK) {
269 printf("Error opening initial RAM disk image.\n");
270 goto error;
271 }
272
273 rv = asprintf(&path, "%s%s", rdpath, "/cfg/volsrv.sif");
274 if (rv < 0) {
275 rc = ENOMEM;
276 goto error;
277 }
278
279 printf("Configuring volume server.\n");
280 rc = vol_volumes_create(path, &volumes);
281 if (rc != EOK) {
282 printf("Error creating volume server configuration.\n");
283 rc = EIO;
284 goto error;
285 }
286
287 printf("Configuring volume server: look up volume\n");
288 rc = vol_volume_lookup_ref(volumes, INST_VOL_LABEL, &volume);
289 if (rc != EOK) {
290 printf("Error creating volume server configuration.\n");
291 rc = EIO;
292 goto error;
293 }
294
295 printf("Configuring volume server: set mount point\n");
296 rc = vol_volume_set_mountp(volume, INST_VOL_MP);
297 if (rc != EOK) {
298 printf("Error creating system partition configuration.\n");
299 rc = EIO;
300 goto error;
301 }
302
303 printf("Configuring volume server: delete reference\n");
304 vol_volume_del_ref(volume);
305 volume = NULL;
306 printf("Configuring volume server: destroy volumes object\n");
307 vol_volumes_destroy(volumes);
308 volumes = NULL;
309
310 rc = rd_img_close(rd);
311 if (rc != EOK) {
312 printf("Error closing initial RAM disk image.\n");
313 rc = EIO;
314 goto error;
315 }
316
317 free(rdpath);
318 rdpath = NULL;
319 free(path);
320 path = NULL;
321
322 return EOK;
323error:
324 if (volume != NULL)
325 vol_volume_del_ref(volume);
326 if (volumes != NULL)
327 vol_volumes_destroy(volumes);
328 if (rd != NULL)
329 (void) rd_img_close(rd);
330 if (path != NULL)
331 free(path);
332 if (rdpath != NULL)
333 free(rdpath);
334 return rc;
335}
336
337/** Write unaligned 64-bit little-endian number.
338 *
339 * @param a Destination buffer
340 * @param data Number
341 */
342static void set_unaligned_u64le(uint8_t *a, uint64_t data)
343{
344 int i;
345
346 for (i = 0; i < 8; i++) {
347 a[i] = (data >> (i * 8)) & 0xff;
348 }
349}
350
351/** Copy boot blocks.
352 *
353 * Install Grub's boot blocks.
354 *
355 * @param devp Disk device
356 * @return EOK on success or an error code
357 */
358static errno_t sysinst_copy_boot_blocks(const char *devp)
359{
360 void *boot_img;
361 size_t boot_img_size;
362 void *core_img;
363 size_t core_img_size;
364 service_id_t sid;
365 size_t bsize;
366 uint8_t bbuf[512];
367 aoff64_t core_start;
368 aoff64_t core_blocks;
369 grub_boot_blocklist_t *first_bl, *bl;
370 errno_t rc;
371
372 printf("sysinst_copy_boot_blocks: Read boot block image.\n");
373 rc = futil_get_file(BOOT_FILES_SRC "/boot/grub/i386-pc/boot.img",
374 &boot_img, &boot_img_size);
375 if (rc != EOK || boot_img_size != 512)
376 return EIO;
377
378 printf("sysinst_copy_boot_blocks: Read GRUB core image.\n");
379 rc = futil_get_file(BOOT_FILES_SRC "/boot/grub/i386-pc/core.img",
380 &core_img, &core_img_size);
381 if (rc != EOK)
382 return EIO;
383
384 printf("sysinst_copy_boot_blocks: get service ID.\n");
385 rc = loc_service_get_id(devp, &sid, 0);
386 if (rc != EOK)
387 return rc;
388
389 printf("sysinst_copy_boot_blocks: block_init.\n");
390 rc = block_init(sid);
391 if (rc != EOK)
392 return rc;
393
394 printf("sysinst_copy_boot_blocks: get block size\n");
395 rc = block_get_bsize(sid, &bsize);
396 if (rc != EOK)
397 return rc;
398
399 if (bsize != 512) {
400 printf("Device block size != 512.\n");
401 return EIO;
402 }
403
404 printf("sysinst_copy_boot_blocks: read boot block\n");
405 rc = block_read_direct(sid, BOOT_BLOCK_IDX, 1, bbuf);
406 if (rc != EOK)
407 return EIO;
408
409 core_start = 16;
410 core_blocks = (core_img_size + 511) / 512;
411
412 /* Clean blocklists */
413 first_bl = core_img + 512 - sizeof(*first_bl);
414 bl = first_bl;
415 while (bl->len != 0) {
416 memset(bl, 0, sizeof(*bl));
417 --bl;
418 if ((void *)bl < core_img) {
419 printf("No block terminator in core image.\n");
420 return EIO;
421 }
422 }
423
424 first_bl->start = host2uint64_t_le(core_start + 1);
425 first_bl->len = host2uint16_t_le(core_blocks - 1);
426 first_bl->segment = grub_boot_i386_pc_kernel_seg + (512 >> 4);
427
428 /* Write boot code into boot block */
429 memcpy(bbuf, boot_img, 440); /* XXX 440 = sizeof(br_block_t.code_area) */
430 bbuf[grub_boot_machine_boot_drive] = 0xff;
431 set_unaligned_u64le(bbuf + grub_boot_machine_kernel_sector, core_start);
432
433 printf("sysinst_copy_boot_blocks: write boot block\n");
434 rc = block_write_direct(sid, BOOT_BLOCK_IDX, 1, bbuf);
435 if (rc != EOK)
436 return EIO;
437
438 printf("sysinst_copy_boot_blocks: write core blocks\n");
439 /* XXX Must pad last block with zeros */
440 rc = block_write_direct(sid, core_start, core_blocks, core_img);
441 if (rc != EOK)
442 return EIO;
443
444 printf("sysinst_copy_boot_blocks: OK.\n");
445 return EOK;
446}
447
448/** Eject installation volume.
449 *
450 * @param psvc_id Partition service ID
451 */
452static errno_t sysinst_eject_dev(service_id_t part_id)
453{
454 vol_t *vol = NULL;
455 errno_t rc;
456
457 rc = vol_create(&vol);
458 if (rc != EOK) {
459 printf("Error contacting volume service.\n");
460 goto out;
461 }
462
463 rc = vol_part_eject(vol, part_id);
464 if (rc != EOK) {
465 printf("Error ejecting volume.\n");
466 goto out;
467 }
468
469 rc = EOK;
470out:
471 vol_destroy(vol);
472 return rc;
473}
474
475/** Install system to a device.
476 *
477 * @param dev Device to install to.
478 * @return EOK on success or an error code
479 */
480static errno_t sysinst_install(const char *dev)
481{
482 errno_t rc;
483 service_id_t psvc_id;
484
485 rc = sysinst_label_dev(dev, &psvc_id);
486 if (rc != EOK)
487 return rc;
488
489 printf("FS created and mounted. Creating system directory structure.\n");
490 rc = sysinst_setup_sysvol();
491 if (rc != EOK)
492 return rc;
493
494 printf("Directories created. Copying boot files.\n");
495 rc = sysinst_copy_boot_files();
496 if (rc != EOK)
497 return rc;
498
499 printf("Boot files done. Configuring the system.\n");
500 rc = sysinst_customize_initrd();
501 if (rc != EOK)
502 return rc;
503
504 printf("Boot files done. Installing boot blocks.\n");
505 rc = sysinst_copy_boot_blocks(dev);
506 if (rc != EOK)
507 return rc;
508
509 printf("Ejecting device.\n");
510 rc = sysinst_eject_dev(psvc_id);
511 if (rc != EOK)
512 return rc;
513
514 return EOK;
515}
516
517int main(int argc, char *argv[])
518{
519 unsigned i;
520 errno_t rc;
521
522 i = 0;
523 while (default_devs[i] != NULL) {
524 rc = sysinst_check_dev(default_devs[i]);
525 if (rc == EOK)
526 break;
527 }
528
529 if (default_devs[i] == NULL) {
530 printf("Cannot determine installation device.\n");
531 return 1;
532 }
533
534 return sysinst_install(default_devs[i]);
535}
536
537/** @}
538 */
Note: See TracBrowser for help on using the repository browser.