1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <unistd.h>
28#include <sys/param.h>
29#include <sys/bootvfs.h>
30#include <sys/filep.h>
31
32#include <libintl.h>
33#include <locale.h>
34#include "message.h"
35
36/*
37 * This file is glue layer to pcfs module in usr/src/common/fs/pcfs.c.
38 * It's main functionality is to get the stage file blocklist. It's
39 * used for installing grub on a Solaris boot partition.
40 */
41extern struct boot_fs_ops bpcfs_ops;
42struct boot_fs_ops *bfs_ops;
43struct boot_fs_ops *bfs_tab[] = {&bpcfs_ops, NULL};
44static int dev_fd;
45int bootrd_debug = 0;
46
47#define	DEV_BSIZE	512
48#define	MAX_CHUNK	64
49
50static unsigned int *blocklist;
51
52/* diskread_callback is set in filesystem module (pcfs.c) */
53int (*diskread_callback)(int, int);
54int (*fileread_callback)(int, int);
55
56static int
57add_stage2_block(int blocknum, int nblk)
58{
59	static int i = -2;
60
61	if (i >= 0 && (blocklist[i] + blocklist[i + 1] == blocknum)) {
62		blocklist[i + 1] += nblk;
63		return (0);
64	}
65
66	i += 2;
67	if (i >= DEV_BSIZE / 8) {
68		fprintf(stderr, PCFS_FRAGMENTED);
69		exit(-1);
70	}
71	blocklist[i] = blocknum;
72	blocklist[i + 1] = nblk;
73	return (0);
74}
75
76/*
77 * This one reads the ramdisk. If fi_memp is set, we copy the
78 * ramdisk content to the designated buffer. Otherwise, we
79 * do a "cached" read (set fi_memp to the actual ramdisk buffer).
80 */
81int
82diskread(fileid_t *filep)
83{
84	int ret;
85	uint_t blocknum, diskloc;
86
87	blocknum = filep->fi_blocknum;
88
89	if (diskread_callback) {
90		diskread_callback(blocknum, filep->fi_count / DEV_BSIZE);
91		return (0);
92	}
93
94	diskloc = blocknum * DEV_BSIZE;
95	if (filep->fi_memp == NULL) {
96		filep->fi_memp = malloc(filep->fi_count);
97	}
98	if (filep->fi_memp == NULL) {
99		fprintf(stderr, OUT_OF_MEMORY);
100		return (-1);
101	}
102
103	ret = pread(dev_fd, filep->fi_memp, filep->fi_count, diskloc);
104	if (ret < 0)
105		perror("diskread: pread");
106	return (ret >= 0 ? 0 : -1);
107}
108
109void *
110bkmem_alloc(size_t s)
111{
112	return (malloc(s));
113}
114
115/*ARGSUSED*/
116void
117bkmem_free(void *p, size_t s)
118{
119	free(p);
120}
121
122static int
123mountroot(char *name)
124{
125	int i;
126
127	/* try ops in bfs_tab and return the first successful one */
128	for (i = 0; bfs_tab[i] != NULL; i++) {
129		bfs_ops = bfs_tab[i];
130		if (BRD_MOUNTROOT(bfs_ops, name) == 0)
131			return (0);
132	}
133	return (-1);
134}
135
136static int
137unmountroot()
138{
139	return (BRD_UNMOUNTROOT(bfs_ops));
140}
141
142static int
143pcfs_glue_open(const char *filename, int flags)
144{
145	return (BRD_OPEN(bfs_ops, (char *)filename, flags));
146}
147
148static int
149pcfs_glue_close(int fd)
150{
151	return (BRD_CLOSE(bfs_ops, fd));
152}
153
154static ssize_t
155pcfs_glue_read(int fd, void *buf, size_t size)
156{
157	return (BRD_READ(bfs_ops, fd, buf, size));
158}
159
160/*
161 * Get the blocklist for stage2
162 */
163int
164read_stage2_blocklist(int device_fd, unsigned int *blkbuf)
165{
166	int i, fd, stage2_block;
167	char buf[DEV_BSIZE];
168	ssize_t size;
169
170	dev_fd = device_fd;
171	if (mountroot("dummy") != 0) {
172		fprintf(stderr, MOUNT_FAIL_PCFS);
173		return (-1);
174	}
175
176	if ((fd = pcfs_glue_open("/boot/grub/stage2", 0)) == -1) {
177		fprintf(stderr, OPEN_FAIL_PCFS);
178		return (-1);
179	}
180
181	if (bootrd_debug)
182		(void) printf("start reading stage2:\n");
183	stage2_block = 0;
184	blocklist = blkbuf;
185	fileread_callback = add_stage2_block;
186	for (;;) {
187		size = pcfs_glue_read(fd, buf, DEV_BSIZE);
188		if (size != DEV_BSIZE)
189			break;
190		stage2_block++;
191	}
192	fileread_callback = NULL;
193	(void) pcfs_glue_close(fd);
194
195	if (bootrd_debug) {
196		(void) printf("last block size = %d\n", size);
197		for (i = 0; blocklist[i] != 0; i += 2) {
198			(void) printf("sectors: %d-%d\n",
199			    blocklist[i],
200			    blocklist[i] + blocklist[i + 1] - 1);
201		}
202		(void) printf("total blocks in stage 2: %d\n", stage2_block);
203	}
204
205	(void) unmountroot();
206	return (0);
207}
208