Initial import from FreeBSD RELENG_4:
[dragonfly.git] / lib / libdisk / write_disk.c
1 /*
2  * ----------------------------------------------------------------------------
3  * "THE BEER-WARE LICENSE" (Revision 42):
4  * <phk@login.dknet.dk> wrote this file.  As long as you retain this notice you
5  * can do whatever you want with this stuff. If we meet some day, and you think
6  * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
7  * ----------------------------------------------------------------------------
8  *
9  * $FreeBSD: src/lib/libdisk/write_disk.c,v 1.28.2.10 2001/05/13 21:01:38 jkh Exp $
10  *
11  */
12
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16 #include <fcntl.h>
17 #include <string.h>
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <sys/ioctl.h>
21 #include <sys/disklabel.h>
22 #include <sys/diskslice.h>
23 #include <paths.h>
24 #include "libdisk.h"
25
26 #define DOSPTYP_EXTENDED        5
27 #define BBSIZE                  8192
28 #define SBSIZE                  8192
29 #define DEF_RPM                 3600
30 #define DEF_INTERLEAVE  1
31
32 #ifdef PC98
33 #define WHERE(offset,disk) (offset)
34 #else
35 #define WHERE(offset,disk) (disk->flags & DISK_ON_TRACK ? offset + 63 : offset)
36 #endif
37
38 /* XXX: A lot of hardcoded 512s probably should be foo->sector_size;
39         I'm not sure which, so I leave it like it worked before. --schweikh */
40 int
41 Write_FreeBSD(int fd, struct disk *new, struct disk *old, struct chunk *c1)
42 {
43         struct disklabel *dl;
44         struct chunk *c2;
45         int i,j;
46         void *p;
47         u_char buf[BBSIZE];
48 #ifdef __alpha__
49         u_long *lp, sum;
50 #endif
51
52         for(i = 0; i < BBSIZE/512; i++) {
53                 p = read_block(fd, WHERE(i + c1->offset, new), 512);
54                 memcpy(buf + 512 * i, p, 512);
55                 free(p);
56         }
57 #if defined(__i386__)
58         if(new->boot1)
59                 memcpy(buf, new->boot1, 512);
60
61         if(new->boot2)
62                 memcpy(buf + 512, new->boot2, BBSIZE-512);
63 #elif defined(__alpha__)
64         if(new->boot1)
65                 memcpy(buf + 512, new->boot1, BBSIZE-512);
66 #endif
67
68         dl = (struct disklabel *)(buf + 512 * LABELSECTOR + LABELOFFSET);
69         memset(dl, 0, sizeof *dl);
70
71         for(c2 = c1->part; c2; c2 = c2->next) {
72                 if (c2->type == unused) continue;
73                 if (!strcmp(c2->name, "X")) continue;
74 #ifdef __alpha__
75                 j = c2->name[strlen(c2->name) - 1] - 'a';
76 #else
77                 j = c2->name[strlen(new->name) + 2] - 'a';
78 #endif
79                 if (j < 0 || j >= MAXPARTITIONS || j == RAW_PART) {
80 #ifdef DEBUG
81                         warn("weird partition letter %c", c2->name[strlen(new->name) + 2]);
82 #endif
83                         continue;
84                 }
85                 dl->d_partitions[j].p_size = c2->size;
86                 dl->d_partitions[j].p_offset = c2->offset;
87                 dl->d_partitions[j].p_fstype = c2->subtype;
88         }
89
90         dl->d_bbsize = BBSIZE;
91         /*
92          * Add in defaults for superblock size, interleave, and rpms
93          */
94         dl->d_sbsize = SBSIZE;
95         dl->d_interleave = DEF_INTERLEAVE;
96         dl->d_rpm = DEF_RPM;
97
98         strcpy(dl->d_typename, c1->name);
99
100         dl->d_secsize = 512;
101         dl->d_secperunit = new->chunks->size;
102         dl->d_ncylinders =  new->bios_cyl;
103         dl->d_ntracks =  new->bios_hd;
104         dl->d_nsectors =  new->bios_sect;
105         dl->d_secpercyl = dl->d_ntracks * dl->d_nsectors;
106
107         dl->d_npartitions = MAXPARTITIONS;
108
109         dl->d_type = new->name[0] == 's' || new->name[0] == 'd' ||
110             new->name[0] == 'o' ? DTYPE_SCSI : DTYPE_ESDI;
111         dl->d_partitions[RAW_PART].p_size = c1->size;
112         dl->d_partitions[RAW_PART].p_offset = c1->offset;
113 #ifdef PC98
114         dl->d_rpm = 3600;
115         dl->d_interleave = 1;
116 #endif
117
118 #ifndef PC98
119         if(new->flags & DISK_ON_TRACK)
120                 for(i=0;i<MAXPARTITIONS;i++)
121                         if (dl->d_partitions[i].p_size)
122                                 dl->d_partitions[i].p_offset += 63;
123 #endif
124         dl->d_magic = DISKMAGIC;
125         dl->d_magic2 = DISKMAGIC;
126         dl->d_checksum = dkcksum(dl);
127
128 #ifdef __alpha__
129         /*
130          * Tell SRM where the bootstrap is.
131          */
132         lp = (u_long *)buf;
133         lp[60] = 15;
134         lp[61] = 1;
135         lp[62] = 0;
136
137         /*
138          * Generate the bootblock checksum for the SRM console.
139          */
140         for (lp = (u_long *)buf, i = 0, sum = 0; i < 63; i++)
141             sum += lp[i];
142         lp[63] = sum;
143 #endif /*__alpha__*/
144
145         for(i=0;i<BBSIZE/512;i++) {
146                 write_block(fd,WHERE(i + c1->offset, new), buf + 512 * i, 512);
147         }
148
149         return 0;
150 }
151
152 int
153 Write_Extended(int fd, struct disk *new, struct disk *old, struct chunk *c1)
154 {
155         return 0;
156 }
157
158 #if defined(__i386__) && !defined(PC98)
159 static void
160 Write_Int32(u_int32_t *p, u_int32_t v)
161 {
162     u_int8_t *bp = (u_int8_t *)p;
163     bp[0] = (v >> 0) & 0xff;
164     bp[1] = (v >> 8) & 0xff;
165     bp[2] = (v >> 16) & 0xff;
166     bp[3] = (v >> 24) & 0xff;
167 }
168 #endif
169
170 #if defined(__i386__) && !defined(PC98)
171 /*
172  * Special install-time configuration for the i386 boot0 boot manager.
173  */
174 static void
175 Cfg_Boot_Mgr(u_char *mbr, int edd)
176 {
177     if (mbr[0x1b0] == 0x66 && mbr[0x1b1] == 0xbb) {
178         if (edd)
179             mbr[0x1bb] |= 0x80; /* Packet mode on */
180         else
181             mbr[0x1bb] &= 0x7f; /* Packet mode off */
182     }
183 }
184 #endif
185
186 int
187 Write_Disk(struct disk *d1)
188 {
189         int fd,i;
190 #ifdef __i386__
191         int j;
192 #endif
193         struct disk *old = 0;
194         struct chunk *c1;
195         int ret = 0;
196         char device[64];
197         u_char *mbr;
198         struct dos_partition *dp,work[NDOSPART];
199 #ifdef PC98
200         int s[7];
201         int PC98_EntireDisk = 0;
202 #else
203         int s[4];
204 #ifdef __i386__
205         int need_edd = 0;       /* Need EDD (packet interface) */
206 #endif
207 #endif
208         int one = 1;
209         int zero = 0;
210
211         strcpy(device,_PATH_DEV);
212         strcat(device,d1->name);
213
214 #ifdef PC98
215         /* XXX - for entire FreeBSD(98) */
216         for (c1 = d1->chunks->part; c1; c1 = c1->next) {
217             if ((c1->type == freebsd) || (c1->offset == 0))
218                 device[9] = 0;
219         }
220 #endif
221
222         fd = open(device,O_RDWR);
223         if (fd < 0) {
224 #ifdef DEBUG
225                 warn("open(%s) failed", device);
226 #endif
227                 return 1;
228         }
229         ioctl(fd, DIOCWLABEL, &one);
230
231         memset(s,0,sizeof s);
232 #ifdef PC98
233         mbr = read_block(fd, WHERE(1, d1), d1->sector_size);
234 #else
235         mbr = read_block(fd, WHERE(0, d1), d1->sector_size);
236 #endif
237         dp = (struct dos_partition*)(mbr + DOSPARTOFF);
238         memcpy(work, dp, sizeof work);
239         dp = work;
240         free(mbr);
241         for (c1 = d1->chunks->part; c1; c1 = c1->next) {
242                 if (c1->type == unused) continue;
243                 if (!strcmp(c1->name, "X")) continue;
244 #ifdef __i386__
245                 j = c1->name[4] - '1';
246                 j = c1->name[strlen(d1->name) + 1] - '1';
247 #ifdef PC98
248                 if (j < 0 || j > 7)
249 #else
250                 if (j < 0 || j > 3)
251 #endif
252                         continue;
253                 s[j]++;
254 #endif
255 #ifndef PC98
256                 if (c1->type == extended)
257                         ret += Write_Extended(fd, d1, old, c1);
258 #endif
259                 if (c1->type == freebsd)
260                         ret += Write_FreeBSD(fd, d1, old, c1);
261
262 #ifdef __i386__
263 #ifndef PC98
264                 Write_Int32(&dp[j].dp_start, c1->offset);
265                 Write_Int32(&dp[j].dp_size, c1->size);
266 #endif
267
268                 i = c1->offset;
269 #ifdef PC98
270                 dp[j].dp_ssect = dp[j].dp_ipl_sct = i % d1->bios_sect;
271                 i -= dp[j].dp_ssect;
272                 i /= d1->bios_sect;
273                 dp[j].dp_shd = dp[j].dp_ipl_head = i % d1->bios_hd;
274                 i -= dp[j].dp_shd;
275                 i /= d1->bios_hd;
276                 dp[j].dp_scyl = dp[j].dp_ipl_cyl = i;
277 #else
278                 if (i >= 1024*d1->bios_sect*d1->bios_hd) {
279                         dp[j].dp_ssect = 0xff;
280                         dp[j].dp_shd = 0xff;
281                         dp[j].dp_scyl = 0xff;
282                         need_edd++;
283                 } else {
284                         dp[j].dp_ssect = i % d1->bios_sect;
285                         i -= dp[j].dp_ssect++;
286                         i /= d1->bios_sect;
287                         dp[j].dp_shd =  i % d1->bios_hd;
288                         i -= dp[j].dp_shd;
289                         i /= d1->bios_hd;
290                         dp[j].dp_scyl = i;
291                         i -= dp[j].dp_scyl;
292                         dp[j].dp_ssect |= i >> 2;
293                 }
294 #endif /* PC98 */
295
296 #ifdef DEBUG
297                 printf("S:%lu = (%x/%x/%x)",
298                         c1->offset, dp[j].dp_scyl, dp[j].dp_shd, dp[j].dp_ssect);
299 #endif
300
301                 i = c1->end;
302 #ifdef PC98
303 #if 1
304                 dp[j].dp_esect = dp[j].dp_ehd = 0;
305                 dp[j].dp_ecyl = i / (d1->bios_sect * d1->bios_hd);
306 #else
307                 dp[j].dp_esect = i % d1->bios_sect;
308                 i -= dp[j].dp_esect;
309                 i /= d1->bios_sect;
310                 dp[j].dp_ehd =  i % d1->bios_hd;
311                 i -= dp[j].dp_ehd;
312                 i /= d1->bios_hd;
313                 dp[j].dp_ecyl = i;
314 #endif
315 #else
316                 dp[j].dp_esect = i % d1->bios_sect;
317                 i -= dp[j].dp_esect++;
318                 i /= d1->bios_sect;
319                 dp[j].dp_ehd =  i % d1->bios_hd;
320                 i -= dp[j].dp_ehd;
321                 i /= d1->bios_hd;
322                 if (i>1023) i = 1023;
323                 dp[j].dp_ecyl = i;
324                 i -= dp[j].dp_ecyl;
325                 dp[j].dp_esect |= i >> 2;
326 #endif
327
328 #ifdef DEBUG
329                 printf("  E:%lu = (%x/%x/%x)\n",
330                         c1->end, dp[j].dp_ecyl, dp[j].dp_ehd, dp[j].dp_esect);
331 #endif
332
333 #ifdef PC98
334                 dp[j].dp_mid = c1->subtype & 0xff;
335                 dp[j].dp_sid = c1->subtype >> 8;
336                 if (c1->flags & CHUNK_ACTIVE)
337                         dp[j].dp_mid |= 0x80;
338
339                 strncpy(dp[j].dp_name, c1->sname, 16);
340 #else
341                 dp[j].dp_typ = c1->subtype;
342                 if (c1->flags & CHUNK_ACTIVE)
343                         dp[j].dp_flag = 0x80;
344                 else
345                         dp[j].dp_flag = 0;
346 #endif
347 #endif
348         }
349 #ifdef __i386__
350         j = 0;
351         for(i = 0; i < NDOSPART; i++) {
352                 if (!s[i])
353                         memset(dp + i, 0, sizeof *dp);
354 #ifndef PC98
355                 if (dp[i].dp_flag)
356                         j++;
357 #endif
358         }
359 #ifndef PC98
360         if (!j)
361                 for(i = 0; i < NDOSPART; i++)
362                         if (dp[i].dp_typ == 0xa5)
363                                 dp[i].dp_flag = 0x80;
364 #endif
365
366 #ifdef PC98
367         if (d1->bootipl)
368                 write_block(fd, WHERE(0, d1), d1->bootipl, d1->sector_size);
369
370         mbr = read_block(fd, WHERE(1, d1), d1->sector_size);
371         memcpy(mbr + DOSPARTOFF, dp, sizeof *dp * NDOSPART);
372         /* XXX - for entire FreeBSD(98) */
373         for (c1 = d1->chunks->part; c1; c1 = c1->next)
374                 if (((c1->type == freebsd) || (c1->type == fat))
375                          && (c1->offset == 0))
376                         PC98_EntireDisk = 1;
377         if (PC98_EntireDisk == 0)
378                 write_block(fd, WHERE(1, d1), mbr, d1->sector_size);
379
380         if (d1->bootmenu)
381                 for (i = 0; i * d1->sector_size < d1->bootmenu_size; i++)
382                         write_block(fd, WHERE(2 + i, d1), &d1->bootmenu[i * d1->sector_size], d1->sector_size);
383 #else
384         mbr = read_block(fd, WHERE(0, d1), d1->sector_size);
385         if (d1->bootmgr) {
386                 memcpy(mbr, d1->bootmgr, DOSPARTOFF);
387                 Cfg_Boot_Mgr(mbr, need_edd);
388         }
389         memcpy(mbr + DOSPARTOFF, dp, sizeof *dp * NDOSPART);
390         mbr[512-2] = 0x55;
391         mbr[512-1] = 0xaa;
392         write_block(fd, WHERE(0, d1), mbr, d1->sector_size);
393         if (d1->bootmgr && d1->bootmgr_size > d1->sector_size)
394           for(i = 1; i * d1->sector_size <= d1->bootmgr_size; i++)
395             write_block(fd, WHERE(i, d1), &d1->bootmgr[i * d1->sector_size], d1->sector_size);
396 #endif
397 #endif
398
399         i = 1;
400         i = ioctl(fd, DIOCSYNCSLICEINFO, &i);
401 #ifdef DEBUG
402         if (i != 0)
403                 warn("ioctl(DIOCSYNCSLICEINFO)");
404 #endif
405         ioctl(fd, DIOCWLABEL, &zero);
406         close(fd);
407         return 0;
408 }