Array of strings sys_dirs must be NULL-terminated
[helenos.git] / uspace / app / sysinst / sysinst.c
blob176a8cf22e180099175623d33d2f4de0c53f01e2
1 /*
2 * Copyright (c) 2024 Jiri Svoboda
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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.
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.
29 /** @addtogroup sysinst
30 * @{
32 /** @file System installer.
34 * Install the operating system onto a disk device. Note that this only works
35 * on ia32/amd64 with Grub platform 'pc'.
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>
51 #include "futil.h"
52 #include "grub.h"
53 #include "rdimg.h"
54 #include "volume.h"
56 /** Device to install to
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).
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"
72 #define MOUNT_POINT "/inst"
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
79 #define BOOT_FILES_SRC CD_MOUNT_POINT
80 #define BOOT_BLOCK_IDX 0 /* MBR */
82 static const char *default_devs[] = {
83 DEFAULT_DEV_0,
84 DEFAULT_DEV_1,
85 NULL
88 static const char *sys_dirs[] = {
89 "/cfg",
90 "/data",
91 NULL
94 /** Check the if the destination device exists.
96 * @param dev Disk device
98 * @return EOK on success or an error code
100 static errno_t sysinst_check_dev(const char *dev)
102 service_id_t sid;
103 errno_t rc;
105 rc = loc_service_get_id(dev, &sid, 0);
106 if (rc != EOK)
107 return rc;
109 (void)sid;
110 return EOK;
113 /** Label the destination device.
115 * @param dev Disk device to label
116 * @param psvc_id Place to store service ID of the created partition
118 * @return EOK on success or an error code
120 static errno_t sysinst_label_dev(const char *dev, service_id_t *psvc_id)
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;
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;
136 printf("sysinst_label_dev(): open device\n");
138 rc = fdisk_create(&fdisk);
139 if (rc != EOK) {
140 printf("Error initializing fdisk.\n");
141 return rc;
144 rc = fdisk_dev_open(fdisk, sid, &fdev);
145 if (rc != EOK) {
146 printf("Error opening device.\n");
147 return rc;
150 printf("sysinst_label_dev(): create mount directory\n");
152 rc = vfs_link_path(MOUNT_POINT, KIND_DIRECTORY, NULL);
153 if (rc != EOK)
154 return rc;
156 printf("sysinst_label_dev(): create label\n");
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;
164 printf("sysinst_label_dev(): create partition\n");
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;
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;
179 rc = fdisk_part_create(fdev, &pspec, &part);
180 if (rc != EOK) {
181 printf("Error creating partition.\n");
182 return rc;
185 rc = fdisk_part_get_info(part, &pinfo);
186 if (rc != EOK) {
187 printf("Error getting partition information.\n");
188 return rc;
191 printf("sysinst_label_dev(): OK\n");
192 *psvc_id = pinfo.svc_id;
193 return EOK;
196 /** Set up system volume structure.
198 * @return EOK on success or an error code
200 static errno_t sysinst_setup_sysvol(void)
202 errno_t rc;
203 char *path = NULL;
204 const char **cp;
205 int rv;
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;
215 rc = vfs_link_path(path, KIND_DIRECTORY, NULL);
216 if (rc != EOK) {
217 printf("Error creating directory '%s'.\n", path);
218 goto error;
221 free(path);
222 path = NULL;
223 ++cp;
226 free(path);
227 path = NULL;
229 return EOK;
230 error:
231 if (path != NULL)
232 free(path);
233 return rc;
236 /** Copy boot files.
238 * @return EOK on success or an error code
240 static errno_t sysinst_copy_boot_files(void)
242 errno_t rc;
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;
249 printf("sysinst_copy_boot_files(): OK\n");
250 return EOK;
253 /** Set up configuration in the initial RAM disk.
255 * @return EOK on success or an error code
257 static errno_t sysinst_customize_initrd(void)
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;
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;
273 rv = asprintf(&path, "%s%s", rdpath, "/cfg/volsrv.sif");
274 if (rv < 0) {
275 rc = ENOMEM;
276 goto error;
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;
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;
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;
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;
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;
317 free(rdpath);
318 rdpath = NULL;
319 free(path);
320 path = NULL;
322 return EOK;
323 error:
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;
337 /** Write unaligned 64-bit little-endian number.
339 * @param a Destination buffer
340 * @param data Number
342 static void set_unaligned_u64le(uint8_t *a, uint64_t data)
344 int i;
346 for (i = 0; i < 8; i++) {
347 a[i] = (data >> (i * 8)) & 0xff;
351 /** Copy boot blocks.
353 * Install Grub's boot blocks.
355 * @param devp Disk device
356 * @return EOK on success or an error code
358 static errno_t sysinst_copy_boot_blocks(const char *devp)
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;
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;
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;
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;
389 printf("sysinst_copy_boot_blocks: block_init.\n");
390 rc = block_init(sid);
391 if (rc != EOK)
392 return rc;
394 printf("sysinst_copy_boot_blocks: get block size\n");
395 rc = block_get_bsize(sid, &bsize);
396 if (rc != EOK)
397 return rc;
399 if (bsize != 512) {
400 printf("Device block size != 512.\n");
401 return EIO;
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;
409 core_start = 16;
410 core_blocks = (core_img_size + 511) / 512;
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;
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);
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);
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;
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;
444 printf("sysinst_copy_boot_blocks: OK.\n");
445 return EOK;
448 /** Eject installation volume.
450 * @param psvc_id Partition service ID
452 static errno_t sysinst_eject_dev(service_id_t part_id)
454 vol_t *vol = NULL;
455 errno_t rc;
457 rc = vol_create(&vol);
458 if (rc != EOK) {
459 printf("Error contacting volume service.\n");
460 goto out;
463 rc = vol_part_eject(vol, part_id);
464 if (rc != EOK) {
465 printf("Error ejecting volume.\n");
466 goto out;
469 rc = EOK;
470 out:
471 vol_destroy(vol);
472 return rc;
475 /** Install system to a device.
477 * @param dev Device to install to.
478 * @return EOK on success or an error code
480 static errno_t sysinst_install(const char *dev)
482 errno_t rc;
483 service_id_t psvc_id;
485 rc = sysinst_label_dev(dev, &psvc_id);
486 if (rc != EOK)
487 return rc;
489 printf("FS created and mounted. Creating system directory structure.\n");
490 rc = sysinst_setup_sysvol();
491 if (rc != EOK)
492 return rc;
494 printf("Directories created. Copying boot files.\n");
495 rc = sysinst_copy_boot_files();
496 if (rc != EOK)
497 return rc;
499 printf("Boot files done. Configuring the system.\n");
500 rc = sysinst_customize_initrd();
501 if (rc != EOK)
502 return rc;
504 printf("Boot files done. Installing boot blocks.\n");
505 rc = sysinst_copy_boot_blocks(dev);
506 if (rc != EOK)
507 return rc;
509 printf("Ejecting device.\n");
510 rc = sysinst_eject_dev(psvc_id);
511 if (rc != EOK)
512 return rc;
514 return EOK;
517 int main(int argc, char *argv[])
519 unsigned i;
520 errno_t rc;
522 i = 0;
523 while (default_devs[i] != NULL) {
524 rc = sysinst_check_dev(default_devs[i]);
525 if (rc == EOK)
526 break;
529 if (default_devs[i] == NULL) {
530 printf("Cannot determine installation device.\n");
531 return 1;
534 return sysinst_install(default_devs[i]);
537 /** @}