Rename capacity-related 'cap' to 'capa'
[helenos.git] / uspace / app / sysinst / sysinst.c
blob9aca27ed6f9c605c15cf5a8481478997891a296e
1 /*
2 * Copyright (c) 2018 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 "devices/\\hw\\sys\\00:01.0\\ata-c1\\d0"
65 //#define DEFAULT_DEV "devices/\\hw\\pci0\\00:01.2\\uhci_rh\\usb01_a1\\mass-storage0\\l0"
66 /** Volume label for the new file system */
67 #define INST_VOL_LABEL "HelenOS"
68 /** Mount point of system partition when running installed system */
69 #define INST_VOL_MP "/w"
71 #define MOUNT_POINT "/inst"
73 /** HelenOS live CD volume label */
74 #define CD_VOL_LABEL "HelenOS-CD"
75 /** XXX Should get this from the volume server */
76 #define CD_MOUNT_POINT "/vol/" CD_VOL_LABEL
78 #define BOOT_FILES_SRC CD_MOUNT_POINT
79 #define BOOT_BLOCK_IDX 0 /* MBR */
81 static const char *sys_dirs[] = {
82 "/cfg",
83 "/data"
86 /** Label the destination device.
88 * @param dev Disk device to label
89 * @param psvc_id Place to store service ID of the created partition
91 * @return EOK on success or an error code
93 static errno_t sysinst_label_dev(const char *dev, service_id_t *psvc_id)
95 fdisk_t *fdisk;
96 fdisk_dev_t *fdev;
97 fdisk_part_t *part;
98 fdisk_part_spec_t pspec;
99 fdisk_part_info_t pinfo;
100 capa_spec_t capa;
101 service_id_t sid;
102 errno_t rc;
104 printf("sysinst_label_dev(): get service ID '%s'\n", dev);
105 rc = loc_service_get_id(dev, &sid, 0);
106 if (rc != EOK)
107 return rc;
109 printf("sysinst_label_dev(): open device\n");
111 rc = fdisk_create(&fdisk);
112 if (rc != EOK) {
113 printf("Error initializing fdisk.\n");
114 return rc;
117 rc = fdisk_dev_open(fdisk, sid, &fdev);
118 if (rc != EOK) {
119 printf("Error opening device.\n");
120 return rc;
123 printf("sysinst_label_dev(): create mount directory\n");
125 rc = vfs_link_path(MOUNT_POINT, KIND_DIRECTORY, NULL);
126 if (rc != EOK)
127 return rc;
129 printf("sysinst_label_dev(): create label\n");
131 rc = fdisk_label_create(fdev, lt_mbr);
132 if (rc != EOK) {
133 printf("Error creating label: %s.\n", str_error(rc));
134 return rc;
137 printf("sysinst_label_dev(): create partition\n");
139 rc = fdisk_part_get_max_avail(fdev, spc_pri, &capa);
140 if (rc != EOK) {
141 printf("Error getting available capacity: %s.\n", str_error(rc));
142 return rc;
145 fdisk_pspec_init(&pspec);
146 pspec.capacity = capa;
147 pspec.pkind = lpk_primary;
148 pspec.fstype = fs_ext4; /* Cannot be changed without modifying core.img */
149 pspec.mountp = MOUNT_POINT;
150 pspec.label = INST_VOL_LABEL;
152 rc = fdisk_part_create(fdev, &pspec, &part);
153 if (rc != EOK) {
154 printf("Error creating partition.\n");
155 return rc;
158 rc = fdisk_part_get_info(part, &pinfo);
159 if (rc != EOK) {
160 printf("Error getting partition information.\n");
161 return rc;
164 printf("sysinst_label_dev(): OK\n");
165 *psvc_id = pinfo.svc_id;
166 return EOK;
169 /** Set up system volume structure.
171 * @return EOK on success or an error code
173 static errno_t sysinst_setup_sysvol(void)
175 errno_t rc;
176 char *path = NULL;
177 const char **cp;
178 int rv;
180 cp = sys_dirs;
181 while (*cp != NULL) {
182 rv = asprintf(&path, "%s%s", MOUNT_POINT, *cp);
183 if (rv < 0) {
184 rc = ENOMEM;
185 goto error;
188 rc = vfs_link_path(path, KIND_DIRECTORY, NULL);
189 if (rc != EOK) {
190 printf("Error creating directory '%s'.\n", path);
191 goto error;
194 free(path);
195 path = NULL;
196 ++cp;
199 free(path);
200 path = NULL;
202 return EOK;
203 error:
204 if (path != NULL)
205 free(path);
206 return rc;
209 /** Copy boot files.
211 * @return EOK on success or an error code
213 static errno_t sysinst_copy_boot_files(void)
215 errno_t rc;
217 printf("sysinst_copy_boot_files(): copy bootloader files\n");
218 rc = futil_rcopy_contents(BOOT_FILES_SRC, MOUNT_POINT);
219 if (rc != EOK)
220 return rc;
222 printf("sysinst_copy_boot_files(): OK\n");
223 return EOK;
226 /** Set up configuration in the initial RAM disk.
228 * @return EOK on success or an error code
230 static errno_t sysinst_customize_initrd(void)
232 errno_t rc;
233 rd_img_t *rd = NULL;
234 char *rdpath = NULL;
235 char *path = NULL;
236 vol_volumes_t *volumes = NULL;
237 vol_volume_t *volume = NULL;
238 int rv;
240 rc = rd_img_open(MOUNT_POINT "/boot/initrd.img", &rdpath, &rd);
241 if (rc != EOK) {
242 printf("Error opening initial RAM disk image.\n");
243 goto error;
246 rv = asprintf(&path, "%s%s", rdpath, "/cfg/volsrv.sif");
247 if (rv < 0) {
248 rc = ENOMEM;
249 goto error;
252 printf("Configuring volume server.\n");
253 rc = vol_volumes_create(path, &volumes);
254 if (rc != EOK) {
255 printf("Error creating volume server configuration.\n");
256 rc = EIO;
257 goto error;
260 printf("Configuring volume server: look up volume\n");
261 rc = vol_volume_lookup_ref(volumes, INST_VOL_LABEL, &volume);
262 if (rc != EOK) {
263 printf("Error creating volume server configuration.\n");
264 rc = EIO;
265 goto error;
268 printf("Configuring volume server: set mount point\n");
269 rc = vol_volume_set_mountp(volume, INST_VOL_MP);
270 if (rc != EOK) {
271 printf("Error creating system partition configuration.\n");
272 rc = EIO;
273 goto error;
276 printf("Configuring volume server: delete reference\n");
277 vol_volume_del_ref(volume);
278 volume = NULL;
279 printf("Configuring volume server: destroy volumes object\n");
280 vol_volumes_destroy(volumes);
281 volumes = NULL;
283 rc = rd_img_close(rd);
284 if (rc != EOK) {
285 printf("Error closing initial RAM disk image.\n");
286 rc = EIO;
287 goto error;
290 free(rdpath);
291 rdpath = NULL;
292 free(path);
293 path = NULL;
295 return EOK;
296 error:
297 if (volume != NULL)
298 vol_volume_del_ref(volume);
299 if (volumes != NULL)
300 vol_volumes_destroy(volumes);
301 if (rd != NULL)
302 (void) rd_img_close(rd);
303 if (path != NULL)
304 free(path);
305 if (rdpath != NULL)
306 free(rdpath);
307 return rc;
310 /** Write unaligned 64-bit little-endian number.
312 * @param a Destination buffer
313 * @param data Number
315 static void set_unaligned_u64le(uint8_t *a, uint64_t data)
317 int i;
319 for (i = 0; i < 8; i++) {
320 a[i] = (data >> (i * 8)) & 0xff;
324 /** Copy boot blocks.
326 * Install Grub's boot blocks.
328 * @param devp Disk device
329 * @return EOK on success or an error code
331 static errno_t sysinst_copy_boot_blocks(const char *devp)
333 void *boot_img;
334 size_t boot_img_size;
335 void *core_img;
336 size_t core_img_size;
337 service_id_t sid;
338 size_t bsize;
339 uint8_t bbuf[512];
340 aoff64_t core_start;
341 aoff64_t core_blocks;
342 grub_boot_blocklist_t *first_bl, *bl;
343 errno_t rc;
345 printf("sysinst_copy_boot_blocks: Read boot block image.\n");
346 rc = futil_get_file(BOOT_FILES_SRC "/boot/grub/i386-pc/boot.img",
347 &boot_img, &boot_img_size);
348 if (rc != EOK || boot_img_size != 512)
349 return EIO;
351 printf("sysinst_copy_boot_blocks: Read GRUB core image.\n");
352 rc = futil_get_file(BOOT_FILES_SRC "/boot/grub/i386-pc/core.img",
353 &core_img, &core_img_size);
354 if (rc != EOK)
355 return EIO;
357 printf("sysinst_copy_boot_blocks: get service ID.\n");
358 rc = loc_service_get_id(devp, &sid, 0);
359 if (rc != EOK)
360 return rc;
362 printf("sysinst_copy_boot_blocks: block_init.\n");
363 rc = block_init(sid, 512);
364 if (rc != EOK)
365 return rc;
367 printf("sysinst_copy_boot_blocks: get block size\n");
368 rc = block_get_bsize(sid, &bsize);
369 if (rc != EOK)
370 return rc;
372 if (bsize != 512) {
373 printf("Device block size != 512.\n");
374 return EIO;
377 printf("sysinst_copy_boot_blocks: read boot block\n");
378 rc = block_read_direct(sid, BOOT_BLOCK_IDX, 1, bbuf);
379 if (rc != EOK)
380 return EIO;
382 core_start = 16;
383 core_blocks = (core_img_size + 511) / 512;
385 /* Clean blocklists */
386 first_bl = core_img + 512 - sizeof(*first_bl);
387 bl = first_bl;
388 while (bl->len != 0) {
389 memset(bl, 0, sizeof(*bl));
390 --bl;
391 if ((void *)bl < core_img) {
392 printf("No block terminator in core image.\n");
393 return EIO;
397 first_bl->start = host2uint64_t_le(core_start + 1);
398 first_bl->len = host2uint16_t_le(core_blocks - 1);
399 first_bl->segment = grub_boot_i386_pc_kernel_seg + (512 >> 4);
401 /* Write boot code into boot block */
402 memcpy(bbuf, boot_img, 440); /* XXX 440 = sizeof(br_block_t.code_area) */
403 bbuf[grub_boot_machine_boot_drive] = 0xff;
404 set_unaligned_u64le(bbuf + grub_boot_machine_kernel_sector, core_start);
406 printf("sysinst_copy_boot_blocks: write boot block\n");
407 rc = block_write_direct(sid, BOOT_BLOCK_IDX, 1, bbuf);
408 if (rc != EOK)
409 return EIO;
411 printf("sysinst_copy_boot_blocks: write core blocks\n");
412 /* XXX Must pad last block with zeros */
413 rc = block_write_direct(sid, core_start, core_blocks, core_img);
414 if (rc != EOK)
415 return EIO;
417 printf("sysinst_copy_boot_blocks: OK.\n");
418 return EOK;
421 /** Eject installation volume.
423 * @param psvc_id Partition service ID
425 static errno_t sysinst_eject_dev(service_id_t part_id)
427 vol_t *vol = NULL;
428 errno_t rc;
430 rc = vol_create(&vol);
431 if (rc != EOK) {
432 printf("Error contacting volume service.\n");
433 goto out;
436 rc = vol_part_eject(vol, part_id);
437 if (rc != EOK) {
438 printf("Error ejecting volume.\n");
439 goto out;
442 rc = EOK;
443 out:
444 vol_destroy(vol);
445 return rc;
448 /** Install system to a device.
450 * @param dev Device to install to.
451 * @return EOK on success or an error code
453 static errno_t sysinst_install(const char *dev)
455 errno_t rc;
456 service_id_t psvc_id;
458 rc = sysinst_label_dev(dev, &psvc_id);
459 if (rc != EOK)
460 return rc;
462 printf("FS created and mounted. Creating system directory structure.\n");
463 rc = sysinst_setup_sysvol();
464 if (rc != EOK)
465 return rc;
467 printf("Directories created. Copying boot files.\n");
468 rc = sysinst_copy_boot_files();
469 if (rc != EOK)
470 return rc;
472 printf("Boot files done. Configuring the system.\n");
473 rc = sysinst_customize_initrd();
474 if (rc != EOK)
475 return rc;
477 printf("Boot files done. Installing boot blocks.\n");
478 rc = sysinst_copy_boot_blocks(dev);
479 if (rc != EOK)
480 return rc;
482 printf("Ejecting device.\n");
483 rc = sysinst_eject_dev(psvc_id);
484 if (rc != EOK)
485 return rc;
487 return EOK;
490 int main(int argc, char *argv[])
492 const char *dev = DEFAULT_DEV;
493 return sysinst_install(dev);
496 /** @}