source: mainline/uspace/app/sysinst/sysinst.c@ 597fa24

Last change on this file since 597fa24 was 90ee3384, checked in by Jiri Svoboda <jiri@…>, 5 months ago

Allow sysinst to restart the system at the end of installation.

When -r option is passed.

  • Property mode set to 100644
File size: 14.7 KB
Line 
1/*
2 * Copyright (c) 2025 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 <futil.h>
44#include <loc.h>
45#include <stdio.h>
46#include <stdlib.h>
47#include <str.h>
48#include <str_error.h>
49#include <system.h>
50#include <vfs/vfs.h>
51#include <vol.h>
52
53#include "grub.h"
54#include "rdimg.h"
55#include "volume.h"
56
57/** Device to install to
58 *
59 * Note that you cannot simply change this, because the installation
60 * device is hardcoded in core.img. If you wanted to install to another
61 * device, you must build your own core.img (e.g. using tools/grub/mkimage.sh
62 * and modifying tools/grub/load.cfg, supplying the device to boot from
63 * in Grub notation).
64 */
65#define DEFAULT_DEV_0 "devices/\\hw\\sys\\00:01.1\\c0d0"
66#define DEFAULT_DEV_1 "devices/\\hw\\sys\\00:01.0\\ide1\\c0d0"
67//#define DEFAULT_DEV "devices/\\hw\\pci0\\00:01.2\\uhci_rh\\usb01_a1\\mass-storage0\\l0"
68/** Volume label for the new file system */
69#define INST_VOL_LABEL "HelenOS"
70/** Mount point of system partition when running installed system */
71#define INST_VOL_MP "/w"
72
73#define MOUNT_POINT "/inst"
74
75/** HelenOS live CD volume label */
76#define CD_VOL_LABEL "HelenOS-CD"
77/** XXX Should get this from the volume server */
78#define CD_MOUNT_POINT "/vol/" CD_VOL_LABEL
79
80#define BOOT_FILES_SRC CD_MOUNT_POINT
81#define BOOT_BLOCK_IDX 0 /* MBR */
82
83#define CFG_FILES_SRC "/cfg"
84#define CFG_FILES_DEST MOUNT_POINT "/cfg"
85
86static const char *default_devs[] = {
87 DEFAULT_DEV_0,
88 DEFAULT_DEV_1,
89 NULL
90};
91
92static const char *sys_dirs[] = {
93 "/cfg",
94 "/data",
95 NULL
96};
97
98static bool restart = false;
99
100static fibril_mutex_t shutdown_lock;
101static fibril_condvar_t shutdown_cv;
102static bool shutdown_stopped;
103static bool shutdown_failed;
104
105static void sysinst_shutdown_complete(void *);
106static void sysinst_shutdown_failed(void *);
107
108static system_cb_t sysinst_system_cb = {
109 .shutdown_complete = sysinst_shutdown_complete,
110 .shutdown_failed = sysinst_shutdown_failed
111};
112
113/** System shutdown complete.
114 *
115 * @param arg Argument (shutdown_t *)
116 */
117static void sysinst_shutdown_complete(void *arg)
118{
119 (void)arg;
120
121 fibril_mutex_lock(&shutdown_lock);
122 shutdown_stopped = true;
123 shutdown_failed = false;
124 fibril_condvar_broadcast(&shutdown_cv);
125 fibril_mutex_unlock(&shutdown_lock);
126}
127
128/** System shutdown failed.
129 *
130 * @param arg Argument (not used)
131 */
132static void sysinst_shutdown_failed(void *arg)
133{
134 (void)arg;
135
136 fibril_mutex_lock(&shutdown_lock);
137 shutdown_stopped = true;
138 shutdown_failed = true;
139 fibril_condvar_broadcast(&shutdown_cv);
140 fibril_mutex_unlock(&shutdown_lock);
141}
142
143/** Check the if the destination device exists.
144 *
145 * @param dev Disk device
146 *
147 * @return EOK on success or an error code
148 */
149static errno_t sysinst_check_dev(const char *dev)
150{
151 service_id_t sid;
152 errno_t rc;
153
154 rc = loc_service_get_id(dev, &sid, 0);
155 if (rc != EOK)
156 return rc;
157
158 (void)sid;
159 return EOK;
160}
161
162/** Label the destination device.
163 *
164 * @param dev Disk device to label
165 * @param psvc_id Place to store service ID of the created partition
166 *
167 * @return EOK on success or an error code
168 */
169static errno_t sysinst_label_dev(const char *dev, service_id_t *psvc_id)
170{
171 fdisk_t *fdisk;
172 fdisk_dev_t *fdev;
173 fdisk_part_t *part;
174 fdisk_part_spec_t pspec;
175 fdisk_part_info_t pinfo;
176 capa_spec_t capa;
177 service_id_t sid;
178 errno_t rc;
179
180 printf("sysinst_label_dev(): get service ID '%s'\n", dev);
181 rc = loc_service_get_id(dev, &sid, 0);
182 if (rc != EOK)
183 return rc;
184
185 printf("sysinst_label_dev(): open device\n");
186
187 rc = fdisk_create(&fdisk);
188 if (rc != EOK) {
189 printf("Error initializing fdisk.\n");
190 return rc;
191 }
192
193 rc = fdisk_dev_open(fdisk, sid, &fdev);
194 if (rc != EOK) {
195 printf("Error opening device.\n");
196 return rc;
197 }
198
199 printf("sysinst_label_dev(): create mount directory\n");
200
201 rc = vfs_link_path(MOUNT_POINT, KIND_DIRECTORY, NULL);
202 if (rc != EOK)
203 return rc;
204
205 printf("sysinst_label_dev(): create label\n");
206
207 rc = fdisk_label_create(fdev, lt_mbr);
208 if (rc != EOK) {
209 printf("Error creating label: %s.\n", str_error(rc));
210 return rc;
211 }
212
213 printf("sysinst_label_dev(): create partition\n");
214
215 rc = fdisk_part_get_max_avail(fdev, spc_pri, &capa);
216 if (rc != EOK) {
217 printf("Error getting available capacity: %s.\n", str_error(rc));
218 return rc;
219 }
220
221 fdisk_pspec_init(&pspec);
222 pspec.capacity = capa;
223 pspec.pkind = lpk_primary;
224 pspec.fstype = fs_ext4; /* Cannot be changed without modifying core.img */
225 pspec.mountp = MOUNT_POINT;
226 pspec.label = INST_VOL_LABEL;
227
228 rc = fdisk_part_create(fdev, &pspec, &part);
229 if (rc != EOK) {
230 printf("Error creating partition.\n");
231 return rc;
232 }
233
234 rc = fdisk_part_get_info(part, &pinfo);
235 if (rc != EOK) {
236 printf("Error getting partition information.\n");
237 return rc;
238 }
239
240 printf("sysinst_label_dev(): OK\n");
241 *psvc_id = pinfo.svc_id;
242 return EOK;
243}
244
245/** Set up system volume structure.
246 *
247 * @return EOK on success or an error code
248 */
249static errno_t sysinst_setup_sysvol(void)
250{
251 errno_t rc;
252 char *path = NULL;
253 const char **cp;
254 int rv;
255
256 cp = sys_dirs;
257 while (*cp != NULL) {
258 rv = asprintf(&path, "%s%s", MOUNT_POINT, *cp);
259 if (rv < 0) {
260 rc = ENOMEM;
261 goto error;
262 }
263
264 rc = vfs_link_path(path, KIND_DIRECTORY, NULL);
265 if (rc != EOK) {
266 printf("Error creating directory '%s'.\n", path);
267 goto error;
268 }
269
270 free(path);
271 path = NULL;
272 ++cp;
273 }
274
275 free(path);
276 path = NULL;
277
278 /* Copy initial configuration files */
279 rc = futil_rcopy_contents(CFG_FILES_SRC, CFG_FILES_DEST);
280 if (rc != EOK)
281 return rc;
282
283 return EOK;
284error:
285 if (path != NULL)
286 free(path);
287 return rc;
288}
289
290/** Copy boot files.
291 *
292 * @return EOK on success or an error code
293 */
294static errno_t sysinst_copy_boot_files(void)
295{
296 errno_t rc;
297
298 printf("sysinst_copy_boot_files(): copy bootloader files\n");
299 rc = futil_rcopy_contents(BOOT_FILES_SRC, MOUNT_POINT);
300 if (rc != EOK)
301 return rc;
302
303 printf("sysinst_copy_boot_files(): OK\n");
304 return EOK;
305}
306
307/** Set up configuration in the initial RAM disk.
308 *
309 * @return EOK on success or an error code
310 */
311static errno_t sysinst_customize_initrd(void)
312{
313 errno_t rc;
314 rd_img_t *rd = NULL;
315 char *rdpath = NULL;
316 char *path = NULL;
317 vol_volumes_t *volumes = NULL;
318 vol_volume_t *volume = NULL;
319 int rv;
320
321 rc = rd_img_open(MOUNT_POINT "/boot/initrd.img", &rdpath, &rd);
322 if (rc != EOK) {
323 printf("Error opening initial RAM disk image.\n");
324 goto error;
325 }
326
327 rv = asprintf(&path, "%s%s", rdpath, "/cfg/initvol.sif");
328 if (rv < 0) {
329 rc = ENOMEM;
330 goto error;
331 }
332
333 printf("Configuring volume server.\n");
334 rc = vol_volumes_create(path, &volumes);
335 if (rc != EOK) {
336 printf("Error creating volume server configuration.\n");
337 rc = EIO;
338 goto error;
339 }
340
341 printf("Configuring volume server: look up volume\n");
342 rc = vol_volume_lookup_ref(volumes, INST_VOL_LABEL, &volume);
343 if (rc != EOK) {
344 printf("Error creating volume server configuration.\n");
345 rc = EIO;
346 goto error;
347 }
348
349 printf("Configuring volume server: set mount point\n");
350 rc = vol_volume_set_mountp(volume, INST_VOL_MP);
351 if (rc != EOK) {
352 printf("Error creating system partition configuration.\n");
353 rc = EIO;
354 goto error;
355 }
356
357 rc = vol_volumes_sync(volumes);
358 if (rc != EOK) {
359 printf("Error saving volume confiuration.\n");
360 goto error;
361 }
362
363 printf("Configuring volume server: delete reference\n");
364 vol_volume_del_ref(volume);
365 volume = NULL;
366 printf("Configuring volume server: destroy volumes object\n");
367 vol_volumes_destroy(volumes);
368 volumes = NULL;
369
370 rc = rd_img_close(rd);
371 if (rc != EOK) {
372 printf("Error closing initial RAM disk image.\n");
373 rc = EIO;
374 goto error;
375 }
376
377 free(rdpath);
378 rdpath = NULL;
379 free(path);
380 path = NULL;
381
382 return EOK;
383error:
384 if (volume != NULL)
385 vol_volume_del_ref(volume);
386 if (volumes != NULL)
387 vol_volumes_destroy(volumes);
388 if (rd != NULL)
389 (void) rd_img_close(rd);
390 if (path != NULL)
391 free(path);
392 if (rdpath != NULL)
393 free(rdpath);
394 return rc;
395}
396
397/** Write unaligned 64-bit little-endian number.
398 *
399 * @param a Destination buffer
400 * @param data Number
401 */
402static void set_unaligned_u64le(uint8_t *a, uint64_t data)
403{
404 int i;
405
406 for (i = 0; i < 8; i++) {
407 a[i] = (data >> (i * 8)) & 0xff;
408 }
409}
410
411/** Copy boot blocks.
412 *
413 * Install Grub's boot blocks.
414 *
415 * @param devp Disk device
416 * @return EOK on success or an error code
417 */
418static errno_t sysinst_copy_boot_blocks(const char *devp)
419{
420 void *boot_img;
421 size_t boot_img_size;
422 void *core_img;
423 size_t core_img_size;
424 service_id_t sid;
425 size_t bsize;
426 uint8_t bbuf[512];
427 aoff64_t core_start;
428 aoff64_t core_blocks;
429 grub_boot_blocklist_t *first_bl, *bl;
430 errno_t rc;
431
432 printf("sysinst_copy_boot_blocks: Read boot block image.\n");
433 rc = futil_get_file(BOOT_FILES_SRC "/boot/grub/i386-pc/boot.img",
434 &boot_img, &boot_img_size);
435 if (rc != EOK || boot_img_size != 512)
436 return EIO;
437
438 printf("sysinst_copy_boot_blocks: Read GRUB core image.\n");
439 rc = futil_get_file(BOOT_FILES_SRC "/boot/grub/i386-pc/core.img",
440 &core_img, &core_img_size);
441 if (rc != EOK)
442 return EIO;
443
444 printf("sysinst_copy_boot_blocks: get service ID.\n");
445 rc = loc_service_get_id(devp, &sid, 0);
446 if (rc != EOK)
447 return rc;
448
449 printf("sysinst_copy_boot_blocks: block_init.\n");
450 rc = block_init(sid);
451 if (rc != EOK)
452 return rc;
453
454 printf("sysinst_copy_boot_blocks: get block size\n");
455 rc = block_get_bsize(sid, &bsize);
456 if (rc != EOK)
457 return rc;
458
459 if (bsize != 512) {
460 printf("Device block size != 512.\n");
461 return EIO;
462 }
463
464 printf("sysinst_copy_boot_blocks: read boot block\n");
465 rc = block_read_direct(sid, BOOT_BLOCK_IDX, 1, bbuf);
466 if (rc != EOK)
467 return EIO;
468
469 core_start = 16;
470 core_blocks = (core_img_size + 511) / 512;
471
472 /* Clean blocklists */
473 first_bl = core_img + 512 - sizeof(*first_bl);
474 bl = first_bl;
475 while (bl->len != 0) {
476 memset(bl, 0, sizeof(*bl));
477 --bl;
478 if ((void *)bl < core_img) {
479 printf("No block terminator in core image.\n");
480 return EIO;
481 }
482 }
483
484 first_bl->start = host2uint64_t_le(core_start + 1);
485 first_bl->len = host2uint16_t_le(core_blocks - 1);
486 first_bl->segment = grub_boot_i386_pc_kernel_seg + (512 >> 4);
487
488 /* Write boot code into boot block */
489 memcpy(bbuf, boot_img, 440); /* XXX 440 = sizeof(br_block_t.code_area) */
490 bbuf[grub_boot_machine_boot_drive] = 0xff;
491 set_unaligned_u64le(bbuf + grub_boot_machine_kernel_sector, core_start);
492
493 printf("sysinst_copy_boot_blocks: write boot block\n");
494 rc = block_write_direct(sid, BOOT_BLOCK_IDX, 1, bbuf);
495 if (rc != EOK)
496 return EIO;
497
498 printf("sysinst_copy_boot_blocks: write core blocks\n");
499 /* XXX Must pad last block with zeros */
500 rc = block_write_direct(sid, core_start, core_blocks, core_img);
501 if (rc != EOK)
502 return EIO;
503
504 printf("sysinst_copy_boot_blocks: OK.\n");
505 return EOK;
506}
507
508/** Eject installation volume.
509 *
510 * @param psvc_id Partition service ID
511 */
512static errno_t sysinst_eject_dev(service_id_t part_id)
513{
514 vol_t *vol = NULL;
515 errno_t rc;
516
517 rc = vol_create(&vol);
518 if (rc != EOK) {
519 printf("Error contacting volume service.\n");
520 goto out;
521 }
522
523 rc = vol_part_eject(vol, part_id, vef_physical);
524 if (rc != EOK) {
525 printf("Error ejecting volume.\n");
526 goto out;
527 }
528
529 rc = EOK;
530out:
531 vol_destroy(vol);
532 return rc;
533}
534
535/** Restart the system.
536 *
537 * @return EOK on success or an error code
538 */
539static errno_t sysinst_restart(void)
540{
541 errno_t rc;
542 system_t *system;
543
544 fibril_mutex_initialize(&shutdown_lock);
545 fibril_condvar_initialize(&shutdown_cv);
546 shutdown_stopped = false;
547 shutdown_failed = false;
548
549 rc = system_open(SYSTEM_DEFAULT, &sysinst_system_cb, NULL, &system);
550 if (rc != EOK) {
551 printf("Failed opening system control service.\n");
552 return rc;
553 }
554
555 rc = system_restart(system);
556 if (rc != EOK) {
557 system_close(system);
558 printf("Failed requesting system restart.\n");
559 return rc;
560 }
561
562 fibril_mutex_lock(&shutdown_lock);
563 printf("The system is shutting down...\n");
564 while (!shutdown_stopped)
565 fibril_condvar_wait(&shutdown_cv, &shutdown_lock);
566
567 if (shutdown_failed) {
568 printf("Shutdown failed.\n");
569 system_close(system);
570 return rc;
571 }
572
573 printf("Shutdown complete. It is now safe to remove power.\n");
574
575 /* Sleep forever */
576 while (true)
577 fibril_condvar_wait(&shutdown_cv, &shutdown_lock);
578
579 fibril_mutex_unlock(&shutdown_lock);
580
581 system_close(system);
582 return 0;
583
584}
585
586/** Install system to a device.
587 *
588 * @param dev Device to install to.
589 * @return EOK on success or an error code
590 */
591static errno_t sysinst_install(const char *dev)
592{
593 errno_t rc;
594 service_id_t psvc_id;
595
596 rc = sysinst_label_dev(dev, &psvc_id);
597 if (rc != EOK)
598 return rc;
599
600 printf("FS created and mounted. Creating system directory structure.\n");
601 rc = sysinst_setup_sysvol();
602 if (rc != EOK)
603 return rc;
604
605 printf("Directories created. Copying boot files.\n");
606 rc = sysinst_copy_boot_files();
607 if (rc != EOK)
608 return rc;
609
610 printf("Boot files done. Configuring the system.\n");
611 rc = sysinst_customize_initrd();
612 if (rc != EOK)
613 return rc;
614
615 printf("Boot files done. Installing boot blocks.\n");
616 rc = sysinst_copy_boot_blocks(dev);
617 if (rc != EOK)
618 return rc;
619
620 printf("Ejecting device.\n");
621 rc = sysinst_eject_dev(psvc_id);
622 if (rc != EOK)
623 return rc;
624
625 rc = sysinst_restart();
626 if (rc != EOK)
627 return rc;
628
629 return EOK;
630}
631
632int main(int argc, char *argv[])
633{
634 unsigned i;
635 errno_t rc;
636
637 if (argc > 1 && str_cmp(argv[1], "-r") == 0)
638 restart = true;
639
640 i = 0;
641 while (default_devs[i] != NULL) {
642 rc = sysinst_check_dev(default_devs[i]);
643 if (rc == EOK)
644 break;
645 ++i;
646 }
647
648 if (default_devs[i] == NULL) {
649 printf("Cannot determine installation device.\n");
650 return 1;
651 }
652
653 return sysinst_install(default_devs[i]);
654}
655
656/** @}
657 */
Note: See TracBrowser for help on using the repository browser.