dosboot.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594
  1. /*
  2. * This file is part of the UCB release of Plan 9. It is subject to the license
  3. * terms in the LICENSE file found in the top-level directory of this
  4. * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
  5. * part of the UCB release of Plan 9, including this file, may be copied,
  6. * modified, propagated, or distributed except according to the terms contained
  7. * in the LICENSE file.
  8. */
  9. #include "u.h"
  10. #include "lib.h"
  11. #include "mem.h"
  12. #include "dat.h"
  13. #include "fns.h"
  14. #include "fs.h"
  15. struct Dosboot{
  16. uint8_t magic[3];
  17. uint8_t version[8];
  18. uint8_t sectsize[2];
  19. uint8_t clustsize;
  20. uint8_t nresrv[2];
  21. uint8_t nfats;
  22. uint8_t rootsize[2];
  23. uint8_t volsize[2];
  24. uint8_t mediadesc;
  25. uint8_t fatsize[2];
  26. uint8_t trksize[2];
  27. uint8_t nheads[2];
  28. uint8_t nhidden[4];
  29. uint8_t bigvolsize[4];
  30. /* fat 32 */
  31. uint8_t bigfatsize[4];
  32. uint8_t extflags[2];
  33. uint8_t fsversion[2];
  34. uint8_t rootdirstartclust[4];
  35. uint8_t fsinfosect[2];
  36. uint8_t backupbootsect[2];
  37. /* ???
  38. uchar driveno;
  39. uchar reserved0;
  40. uchar bootsig;
  41. uchar volid[4];
  42. uchar label[11];
  43. uchar reserved1[8];
  44. */
  45. };
  46. struct Dosdir{
  47. uint8_t name[8];
  48. uint8_t ext[3];
  49. uint8_t attr;
  50. uint8_t lowercase;
  51. uint8_t hundredth;
  52. uint8_t ctime[2];
  53. uint8_t cdate[2];
  54. uint8_t adate[2];
  55. uint8_t highstart[2];
  56. uint8_t mtime[2];
  57. uint8_t mdate[2];
  58. uint8_t start[2];
  59. uint8_t length[4];
  60. };
  61. #define DOSRONLY 0x01
  62. #define DOSHIDDEN 0x02
  63. #define DOSSYSTEM 0x04
  64. #define DOSVLABEL 0x08
  65. #define DOSDIR 0x10
  66. #define DOSARCH 0x20
  67. /*
  68. * predeclared
  69. */
  70. static void bootdump(Dosboot*);
  71. static void setname(Dosfile*, char*);
  72. /*
  73. * debugging
  74. */
  75. #define chatty 0
  76. #define chat if(chatty)print
  77. /*
  78. * block io buffers
  79. */
  80. enum
  81. {
  82. Nbio= 16,
  83. };
  84. typedef struct Clustbuf Clustbuf;
  85. struct Clustbuf
  86. {
  87. int age;
  88. int32_t sector;
  89. uint8_t *iobuf;
  90. Dos *dos;
  91. int size;
  92. };
  93. Clustbuf bio[Nbio];
  94. /*
  95. * get an io block from an io buffer
  96. */
  97. Clustbuf*
  98. getclust(Dos *dos, int32_t sector)
  99. {
  100. Fs *fs;
  101. Clustbuf *p, *oldest;
  102. int size;
  103. chat("getclust @ %ld\n", sector);
  104. /*
  105. * if we have it, just return it
  106. */
  107. for(p = bio; p < &bio[Nbio]; p++){
  108. if(sector == p->sector && dos == p->dos){
  109. p->age = machp()->ticks;
  110. chat("getclust %ld in cache\n", sector);
  111. return p;
  112. }
  113. }
  114. /*
  115. * otherwise, reuse the oldest entry
  116. */
  117. oldest = bio;
  118. for(p = &bio[1]; p < &bio[Nbio]; p++){
  119. if(p->age <= oldest->age)
  120. oldest = p;
  121. }
  122. p = oldest;
  123. /*
  124. * make sure the buffer is big enough
  125. */
  126. size = dos->clustsize*dos->sectsize;
  127. if(p->iobuf==0 || p->size < size)
  128. p->iobuf = ialloc(size, 0);
  129. p->size = size;
  130. /*
  131. * read in the cluster
  132. */
  133. fs = (Fs*)dos;
  134. chat("getclust addr %lud %p %p %p\n",
  135. (uint32_t)((sector+dos->start)*(int64_t)dos->sectsize),
  136. fs, fs->diskseek, fs->diskread);
  137. if(fs->diskseek(fs, (sector+dos->start)*(int64_t)dos->sectsize) < 0){
  138. chat("can't seek block\n");
  139. return 0;
  140. }
  141. if(fs->diskread(fs, p->iobuf, size) != size){
  142. chat("can't read block\n");
  143. return 0;
  144. }
  145. p->age = machp()->ticks;
  146. p->dos = dos;
  147. p->sector = sector;
  148. chat("getclust %ld read\n", sector);
  149. return p;
  150. }
  151. /*
  152. * walk the fat one level ( n is a current cluster number ).
  153. * return the new cluster number or -1 if no more.
  154. */
  155. static int32_t
  156. fatwalk(Dos *dos, int n)
  157. {
  158. uint32_t k, sect;
  159. Clustbuf *p;
  160. int o;
  161. chat("fatwalk %d\n", n);
  162. if(n < 2 || n >= dos->fatclusters)
  163. return -1;
  164. switch(dos->fatbits){
  165. case 12:
  166. k = (3*n)/2; break;
  167. case 16:
  168. k = 2*n; break;
  169. case 32:
  170. k = 4*n; break;
  171. default:
  172. return -1;
  173. }
  174. if(k >= dos->fatsize*dos->sectsize)
  175. panic("getfat");
  176. sect = (k/(dos->sectsize*dos->clustsize))*dos->clustsize + dos->fataddr;
  177. o = k%(dos->sectsize*dos->clustsize);
  178. p = getclust(dos, sect);
  179. k = p->iobuf[o++];
  180. if(o >= dos->sectsize*dos->clustsize){
  181. p = getclust(dos, sect+dos->clustsize);
  182. o = 0;
  183. }
  184. k |= p->iobuf[o++]<<8;
  185. if(dos->fatbits == 12){
  186. if(n&1)
  187. k >>= 4;
  188. else
  189. k &= 0xfff;
  190. if(k >= 0xff8)
  191. k = -1;
  192. }
  193. else if (dos->fatbits == 32){
  194. if(o >= dos->sectsize*dos->clustsize){
  195. p = getclust(dos, sect+dos->clustsize);
  196. o = 0;
  197. }
  198. k |= p->iobuf[o++]<<16;
  199. k |= p->iobuf[o]<<24;
  200. if (k >= 0xfffffff8)
  201. k = -1;
  202. }
  203. else
  204. k = k < 0xfff8 ? k : -1;
  205. chat("fatwalk %d -> %lud\n", n, k);
  206. return k;
  207. }
  208. /*
  209. * map a file's logical cluster address to a physical sector address
  210. */
  211. static int32_t
  212. fileaddr(Dosfile *fp, int32_t ltarget)
  213. {
  214. Dos *dos = fp->dos;
  215. int32_t l;
  216. int32_t p;
  217. chat("fileaddr %8.8s %ld\n", fp->name, ltarget);
  218. /*
  219. * root directory is contiguous and easy (unless FAT32)
  220. */
  221. if(fp->pstart == 0 && dos->rootsize != 0) {
  222. if(ltarget*dos->sectsize*dos->clustsize >= dos->rootsize*sizeof(Dosdir))
  223. return -1;
  224. l = dos->rootaddr + ltarget*dos->clustsize;
  225. chat("fileaddr %ld -> %ld\n", ltarget, l);
  226. return l;
  227. }
  228. /*
  229. * anything else requires a walk through the fat
  230. */
  231. if(ltarget >= fp->lcurrent && fp->pcurrent){
  232. /* start at the currrent point */
  233. l = fp->lcurrent;
  234. p = fp->pcurrent;
  235. } else {
  236. /* go back to the beginning */
  237. l = 0;
  238. p = fp->pstart;
  239. }
  240. while(l != ltarget){
  241. /* walk the fat */
  242. p = fatwalk(dos, p);
  243. if(p < 0)
  244. return -1;
  245. l++;
  246. }
  247. fp->lcurrent = l;
  248. fp->pcurrent = p;
  249. /*
  250. * clusters start at 2 instead of 0 (why? - presotto)
  251. */
  252. l = dos->dataaddr + (p-2)*dos->clustsize;
  253. chat("fileaddr %ld -> %ld\n", ltarget, l);
  254. return l;
  255. }
  256. /*
  257. * read from a dos file
  258. */
  259. int32_t
  260. dosread(Dosfile *fp, void *a, int32_t n)
  261. {
  262. int32_t addr;
  263. int32_t rv;
  264. int i;
  265. int off;
  266. Clustbuf *p;
  267. uint8_t *from, *to;
  268. if((fp->attr & DOSDIR) == 0){
  269. if(fp->offset >= fp->length)
  270. return 0;
  271. if(fp->offset+n > fp->length)
  272. n = fp->length - fp->offset;
  273. }
  274. to = a;
  275. for(rv = 0; rv < n; rv+=i){
  276. /*
  277. * read the cluster
  278. */
  279. addr = fileaddr(fp, fp->offset/fp->dos->clustbytes);
  280. if(addr < 0)
  281. return -1;
  282. p = getclust(fp->dos, addr);
  283. if(p == 0)
  284. return -1;
  285. /*
  286. * copy the bytes we need
  287. */
  288. off = fp->offset % fp->dos->clustbytes;
  289. from = &p->iobuf[off];
  290. i = n - rv;
  291. if(i > fp->dos->clustbytes - off)
  292. i = fp->dos->clustbytes - off;
  293. memmove(to, from, i);
  294. to += i;
  295. fp->offset += i;
  296. }
  297. return rv;
  298. }
  299. /*
  300. * walk a directory returns
  301. * -1 if something went wrong
  302. * 0 if not found
  303. * 1 if found
  304. */
  305. int
  306. doswalk(File *f, char *name)
  307. {
  308. Dosdir d;
  309. int32_t n;
  310. Dosfile *file;
  311. chat("doswalk %s\n", name);
  312. file = &f->dos;
  313. if((file->attr & DOSDIR) == 0){
  314. chat("walking non-directory!\n");
  315. return -1;
  316. }
  317. setname(file, name);
  318. file->offset = 0; /* start at the beginning */
  319. while((n = dosread(file, &d, sizeof(d))) == sizeof(d)){
  320. chat("comparing to %8.8s.%3.3s\n", (char*)d.name,
  321. (char*)d.ext);
  322. if(memcmp(file->name, d.name, sizeof(d.name)) != 0)
  323. continue;
  324. if(memcmp(file->ext, d.ext, sizeof(d.ext)) != 0)
  325. continue;
  326. if(d.attr & DOSVLABEL){
  327. chat("%8.8s.%3.3s is a LABEL\n", (char*)d.name,
  328. (char*)d.ext);
  329. continue;
  330. }
  331. file->attr = d.attr;
  332. file->pstart = GSHORT(d.start);
  333. if (file->dos->fatbits == 32)
  334. file->pstart |= GSHORT(d.highstart) << 16;
  335. file->length = GLONG(d.length);
  336. file->pcurrent = 0;
  337. file->lcurrent = 0;
  338. file->offset = 0;
  339. return 1;
  340. }
  341. return n >= 0 ? 0 : -1;
  342. }
  343. /*
  344. * instructions that boot blocks can start with
  345. */
  346. #define JMPSHORT 0xeb
  347. #define JMPNEAR 0xe9
  348. /*
  349. * read in a segment
  350. */
  351. int32_t
  352. dosreadseg(File *f, void *va, int32_t len)
  353. {
  354. char *a;
  355. int32_t n, sofar;
  356. Dosfile *fp;
  357. fp = &f->dos;
  358. a = va;
  359. for(sofar = 0; sofar < len; sofar += n){
  360. n = 8*1024;
  361. if(len - sofar < n)
  362. n = len - sofar;
  363. n = dosread(fp, a + sofar, n);
  364. if(n <= 0)
  365. break;
  366. print(".");
  367. }
  368. return sofar;
  369. }
  370. int
  371. dosinit(Fs *fs)
  372. {
  373. Clustbuf *p;
  374. Dosboot *b;
  375. int i;
  376. Dos *dos;
  377. Dosfile *root;
  378. chat("dosinit0 %p %p %p\n", fs, fs->diskseek, fs->diskread);
  379. dos = &fs->dos;
  380. /* defaults till we know better */
  381. dos->sectsize = 512;
  382. dos->clustsize = 1;
  383. /* get first sector */
  384. p = getclust(dos, 0);
  385. if(p == 0){
  386. chat("can't read boot block\n");
  387. return -1;
  388. }
  389. chat("dosinit0a\n");
  390. p->dos = 0;
  391. b = (Dosboot *)p->iobuf;
  392. if(b->magic[0] != JMPNEAR && (b->magic[0] != JMPSHORT || b->magic[2] != 0x90)){
  393. chat("no dos file system %x %x %x %x\n",
  394. b->magic[0], b->magic[1], b->magic[2], b->magic[3]);
  395. return -1;
  396. }
  397. if(chatty)
  398. bootdump(b);
  399. if(b->clustsize == 0) {
  400. unreasonable:
  401. if(chatty){
  402. print("unreasonable FAT BPB: ");
  403. for(i=0; i<3+8+2+1; i++)
  404. print(" %.2ux", p->iobuf[i]);
  405. print("\n");
  406. }
  407. return -1;
  408. }
  409. chat("dosinit1\n");
  410. /*
  411. * Determine the systems' wondrous properties.
  412. * There are heuristics here, but there's no real way
  413. * of knowing if this is a reasonable FAT.
  414. */
  415. dos->fatbits = 0;
  416. dos->sectsize = GSHORT(b->sectsize);
  417. if(dos->sectsize & 0xFF)
  418. goto unreasonable;
  419. dos->clustsize = b->clustsize;
  420. dos->clustbytes = dos->sectsize*dos->clustsize;
  421. dos->nresrv = GSHORT(b->nresrv);
  422. dos->nfats = b->nfats;
  423. dos->fatsize = GSHORT(b->fatsize);
  424. dos->rootsize = GSHORT(b->rootsize);
  425. dos->volsize = GSHORT(b->volsize);
  426. if(dos->volsize == 0)
  427. dos->volsize = GLONG(b->bigvolsize);
  428. dos->mediadesc = b->mediadesc;
  429. if(dos->fatsize == 0) {
  430. chat("fat32\n");
  431. dos->rootsize = 0;
  432. dos->fatsize = GLONG(b->bigfatsize);
  433. dos->fatbits = 32;
  434. }
  435. dos->fataddr = dos->nresrv;
  436. if (dos->rootsize == 0) {
  437. dos->rootaddr = 0;
  438. dos->rootclust = GLONG(b->rootdirstartclust);
  439. dos->dataaddr = dos->fataddr + dos->nfats*dos->fatsize;
  440. } else {
  441. dos->rootaddr = dos->fataddr + dos->nfats*dos->fatsize;
  442. i = dos->rootsize*sizeof(Dosdir) + dos->sectsize - 1;
  443. i = i/dos->sectsize;
  444. dos->dataaddr = dos->rootaddr + i;
  445. }
  446. dos->fatclusters = 2+(dos->volsize - dos->dataaddr)/dos->clustsize;
  447. if(dos->fatbits != 32) {
  448. if(dos->fatclusters < 4087)
  449. dos->fatbits = 12;
  450. else
  451. dos->fatbits = 16;
  452. }
  453. dos->freeptr = 2;
  454. if(dos->clustbytes < 512 || dos->clustbytes > 64*1024)
  455. goto unreasonable;
  456. chat("dosinit2\n");
  457. /*
  458. * set up the root
  459. */
  460. fs->root.fs = fs;
  461. root = &fs->root.dos;
  462. root->dos = dos;
  463. root->pstart = dos->rootsize == 0 ? dos->rootclust : 0;
  464. root->pcurrent = root->lcurrent = 0;
  465. root->offset = 0;
  466. root->attr = DOSDIR;
  467. root->length = dos->rootsize*sizeof(Dosdir);
  468. chat("dosinit3\n");
  469. fs->read = dosreadseg;
  470. fs->walk = doswalk;
  471. return 0;
  472. }
  473. static void
  474. bootdump(Dosboot *b)
  475. {
  476. if(chatty == 0)
  477. return;
  478. print("magic: 0x%2.2x 0x%2.2x 0x%2.2x ",
  479. b->magic[0], b->magic[1], b->magic[2]);
  480. print("version: \"%8.8s\"\n", (char*)b->version);
  481. print("sectsize: %d ", GSHORT(b->sectsize));
  482. print("allocsize: %d ", b->clustsize);
  483. print("nresrv: %d ", GSHORT(b->nresrv));
  484. print("nfats: %d\n", b->nfats);
  485. print("rootsize: %d ", GSHORT(b->rootsize));
  486. print("volsize: %d ", GSHORT(b->volsize));
  487. print("mediadesc: 0x%2.2x\n", b->mediadesc);
  488. print("fatsize: %d ", GSHORT(b->fatsize));
  489. print("trksize: %d ", GSHORT(b->trksize));
  490. print("nheads: %d ", GSHORT(b->nheads));
  491. print("nhidden: %d ", GLONG(b->nhidden));
  492. print("bigvolsize: %d\n", GLONG(b->bigvolsize));
  493. /*
  494. print("driveno: %d\n", b->driveno);
  495. print("reserved0: 0x%2.2x\n", b->reserved0);
  496. print("bootsig: 0x%2.2x\n", b->bootsig);
  497. print("volid: 0x%8.8x\n", GLONG(b->volid));
  498. print("label: \"%11.11s\"\n", b->label);
  499. */
  500. }
  501. /*
  502. * set up a dos file name
  503. */
  504. static void
  505. setname(Dosfile *fp, char *from)
  506. {
  507. char *to;
  508. to = fp->name;
  509. for(; *from && to-fp->name < 8; from++, to++){
  510. if(*from == '.'){
  511. from++;
  512. break;
  513. }
  514. if(*from >= 'a' && *from <= 'z')
  515. *to = *from + 'A' - 'a';
  516. else
  517. *to = *from;
  518. }
  519. while(to - fp->name < 8)
  520. *to++ = ' ';
  521. /* from might be 12345678.123: don't save the '.' in ext */
  522. if(*from == '.')
  523. from++;
  524. to = fp->ext;
  525. for(; *from && to-fp->ext < 3; from++, to++){
  526. if(*from >= 'a' && *from <= 'z')
  527. *to = *from + 'A' - 'a';
  528. else
  529. *to = *from;
  530. }
  531. while(to-fp->ext < 3)
  532. *to++ = ' ';
  533. chat("name is %8.8s.%3.3s\n", fp->name, fp->ext);
  534. }