1 /*
2 * xhdi.cpp - XHDI like disk driver interface
3 *
4 * Copyright (c) 2002-2008 Petr Stehlik of ARAnyM dev team (see AUTHORS)
5 *
6 * This file is part of the ARAnyM project which builds a new and powerful
7 * TOS/FreeMiNT compatible virtual machine running on almost any hardware.
8 *
9 * ARAnyM is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * ARAnyM is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with ARAnyM; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24 #include "sysdeps.h"
25 #include <cassert>
26 #include "cpu_emulation.h"
27 #include "xhdi.h"
28 #include "atari_rootsec.h"
29 #include "tools.h"
30 #include <errno.h>
31 #if (defined(X86_ASSEMBLY) || defined(X86_64_ASSEMBLY)) && defined(__SSE2__)
32 #include <emmintrin.h>
33 /* #define USE_SSE_BYTESWAP 1 */
34 #endif
35
36 #define DEBUG 0
37 #include "debug.h"
38
39 #ifndef HAVE_FSEEKO
40 # define fseeko(a,b,c) fseek(a,b,c)
41 #endif
42 #ifdef __BEOS__
43 extern "C" int fseeko(FILE *stream, off_t offset, int whence);
44 #endif
45
46 #define XHDI_BLOCK_SIZE 512
47
48 /* XHDI error codes */
49 #include "toserror.h"
50
51
XHDIDriver()52 XHDIDriver::XHDIDriver()
53 {
54 init_disks();
55 }
56
~XHDIDriver()57 XHDIDriver::~XHDIDriver()
58 {
59 close_disks();
60 }
61
reset()62 void XHDIDriver::reset()
63 {
64 close_disks();
65 init_disks();
66 }
67
init_disks()68 void XHDIDriver::init_disks()
69 {
70 // init all disks
71 for(unsigned i=0; i<sizeof(disks)/sizeof(disks[0]); i++) {
72 disks[i].present = false;
73 disks[i].file = NULL;
74 }
75
76 // setup disks array with copied disks and partitions values so that
77 // user's changes in SETUP GUI don't affect the pending disk operations
78 for(int i=0; i<DISKS; i++) {
79 copy_scsidevice_settings(i, &bx_options.disks[i], disks+SCSI_START+i);
80 }
81 for(int i=0; i<2; i++) {
82 copy_atadevice_settings(&bx_options.atadevice[0][i], disks+IDE_START+i);
83 }
84 }
85
close_disks()86 void XHDIDriver::close_disks()
87 {
88 // close all open disks
89 for(unsigned i=0; i<sizeof(disks)/sizeof(disks[0]); i++) {
90 disk_t *disk = &disks[i];
91 if (disk->file != NULL) {
92 #ifdef HAVE_FSYNC
93 fsync(fileno(disk->file));
94 #endif
95 fclose(disk->file);
96 disk->file = NULL;
97 }
98 }
99 }
100
copy_atadevice_settings(const bx_atadevice_options_t * src,disk_t * dest)101 void XHDIDriver::copy_atadevice_settings(const bx_atadevice_options_t *src, disk_t *dest)
102 {
103 safe_strncpy(dest->path, src->path, sizeof(dest->path));
104 safe_strncpy(dest->name, src->model, sizeof(dest->name));
105 dest->present = (src->isCDROM ? false : src->present); // CD-ROMs not supported via XHDI
106 dest->readonly = src->readonly;
107 dest->byteswap = src->byteswap;
108 dest->sim_root = false; // ATA devices are real disks
109
110 // check and remember disk size
111 setDiskSizeInBlocks(dest);
112 }
113
copy_scsidevice_settings(int index,const bx_scsidevice_options_t * src,disk_t * dest)114 void XHDIDriver::copy_scsidevice_settings(int index, const bx_scsidevice_options_t *src, disk_t *dest)
115 {
116 safe_strncpy(dest->path, src->path, sizeof(dest->path));
117 sprintf(dest->name, "PARTITION%d", index);
118 dest->present = src->present;
119 dest->readonly = src->readonly;
120 dest->byteswap = src->byteswap;
121 dest->sim_root = true; // SCSI devices are simulated by prepending a virtual root sector to a single partition
122
123 // check and remember disk size
124 setDiskSizeInBlocks(dest);
125
126 dest->partID[0] = src->partID[0];
127 dest->partID[1] = src->partID[1];
128 dest->partID[2] = src->partID[2];
129 }
130
dev2disk(uint16 major,uint16 minor)131 disk_t *XHDIDriver::dev2disk(uint16 major, uint16 minor)
132 {
133 if (minor != 0)
134 return NULL;
135
136 disk_t *disk = NULL;
137 if (major >= SCSI_START && major <= IDE_END) {
138 disk = &disks[major];
139 if (disk->present) {
140 return disk;
141 }
142 }
143
144 return NULL;
145 }
146
byteSwapBuf(uint8 * dst,int size)147 void XHDIDriver::byteSwapBuf(uint8 *dst, int size)
148 {
149 #ifdef USE_SSE_BYTESWAP
150 __m128i A;
151
152 if ((((uintptr)dst) & 0xf) == 0)
153 {
154 while (size >= 16)
155 {
156 A = *((__m128i *)dst);
157 *((__m128i *)dst) = _mm_or_si128(_mm_srli_epi16(A, 8), _mm_or_si128(_mm_slli_epi16(A, 8), _mm_setzero_si128()));
158 dst += 16;
159 size -= 16;
160 }
161 }
162 #endif
163 while (size >= 2)
164 {
165 char tmp = *dst++;
166 dst[-1] = *dst;
167 *dst++ = tmp;
168 size -= 2;
169 }
170 }
171
XHDrvMap()172 int32 XHDIDriver::XHDrvMap()
173 {
174 D(bug("ARAnyM XHDrvMap"));
175
176 return 0; // drive map
177 }
178
XHInqDriver(uint16 bios_device,memptr name,memptr version,memptr company,wmemptr ahdi_version,wmemptr maxIPL)179 int32 XHDIDriver::XHInqDriver(uint16 bios_device, memptr name, memptr version,
180 memptr company, wmemptr ahdi_version, wmemptr maxIPL)
181 {
182 DUNUSED(bios_device);
183 DUNUSED(name);
184 DUNUSED(version);
185 DUNUSED(company);
186 DUNUSED(ahdi_version);
187 DUNUSED(maxIPL);
188 D(bug("ARAnyM XHInqDriver(bios_device=%u)", bios_device));
189
190 return TOS_EINVFN;
191 }
192
193
XHReadWrite(uint16 major,uint16 minor,uint16 rwflag,uint32 recno,uint16 count,memptr buf)194 int32 XHDIDriver::XHReadWrite(uint16 major, uint16 minor,
195 uint16 rwflag, uint32 recno, uint16 count, memptr buf)
196 {
197 D(bug("ARAnyM XH%s(%u.%u, recno=%lu, count=%u, buf=$%x)",
198 (rwflag & 1) ? "Write" : "Read",
199 major, minor, recno, count, buf));
200
201 disk_t *disk = dev2disk(major, minor);
202 if (disk == NULL) {
203 return TOS_EUNDEV;
204 }
205
206 bool writing = (rwflag & 1);
207 if (writing && disk->readonly) {
208 return TOS_EACCDN;
209 }
210
211 FILE *f = disk->file;
212 if (f == NULL) {
213 // TODO FIXME - if opening the IDE disk drives here then block the ATA emu layer
214 // until the disk->path is closed here again! Otherwise bad data loss might occur
215 // if both the direct PARTITION access and the ATA emu layer start writing to the same disk
216 f = fopen(disk->path, disk->readonly ? "rb" : "r+b");
217 if (f != NULL) {
218 disk->file = f;
219 }
220 else {
221 return TOS_EDRVNR;
222 }
223 }
224
225 if (disk->sim_root) {
226 if (recno == 0 && count > 0) {
227 if (!writing) {
228 // simulate the root sector
229 assert(sizeof(rootsector) == XHDI_BLOCK_SIZE);
230 rootsector sector;
231 memset(§or, 0, sizeof(rootsector));
232
233 sector.hd_siz = SDL_SwapBE32(disk->size_blocks);
234
235 sector.part[0].flg = 1;
236 if (disk->partID[0] == '$') {
237 sector.part[0].id[0] = 0;
238 sector.part[0].id[1] = 'D';
239 char str[3] = {disk->partID[1], disk->partID[2], 0};
240 sector.part[0].id[2] = strtol(str, NULL, 16);
241 }
242 else {
243 sector.part[0].id[0] = disk->partID[0];
244 sector.part[0].id[1] = disk->partID[1];
245 sector.part[0].id[2] = disk->partID[2];
246 }
247 int start_sect = 1;
248 sector.part[0].st = SDL_SwapBE32(start_sect);
249 sector.part[0].siz = SDL_SwapBE32(disk->size_blocks - start_sect);
250
251 // zero out the other three partitions in PTBL
252 for(int i=1; i<4; i++) {
253 sector.part[i].flg = 0;
254 sector.part[i].id[0] = 0;
255 sector.part[i].id[1] = 0;
256 sector.part[i].id[2] = 0;
257 sector.part[i].st = 0;
258 sector.part[i].siz = 0;
259 }
260
261 Host2Atari_memcpy(buf, §or, sizeof(sector));
262 }
263 // correct the count and buffer position
264 count--;
265 buf+=XHDI_BLOCK_SIZE;
266 if (count == 0) {
267 return writing ? TOS_EACCDN : TOS_E_OK;
268 }
269 }
270
271 // correct the offset to the partition
272 if (recno > 0)
273 recno--;
274 }
275
276 off_t offset = (off_t)recno * XHDI_BLOCK_SIZE;
277 if (fseeko(f, offset, SEEK_SET) != 0)
278 return errnoHost2Mint(errno, TOS_EINVAL);
279 if (count == 0)
280 return writing ? TOS_EACCDN : TOS_E_OK;
281
282 memptr bytes = count * XHDI_BLOCK_SIZE;
283 if (writing)
284 {
285 #ifdef USE_SSE_BYTESWAP
286 __attribute__((__aligned__(16))
287 #endif
288 uint8 tempbuf[bytes];
289
290 memptr src_end = buf + bytes - 1;
291 if (! ValidAtariAddr(buf, false, 1))
292 BUS_ERROR(buf);
293 if (! ValidAtariAddr(src_end, false, 1))
294 BUS_ERROR(src_end);
295 uint8 *src = Atari2HostAddr(buf);
296 if (! disk->byteswap)
297 {
298 memcpy(tempbuf, src, bytes);
299 byteSwapBuf(tempbuf, bytes);
300 src = tempbuf;
301 }
302 if (fwrite(src, bytes, 1, f) != 1) {
303 panicbug("nfXHDI: Error writing to device %u.%u (record=%d)", major, minor, recno);
304 return TOS_EWRITF;
305 }
306 } else {
307 memptr dst_end = buf + bytes - 1;
308 if (! ValidAtariAddr(buf, true, 1))
309 BUS_ERROR(buf);
310 if (! ValidAtariAddr(dst_end, true, 1))
311 BUS_ERROR(dst_end);
312 uint8 *dst = Atari2HostAddr(buf);
313 if (fread(dst, bytes, 1, f) != 1) {
314 panicbug("nfXHDI: error reading device %u.%u (record=%d)", major, minor, recno);
315 return TOS_EREADF;
316 } else
317 {
318 if (! disk->byteswap)
319 byteSwapBuf(dst, bytes);
320 }
321 }
322 return TOS_E_OK;
323 }
324
325 int32 XHDIDriver::XHInqTarget2(uint16 major, uint16 minor, lmemptr blocksize,
326 lmemptr device_flags, memptr product_name, uint16 stringlen)
327 {
328 D(bug("ARAnyM XHInqTarget2(%u.%u, product_name_len=%u)", major, minor, stringlen));
329
330 disk_t *disk = dev2disk(major, minor);
331 if (disk == NULL)
332 return TOS_EUNDEV;
333
334 if (blocksize) {
335 WriteInt32(blocksize, XHDI_BLOCK_SIZE);
336 }
337
338 if (device_flags) {
339 WriteInt32(device_flags, 0);
340 }
341
342 if (product_name && stringlen) {
343 Host2AtariSafeStrncpy(product_name, disk->name, stringlen);
344 }
345
346 return TOS_E_OK;
347 }
348
349 int32 XHDIDriver::XHInqDev2(uint16 bios_device, wmemptr major, wmemptr minor,
350 lmemptr start_sector, memptr bpb, lmemptr blocks,
351 memptr partid)
352 {
353 DUNUSED(bios_device);
354 DUNUSED(major);
355 DUNUSED(minor);
356 DUNUSED(start_sector);
357 DUNUSED(bpb);
358 DUNUSED(blocks);
359 DUNUSED(partid);
360 D(bug("ARAnyM XHInqDev2(bios_device=%u)", bios_device));
361
362 return TOS_EINVFN;
363 }
364
365 int32 XHDIDriver::XHGetCapacity(uint16 major, uint16 minor,
366 lmemptr blocks, lmemptr blocksize)
367 {
368 D(bug("ARAnyM XHGetCapacity(%u.%u, blocks=%lu, blocksize=%lu)", major, minor, blocks, blocksize));
369
370 disk_t *disk = dev2disk(major, minor);
371 if (disk == NULL)
372 return TOS_EUNDEV;
373
374 if (! setDiskSizeInBlocks(disk))
375 return TOS_EDRVNR;
376
377 D(bug("XHGetCapacity in blocks = %ld\n", disk->size_blocks));
378 if (blocks != 0)
379 WriteAtariInt32(blocks, disk->size_blocks);
380 if (blocksize != 0)
381 WriteAtariInt32(blocksize, XHDI_BLOCK_SIZE);
382 return TOS_E_OK;
383 }
384
385 int32 XHDIDriver::dispatch(uint32 fncode)
386 {
387 D(bug("ARAnyM XHDI(%u)", fncode));
388 int32 ret;
389 switch(fncode) {
390 case 0: ret = 0x0130; /* XHDI version */
391 break;
392
393 case 1: ret = XHInqTarget2(
394 getParameter(0), /* UWORD major */
395 getParameter(1), /* UWORD minor */
396 getParameter(2), /* ULONG *block_size */
397 getParameter(3), /* ULONG *device_flags */
398 getParameter(4), /* char *product_name */
399 33 /* UWORD stringlen */
400 );
401 break;
402
403 case 6: ret = XHDrvMap();
404 break;
405
406 case 7: ret = XHInqDev2(
407 getParameter(0), /* UWORD bios_device */
408 getParameter(1), /* UWORD *major */
409 getParameter(2), /* UWORD *minor */
410 getParameter(3), /* ULONG *start_sector */
411 getParameter(4), /* BPB *bpb */
412 0, /* ULONG *blocks */
413 0 /* char *partid */
414 );
415 break;
416
417 case 8: ret = XHInqDriver(
418 getParameter(0), /* UWORD bios_device */
419 getParameter(1), /* char *name */
420 getParameter(2), /* char *version */
421 getParameter(3), /* char *company */
422 getParameter(4), /* UWORD *ahdi_version */
423 getParameter(5) /* UWORD *maxIPL */
424 );
425 break;
426
427 case 10: ret = XHReadWrite(
428 getParameter(0), /* UWORD major */
429 getParameter(1), /* UWORD minor */
430 getParameter(2), /* UWORD rwflag */
431 getParameter(3), /* ULONG recno */
432 getParameter(4), /* UWORD count */
433 getParameter(5) /* void *buf */
434 );
435 break;
436
437 case 11: ret = XHInqTarget2(
438 getParameter(0), /* UWORD major */
439 getParameter(1), /* UWORD minor */
440 getParameter(2), /* ULONG *block_size */
441 getParameter(3), /* ULONG *device_flags */
442 getParameter(4), /* char *product_name */
443 getParameter(5) /* UWORD stringlen */
444 );
445 break;
446
447 case 12: ret = XHInqDev2(
448 getParameter(0), /* UWORD bios_device */
449 getParameter(1), /* UWORD *major */
450 getParameter(2), /* UWORD *minor */
451 getParameter(3), /* ULONG *start_sector */
452 getParameter(4), /* BPB *bpb */
453 getParameter(5), /* ULONG *blocks */
454 getParameter(6) /* char *partid */
455 );
456 break;
457
458 case 14: ret = XHGetCapacity(
459 getParameter(0), /* UWORD major */
460 getParameter(1), /* UWORD minor */
461 getParameter(2), /* ULONG *blocks */
462 getParameter(3) /* ULONG *blocksize */
463 );
464 break;
465
466 case 2: /* XHReserve */
467 case 3: /* XHLock */
468 case 4: /* XHStop */
469 case 5: /* XHEject */
470 case 9: /* XHNewCookie */
471 case 15: /* XHMediumChanged */
472 case 16: /* XHMiNTInfo */
473 case 17: /* XHDOSLimits */
474 case 18: /* XHLastAccess */
475 case 19: /* XHReaccess */
476 default: ret = TOS_EINVFN;
477 D(bug("Unimplemented ARAnyM XHDI function #%d", fncode));
478 break;
479 }
480 D(bug("ARAnyM XHDI function returning with %d", ret));
481 return ret;
482 }
483
484 bool XHDIDriver::setDiskSizeInBlocks(disk_t *disk)
485 {
486 disk->size_blocks = 0;
487
488 if (! disk->present)
489 return false;
490
491 struct stat buf;
492 long blocks = 0;
493 if (stat(disk->path, &buf))
494 return false;
495
496 if (S_ISBLK(buf.st_mode)) {
497 int fd = open(disk->path, 0);
498 if (fd < 0) {
499 panicbug("open(%s) failed", disk->path);
500 return false;
501 }
502 blocks = lseek(fd, 0, SEEK_END) / XHDI_BLOCK_SIZE;
503 close(fd);
504 D(bug("%ld blocks on %s", blocks, disk->path));
505 }
506 else {
507 blocks = buf.st_size / XHDI_BLOCK_SIZE;
508 }
509
510 if (disk->sim_root)
511 blocks++; // add the virtual master boot record
512
513 disk->size_blocks = blocks;
514
515 return true;
516 }
517
518 /*
519 vim:ts=4:sw=4:
520 */
521