2 * Copyright (c) 2018 Jiri Svoboda
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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
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'.
39 #include <byteorder.h>
47 #include <str_error.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
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
[] = {
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
)
98 fdisk_part_spec_t pspec
;
99 fdisk_part_info_t pinfo
;
104 printf("sysinst_label_dev(): get service ID '%s'\n", dev
);
105 rc
= loc_service_get_id(dev
, &sid
, 0);
109 printf("sysinst_label_dev(): open device\n");
111 rc
= fdisk_create(&fdisk
);
113 printf("Error initializing fdisk.\n");
117 rc
= fdisk_dev_open(fdisk
, sid
, &fdev
);
119 printf("Error opening device.\n");
123 printf("sysinst_label_dev(): create mount directory\n");
125 rc
= vfs_link_path(MOUNT_POINT
, KIND_DIRECTORY
, NULL
);
129 printf("sysinst_label_dev(): create label\n");
131 rc
= fdisk_label_create(fdev
, lt_mbr
);
133 printf("Error creating label: %s.\n", str_error(rc
));
137 printf("sysinst_label_dev(): create partition\n");
139 rc
= fdisk_part_get_max_avail(fdev
, spc_pri
, &capa
);
141 printf("Error getting available capacity: %s.\n", str_error(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
);
154 printf("Error creating partition.\n");
158 rc
= fdisk_part_get_info(part
, &pinfo
);
160 printf("Error getting partition information.\n");
164 printf("sysinst_label_dev(): OK\n");
165 *psvc_id
= pinfo
.svc_id
;
169 /** Set up system volume structure.
171 * @return EOK on success or an error code
173 static errno_t
sysinst_setup_sysvol(void)
181 while (*cp
!= NULL
) {
182 rv
= asprintf(&path
, "%s%s", MOUNT_POINT
, *cp
);
188 rc
= vfs_link_path(path
, KIND_DIRECTORY
, NULL
);
190 printf("Error creating directory '%s'.\n", path
);
211 * @return EOK on success or an error code
213 static errno_t
sysinst_copy_boot_files(void)
217 printf("sysinst_copy_boot_files(): copy bootloader files\n");
218 rc
= futil_rcopy_contents(BOOT_FILES_SRC
, MOUNT_POINT
);
222 printf("sysinst_copy_boot_files(): OK\n");
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)
236 vol_volumes_t
*volumes
= NULL
;
237 vol_volume_t
*volume
= NULL
;
240 rc
= rd_img_open(MOUNT_POINT
"/boot/initrd.img", &rdpath
, &rd
);
242 printf("Error opening initial RAM disk image.\n");
246 rv
= asprintf(&path
, "%s%s", rdpath
, "/cfg/volsrv.sif");
252 printf("Configuring volume server.\n");
253 rc
= vol_volumes_create(path
, &volumes
);
255 printf("Error creating volume server configuration.\n");
260 printf("Configuring volume server: look up volume\n");
261 rc
= vol_volume_lookup_ref(volumes
, INST_VOL_LABEL
, &volume
);
263 printf("Error creating volume server configuration.\n");
268 printf("Configuring volume server: set mount point\n");
269 rc
= vol_volume_set_mountp(volume
, INST_VOL_MP
);
271 printf("Error creating system partition configuration.\n");
276 printf("Configuring volume server: delete reference\n");
277 vol_volume_del_ref(volume
);
279 printf("Configuring volume server: destroy volumes object\n");
280 vol_volumes_destroy(volumes
);
283 rc
= rd_img_close(rd
);
285 printf("Error closing initial RAM disk image.\n");
298 vol_volume_del_ref(volume
);
300 vol_volumes_destroy(volumes
);
302 (void) rd_img_close(rd
);
310 /** Write unaligned 64-bit little-endian number.
312 * @param a Destination buffer
315 static void set_unaligned_u64le(uint8_t *a
, uint64_t data
)
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
)
334 size_t boot_img_size
;
336 size_t core_img_size
;
341 aoff64_t core_blocks
;
342 grub_boot_blocklist_t
*first_bl
, *bl
;
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)
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
);
357 printf("sysinst_copy_boot_blocks: get service ID.\n");
358 rc
= loc_service_get_id(devp
, &sid
, 0);
362 printf("sysinst_copy_boot_blocks: block_init.\n");
363 rc
= block_init(sid
, 512);
367 printf("sysinst_copy_boot_blocks: get block size\n");
368 rc
= block_get_bsize(sid
, &bsize
);
373 printf("Device block size != 512.\n");
377 printf("sysinst_copy_boot_blocks: read boot block\n");
378 rc
= block_read_direct(sid
, BOOT_BLOCK_IDX
, 1, bbuf
);
383 core_blocks
= (core_img_size
+ 511) / 512;
385 /* Clean blocklists */
386 first_bl
= core_img
+ 512 - sizeof(*first_bl
);
388 while (bl
->len
!= 0) {
389 memset(bl
, 0, sizeof(*bl
));
391 if ((void *)bl
< core_img
) {
392 printf("No block terminator in core image.\n");
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
);
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
);
417 printf("sysinst_copy_boot_blocks: OK.\n");
421 /** Eject installation volume.
423 * @param psvc_id Partition service ID
425 static errno_t
sysinst_eject_dev(service_id_t part_id
)
430 rc
= vol_create(&vol
);
432 printf("Error contacting volume service.\n");
436 rc
= vol_part_eject(vol
, part_id
);
438 printf("Error ejecting volume.\n");
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
)
456 service_id_t psvc_id
;
458 rc
= sysinst_label_dev(dev
, &psvc_id
);
462 printf("FS created and mounted. Creating system directory structure.\n");
463 rc
= sysinst_setup_sysvol();
467 printf("Directories created. Copying boot files.\n");
468 rc
= sysinst_copy_boot_files();
472 printf("Boot files done. Configuring the system.\n");
473 rc
= sysinst_customize_initrd();
477 printf("Boot files done. Installing boot blocks.\n");
478 rc
= sysinst_copy_boot_blocks(dev
);
482 printf("Ejecting device.\n");
483 rc
= sysinst_eject_dev(psvc_id
);
490 int main(int argc
, char *argv
[])
492 const char *dev
= DEFAULT_DEV
;
493 return sysinst_install(dev
);