2 * Copyright (c) 2024 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_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
[] = {
88 static const char *sys_dirs
[] = {
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
)
105 rc
= loc_service_get_id(dev
, &sid
, 0);
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
)
125 fdisk_part_spec_t pspec
;
126 fdisk_part_info_t pinfo
;
131 printf("sysinst_label_dev(): get service ID '%s'\n", dev
);
132 rc
= loc_service_get_id(dev
, &sid
, 0);
136 printf("sysinst_label_dev(): open device\n");
138 rc
= fdisk_create(&fdisk
);
140 printf("Error initializing fdisk.\n");
144 rc
= fdisk_dev_open(fdisk
, sid
, &fdev
);
146 printf("Error opening device.\n");
150 printf("sysinst_label_dev(): create mount directory\n");
152 rc
= vfs_link_path(MOUNT_POINT
, KIND_DIRECTORY
, NULL
);
156 printf("sysinst_label_dev(): create label\n");
158 rc
= fdisk_label_create(fdev
, lt_mbr
);
160 printf("Error creating label: %s.\n", str_error(rc
));
164 printf("sysinst_label_dev(): create partition\n");
166 rc
= fdisk_part_get_max_avail(fdev
, spc_pri
, &capa
);
168 printf("Error getting available capacity: %s.\n", str_error(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
);
181 printf("Error creating partition.\n");
185 rc
= fdisk_part_get_info(part
, &pinfo
);
187 printf("Error getting partition information.\n");
191 printf("sysinst_label_dev(): OK\n");
192 *psvc_id
= pinfo
.svc_id
;
196 /** Set up system volume structure.
198 * @return EOK on success or an error code
200 static errno_t
sysinst_setup_sysvol(void)
208 while (*cp
!= NULL
) {
209 rv
= asprintf(&path
, "%s%s", MOUNT_POINT
, *cp
);
215 rc
= vfs_link_path(path
, KIND_DIRECTORY
, NULL
);
217 printf("Error creating directory '%s'.\n", path
);
238 * @return EOK on success or an error code
240 static errno_t
sysinst_copy_boot_files(void)
244 printf("sysinst_copy_boot_files(): copy bootloader files\n");
245 rc
= futil_rcopy_contents(BOOT_FILES_SRC
, MOUNT_POINT
);
249 printf("sysinst_copy_boot_files(): OK\n");
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)
263 vol_volumes_t
*volumes
= NULL
;
264 vol_volume_t
*volume
= NULL
;
267 rc
= rd_img_open(MOUNT_POINT
"/boot/initrd.img", &rdpath
, &rd
);
269 printf("Error opening initial RAM disk image.\n");
273 rv
= asprintf(&path
, "%s%s", rdpath
, "/cfg/volsrv.sif");
279 printf("Configuring volume server.\n");
280 rc
= vol_volumes_create(path
, &volumes
);
282 printf("Error creating volume server configuration.\n");
287 printf("Configuring volume server: look up volume\n");
288 rc
= vol_volume_lookup_ref(volumes
, INST_VOL_LABEL
, &volume
);
290 printf("Error creating volume server configuration.\n");
295 printf("Configuring volume server: set mount point\n");
296 rc
= vol_volume_set_mountp(volume
, INST_VOL_MP
);
298 printf("Error creating system partition configuration.\n");
303 printf("Configuring volume server: delete reference\n");
304 vol_volume_del_ref(volume
);
306 printf("Configuring volume server: destroy volumes object\n");
307 vol_volumes_destroy(volumes
);
310 rc
= rd_img_close(rd
);
312 printf("Error closing initial RAM disk image.\n");
325 vol_volume_del_ref(volume
);
327 vol_volumes_destroy(volumes
);
329 (void) rd_img_close(rd
);
337 /** Write unaligned 64-bit little-endian number.
339 * @param a Destination buffer
342 static void set_unaligned_u64le(uint8_t *a
, uint64_t data
)
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
)
361 size_t boot_img_size
;
363 size_t core_img_size
;
368 aoff64_t core_blocks
;
369 grub_boot_blocklist_t
*first_bl
, *bl
;
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)
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
);
384 printf("sysinst_copy_boot_blocks: get service ID.\n");
385 rc
= loc_service_get_id(devp
, &sid
, 0);
389 printf("sysinst_copy_boot_blocks: block_init.\n");
390 rc
= block_init(sid
);
394 printf("sysinst_copy_boot_blocks: get block size\n");
395 rc
= block_get_bsize(sid
, &bsize
);
400 printf("Device block size != 512.\n");
404 printf("sysinst_copy_boot_blocks: read boot block\n");
405 rc
= block_read_direct(sid
, BOOT_BLOCK_IDX
, 1, bbuf
);
410 core_blocks
= (core_img_size
+ 511) / 512;
412 /* Clean blocklists */
413 first_bl
= core_img
+ 512 - sizeof(*first_bl
);
415 while (bl
->len
!= 0) {
416 memset(bl
, 0, sizeof(*bl
));
418 if ((void *)bl
< core_img
) {
419 printf("No block terminator in core image.\n");
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
);
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
);
444 printf("sysinst_copy_boot_blocks: OK.\n");
448 /** Eject installation volume.
450 * @param psvc_id Partition service ID
452 static errno_t
sysinst_eject_dev(service_id_t part_id
)
457 rc
= vol_create(&vol
);
459 printf("Error contacting volume service.\n");
463 rc
= vol_part_eject(vol
, part_id
);
465 printf("Error ejecting volume.\n");
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
)
483 service_id_t psvc_id
;
485 rc
= sysinst_label_dev(dev
, &psvc_id
);
489 printf("FS created and mounted. Creating system directory structure.\n");
490 rc
= sysinst_setup_sysvol();
494 printf("Directories created. Copying boot files.\n");
495 rc
= sysinst_copy_boot_files();
499 printf("Boot files done. Configuring the system.\n");
500 rc
= sysinst_customize_initrd();
504 printf("Boot files done. Installing boot blocks.\n");
505 rc
= sysinst_copy_boot_blocks(dev
);
509 printf("Ejecting device.\n");
510 rc
= sysinst_eject_dev(psvc_id
);
517 int main(int argc
, char *argv
[])
523 while (default_devs
[i
] != NULL
) {
524 rc
= sysinst_check_dev(default_devs
[i
]);
529 if (default_devs
[i
] == NULL
) {
530 printf("Cannot determine installation device.\n");
534 return sysinst_install(default_devs
[i
]);