source: mainline/uspace/app/sysinst/sysinst.c@ 2ee6351

Last change on this file since 2ee6351 was ca127f37, checked in by Jiri Svoboda <jiri@…>, 11 months ago

Persist volume configuration

  • Property mode set to 100644
File size: 12.6 KB
Line 
1/*
2 * Copyright (c) 2024 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 <vfs/vfs.h>
50#include <vol.h>
51
52#include "grub.h"
53#include "rdimg.h"
54#include "volume.h"
55
56/** Device to install to
57 *
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).
63 */
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"
71
72#define MOUNT_POINT "/inst"
73
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
78
79#define BOOT_FILES_SRC CD_MOUNT_POINT
80#define BOOT_BLOCK_IDX 0 /* MBR */
81
82#define CFG_FILES_SRC "/cfg"
83#define CFG_FILES_DEST MOUNT_POINT "/cfg"
84
85static const char *default_devs[] = {
86 DEFAULT_DEV_0,
87 DEFAULT_DEV_1,
88 NULL
89};
90
91static const char *sys_dirs[] = {
92 "/cfg",
93 "/data",
94 NULL
95};
96
97/** Check the if the destination device exists.
98 *
99 * @param dev Disk device
100 *
101 * @return EOK on success or an error code
102 */
103static errno_t sysinst_check_dev(const char *dev)
104{
105 service_id_t sid;
106 errno_t rc;
107
108 rc = loc_service_get_id(dev, &sid, 0);
109 if (rc != EOK)
110 return rc;
111
112 (void)sid;
113 return EOK;
114}
115
116/** Label the destination device.
117 *
118 * @param dev Disk device to label
119 * @param psvc_id Place to store service ID of the created partition
120 *
121 * @return EOK on success or an error code
122 */
123static errno_t sysinst_label_dev(const char *dev, service_id_t *psvc_id)
124{
125 fdisk_t *fdisk;
126 fdisk_dev_t *fdev;
127 fdisk_part_t *part;
128 fdisk_part_spec_t pspec;
129 fdisk_part_info_t pinfo;
130 capa_spec_t capa;
131 service_id_t sid;
132 errno_t rc;
133
134 printf("sysinst_label_dev(): get service ID '%s'\n", dev);
135 rc = loc_service_get_id(dev, &sid, 0);
136 if (rc != EOK)
137 return rc;
138
139 printf("sysinst_label_dev(): open device\n");
140
141 rc = fdisk_create(&fdisk);
142 if (rc != EOK) {
143 printf("Error initializing fdisk.\n");
144 return rc;
145 }
146
147 rc = fdisk_dev_open(fdisk, sid, &fdev);
148 if (rc != EOK) {
149 printf("Error opening device.\n");
150 return rc;
151 }
152
153 printf("sysinst_label_dev(): create mount directory\n");
154
155 rc = vfs_link_path(MOUNT_POINT, KIND_DIRECTORY, NULL);
156 if (rc != EOK)
157 return rc;
158
159 printf("sysinst_label_dev(): create label\n");
160
161 rc = fdisk_label_create(fdev, lt_mbr);
162 if (rc != EOK) {
163 printf("Error creating label: %s.\n", str_error(rc));
164 return rc;
165 }
166
167 printf("sysinst_label_dev(): create partition\n");
168
169 rc = fdisk_part_get_max_avail(fdev, spc_pri, &capa);
170 if (rc != EOK) {
171 printf("Error getting available capacity: %s.\n", str_error(rc));
172 return rc;
173 }
174
175 fdisk_pspec_init(&pspec);
176 pspec.capacity = capa;
177 pspec.pkind = lpk_primary;
178 pspec.fstype = fs_ext4; /* Cannot be changed without modifying core.img */
179 pspec.mountp = MOUNT_POINT;
180 pspec.label = INST_VOL_LABEL;
181
182 rc = fdisk_part_create(fdev, &pspec, &part);
183 if (rc != EOK) {
184 printf("Error creating partition.\n");
185 return rc;
186 }
187
188 rc = fdisk_part_get_info(part, &pinfo);
189 if (rc != EOK) {
190 printf("Error getting partition information.\n");
191 return rc;
192 }
193
194 printf("sysinst_label_dev(): OK\n");
195 *psvc_id = pinfo.svc_id;
196 return EOK;
197}
198
199/** Set up system volume structure.
200 *
201 * @return EOK on success or an error code
202 */
203static errno_t sysinst_setup_sysvol(void)
204{
205 errno_t rc;
206 char *path = NULL;
207 const char **cp;
208 int rv;
209
210 cp = sys_dirs;
211 while (*cp != NULL) {
212 rv = asprintf(&path, "%s%s", MOUNT_POINT, *cp);
213 if (rv < 0) {
214 rc = ENOMEM;
215 goto error;
216 }
217
218 rc = vfs_link_path(path, KIND_DIRECTORY, NULL);
219 if (rc != EOK) {
220 printf("Error creating directory '%s'.\n", path);
221 goto error;
222 }
223
224 free(path);
225 path = NULL;
226 ++cp;
227 }
228
229 free(path);
230 path = NULL;
231
232 /* Copy initial configuration files */
233 rc = futil_rcopy_contents(CFG_FILES_SRC, CFG_FILES_DEST);
234 if (rc != EOK)
235 return rc;
236
237 return EOK;
238error:
239 if (path != NULL)
240 free(path);
241 return rc;
242}
243
244/** Copy boot files.
245 *
246 * @return EOK on success or an error code
247 */
248static errno_t sysinst_copy_boot_files(void)
249{
250 errno_t rc;
251
252 printf("sysinst_copy_boot_files(): copy bootloader files\n");
253 rc = futil_rcopy_contents(BOOT_FILES_SRC, MOUNT_POINT);
254 if (rc != EOK)
255 return rc;
256
257 printf("sysinst_copy_boot_files(): OK\n");
258 return EOK;
259}
260
261/** Set up configuration in the initial RAM disk.
262 *
263 * @return EOK on success or an error code
264 */
265static errno_t sysinst_customize_initrd(void)
266{
267 errno_t rc;
268 rd_img_t *rd = NULL;
269 char *rdpath = NULL;
270 char *path = NULL;
271 vol_volumes_t *volumes = NULL;
272 vol_volume_t *volume = NULL;
273 int rv;
274
275 rc = rd_img_open(MOUNT_POINT "/boot/initrd.img", &rdpath, &rd);
276 if (rc != EOK) {
277 printf("Error opening initial RAM disk image.\n");
278 goto error;
279 }
280
281 rv = asprintf(&path, "%s%s", rdpath, "/cfg/initvol.sif");
282 if (rv < 0) {
283 rc = ENOMEM;
284 goto error;
285 }
286
287 printf("Configuring volume server.\n");
288 rc = vol_volumes_create(path, &volumes);
289 if (rc != EOK) {
290 printf("Error creating volume server configuration.\n");
291 rc = EIO;
292 goto error;
293 }
294
295 printf("Configuring volume server: look up volume\n");
296 rc = vol_volume_lookup_ref(volumes, INST_VOL_LABEL, &volume);
297 if (rc != EOK) {
298 printf("Error creating volume server configuration.\n");
299 rc = EIO;
300 goto error;
301 }
302
303 printf("Configuring volume server: set mount point\n");
304 rc = vol_volume_set_mountp(volume, INST_VOL_MP);
305 if (rc != EOK) {
306 printf("Error creating system partition configuration.\n");
307 rc = EIO;
308 goto error;
309 }
310
311 rc = vol_volumes_sync(volumes);
312 if (rc != EOK) {
313 printf("Error saving volume confiuration.\n");
314 goto error;
315 }
316
317 printf("Configuring volume server: delete reference\n");
318 vol_volume_del_ref(volume);
319 volume = NULL;
320 printf("Configuring volume server: destroy volumes object\n");
321 vol_volumes_destroy(volumes);
322 volumes = NULL;
323
324 rc = rd_img_close(rd);
325 if (rc != EOK) {
326 printf("Error closing initial RAM disk image.\n");
327 rc = EIO;
328 goto error;
329 }
330
331 free(rdpath);
332 rdpath = NULL;
333 free(path);
334 path = NULL;
335
336 return EOK;
337error:
338 if (volume != NULL)
339 vol_volume_del_ref(volume);
340 if (volumes != NULL)
341 vol_volumes_destroy(volumes);
342 if (rd != NULL)
343 (void) rd_img_close(rd);
344 if (path != NULL)
345 free(path);
346 if (rdpath != NULL)
347 free(rdpath);
348 return rc;
349}
350
351/** Write unaligned 64-bit little-endian number.
352 *
353 * @param a Destination buffer
354 * @param data Number
355 */
356static void set_unaligned_u64le(uint8_t *a, uint64_t data)
357{
358 int i;
359
360 for (i = 0; i < 8; i++) {
361 a[i] = (data >> (i * 8)) & 0xff;
362 }
363}
364
365/** Copy boot blocks.
366 *
367 * Install Grub's boot blocks.
368 *
369 * @param devp Disk device
370 * @return EOK on success or an error code
371 */
372static errno_t sysinst_copy_boot_blocks(const char *devp)
373{
374 void *boot_img;
375 size_t boot_img_size;
376 void *core_img;
377 size_t core_img_size;
378 service_id_t sid;
379 size_t bsize;
380 uint8_t bbuf[512];
381 aoff64_t core_start;
382 aoff64_t core_blocks;
383 grub_boot_blocklist_t *first_bl, *bl;
384 errno_t rc;
385
386 printf("sysinst_copy_boot_blocks: Read boot block image.\n");
387 rc = futil_get_file(BOOT_FILES_SRC "/boot/grub/i386-pc/boot.img",
388 &boot_img, &boot_img_size);
389 if (rc != EOK || boot_img_size != 512)
390 return EIO;
391
392 printf("sysinst_copy_boot_blocks: Read GRUB core image.\n");
393 rc = futil_get_file(BOOT_FILES_SRC "/boot/grub/i386-pc/core.img",
394 &core_img, &core_img_size);
395 if (rc != EOK)
396 return EIO;
397
398 printf("sysinst_copy_boot_blocks: get service ID.\n");
399 rc = loc_service_get_id(devp, &sid, 0);
400 if (rc != EOK)
401 return rc;
402
403 printf("sysinst_copy_boot_blocks: block_init.\n");
404 rc = block_init(sid);
405 if (rc != EOK)
406 return rc;
407
408 printf("sysinst_copy_boot_blocks: get block size\n");
409 rc = block_get_bsize(sid, &bsize);
410 if (rc != EOK)
411 return rc;
412
413 if (bsize != 512) {
414 printf("Device block size != 512.\n");
415 return EIO;
416 }
417
418 printf("sysinst_copy_boot_blocks: read boot block\n");
419 rc = block_read_direct(sid, BOOT_BLOCK_IDX, 1, bbuf);
420 if (rc != EOK)
421 return EIO;
422
423 core_start = 16;
424 core_blocks = (core_img_size + 511) / 512;
425
426 /* Clean blocklists */
427 first_bl = core_img + 512 - sizeof(*first_bl);
428 bl = first_bl;
429 while (bl->len != 0) {
430 memset(bl, 0, sizeof(*bl));
431 --bl;
432 if ((void *)bl < core_img) {
433 printf("No block terminator in core image.\n");
434 return EIO;
435 }
436 }
437
438 first_bl->start = host2uint64_t_le(core_start + 1);
439 first_bl->len = host2uint16_t_le(core_blocks - 1);
440 first_bl->segment = grub_boot_i386_pc_kernel_seg + (512 >> 4);
441
442 /* Write boot code into boot block */
443 memcpy(bbuf, boot_img, 440); /* XXX 440 = sizeof(br_block_t.code_area) */
444 bbuf[grub_boot_machine_boot_drive] = 0xff;
445 set_unaligned_u64le(bbuf + grub_boot_machine_kernel_sector, core_start);
446
447 printf("sysinst_copy_boot_blocks: write boot block\n");
448 rc = block_write_direct(sid, BOOT_BLOCK_IDX, 1, bbuf);
449 if (rc != EOK)
450 return EIO;
451
452 printf("sysinst_copy_boot_blocks: write core blocks\n");
453 /* XXX Must pad last block with zeros */
454 rc = block_write_direct(sid, core_start, core_blocks, core_img);
455 if (rc != EOK)
456 return EIO;
457
458 printf("sysinst_copy_boot_blocks: OK.\n");
459 return EOK;
460}
461
462/** Eject installation volume.
463 *
464 * @param psvc_id Partition service ID
465 */
466static errno_t sysinst_eject_dev(service_id_t part_id)
467{
468 vol_t *vol = NULL;
469 errno_t rc;
470
471 rc = vol_create(&vol);
472 if (rc != EOK) {
473 printf("Error contacting volume service.\n");
474 goto out;
475 }
476
477 rc = vol_part_eject(vol, part_id);
478 if (rc != EOK) {
479 printf("Error ejecting volume.\n");
480 goto out;
481 }
482
483 rc = EOK;
484out:
485 vol_destroy(vol);
486 return rc;
487}
488
489/** Install system to a device.
490 *
491 * @param dev Device to install to.
492 * @return EOK on success or an error code
493 */
494static errno_t sysinst_install(const char *dev)
495{
496 errno_t rc;
497 service_id_t psvc_id;
498
499 rc = sysinst_label_dev(dev, &psvc_id);
500 if (rc != EOK)
501 return rc;
502
503 printf("FS created and mounted. Creating system directory structure.\n");
504 rc = sysinst_setup_sysvol();
505 if (rc != EOK)
506 return rc;
507
508 printf("Directories created. Copying boot files.\n");
509 rc = sysinst_copy_boot_files();
510 if (rc != EOK)
511 return rc;
512
513 printf("Boot files done. Configuring the system.\n");
514 rc = sysinst_customize_initrd();
515 if (rc != EOK)
516 return rc;
517
518 printf("Boot files done. Installing boot blocks.\n");
519 rc = sysinst_copy_boot_blocks(dev);
520 if (rc != EOK)
521 return rc;
522
523 printf("Ejecting device.\n");
524 rc = sysinst_eject_dev(psvc_id);
525 if (rc != EOK)
526 return rc;
527
528 return EOK;
529}
530
531int main(int argc, char *argv[])
532{
533 unsigned i;
534 errno_t rc;
535
536 i = 0;
537 while (default_devs[i] != NULL) {
538 rc = sysinst_check_dev(default_devs[i]);
539 if (rc == EOK)
540 break;
541 }
542
543 if (default_devs[i] == NULL) {
544 printf("Cannot determine installation device.\n");
545 return 1;
546 }
547
548 return sysinst_install(default_devs[i]);
549}
550
551/** @}
552 */
Note: See TracBrowser for help on using the repository browser.