boot.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496
  1. #include "logfsos.h"
  2. #include "logfs.h"
  3. #include "local.h"
  4. struct LogfsBoot {
  5. LogfsLowLevel *ll;
  6. long bootblocks;
  7. long blocksize;
  8. long size;
  9. long *map;
  10. int trace;
  11. int printbad;
  12. // ulong bootpathmask;
  13. // int bootgenshift;
  14. };
  15. typedef struct LogfsBootPath LogfsBootPath;
  16. //#define LogfsBootGenBits 2
  17. //#define LogfsBootGenMask ((1 << LogfsBootGenBits) - 1)
  18. #define LogfsBootGenMask ((1 << L2BlockCopies) - 1)
  19. struct LogfsBootPath {
  20. ulong path;
  21. uchar gen;
  22. };
  23. #define LOGFSMKBOOTPATH(lb, p) mkdatapath((p)->path, (p)->gen)
  24. #define LOGFSSPLITBOOTPATHEX(bgs, bpm, p, v) ((p)->path = dataseqof(v), (p)->gen = copygenof(v))
  25. #define LOGFSSPLITBOOTPATH(lb, p, v) LOGFSSPLITBOOTPATHEX(0, 0, p, v)
  26. //#define LOGFSMKBOOTPATH(lb, p) (((p)->path & (lb)->bootpathmask) | (((p)->gen & LogfsBootGenMask) << (lb)->bootgenshift))
  27. //#define LOGFSSPLITBOOTPATHEX(bgs, bpm, p, v) ((p)->path = (v) & (bpm), (p)->gen = ((v) >> (bgs)) & LogfsBootGenMask)
  28. //#define LOGFSSPLITBOOTPATH(lb, p, v) LOGFSSPLITBOOTPATHEX((lb)->bootgenshift, (lb)->bootpathmask, p, v)
  29. extern LogfsBootPath logfsbooterasedpath;
  30. static char Ecorrupt[] = "filesystem corrupt";
  31. static char Enospc[] = "no free blocks";
  32. static char Eaddress[] = "address out of range";
  33. static char *
  34. logfsbootblockupdate(LogfsBoot *lb, void *buf, LogfsBootPath *path, uchar tag, ulong block)
  35. {
  36. LogfsLowLevel *ll = lb->ll;
  37. char *errmsg;
  38. ulong packedpath;
  39. if(lb->trace > 1)
  40. print("logfsbootblockupdate: path 0x%.8lux(%d) tag %s block %lud\n",
  41. path->path, path->gen, logfstagname(tag), block);
  42. packedpath = LOGFSMKBOOTPATH(lb, path);
  43. errmsg = (*ll->writeblock)(ll, buf, tag, packedpath, 1, &lb->bootblocks, block);
  44. if(errmsg) {
  45. /*
  46. * ensure block never used again until file system reinitialised
  47. * We have absolutely no idea what state it's in. This is most
  48. * likely if someone turns off the power (or at least threatens
  49. * the power supply), during a block update. This way the block
  50. * is protected until the file system in reinitialised. An alternative
  51. * would be check the file system after a power fail false alarm,
  52. * and erase any Tworse blocks
  53. */
  54. (*ll->setblocktag)(ll, block, LogfsTworse);
  55. return errmsg;
  56. }
  57. (*ll->setblocktag)(ll, block, tag);
  58. (*ll->setblockpath)(ll, block, packedpath);
  59. return nil;
  60. }
  61. char *
  62. logfsbootfettleblock(LogfsBoot *lb, long block, uchar tag, long path, int *markedbad)
  63. {
  64. LogfsLowLevel *ll = lb->ll;
  65. char *errmsg;
  66. void *llsave;
  67. errmsg = (*ll->eraseblock)(ll, block, &llsave, markedbad);
  68. if(errmsg || (markedbad && *markedbad)) {
  69. logfsfreemem(llsave);
  70. return errmsg;
  71. }
  72. errmsg = (*ll->reformatblock)(ll, block, tag, path, 1, &lb->bootblocks, llsave, markedbad);
  73. logfsfreemem(llsave);
  74. return errmsg;
  75. }
  76. /*
  77. * block transfer is the critical unit of update
  78. * we are going to assume that page writes and block erases are atomic
  79. * this can pretty much be assured by not starting a page write or block erase
  80. * if the device feels it is in power fail
  81. */
  82. static char *
  83. logfsbootblocktransfer(LogfsBoot *lb, void *buf, ulong oldblock, int markbad)
  84. {
  85. LogfsLowLevel *ll = lb->ll;
  86. long bestnewblock;
  87. ulong oldpackedpath;
  88. LogfsBootPath oldpath;
  89. short oldtag;
  90. char *errmsg;
  91. int markedbad;
  92. oldpackedpath = (*ll->getblockpath)(ll, oldblock);
  93. oldtag = (*ll->getblocktag)(ll, oldblock);
  94. LOGFSSPLITBOOTPATH(lb, &oldpath, oldpackedpath);
  95. for(;;) {
  96. LogfsBootPath newpath;
  97. bestnewblock = logfsfindfreeblock(ll, markbad ? AllocReasonReplace : AllocReasonTransfer);
  98. if(lb->trace > 0 && markbad)
  99. print("logfsbootblocktransfer: block %lud is bad, copying to %ld\n",
  100. oldblock, bestnewblock);
  101. if(lb->trace > 1 && !markbad)
  102. print("logfsbootblocktransfer: copying block %lud to %ld\n",
  103. oldblock, bestnewblock);
  104. if(bestnewblock == -1)
  105. return Enospc;
  106. newpath = oldpath;
  107. // newpath.gen = (newpath.gen + 1) & LogfsBootGenMask;
  108. newpath.gen = copygensucc(newpath.gen);
  109. errmsg = logfsbootblockupdate(lb, buf, &newpath, oldtag, bestnewblock);
  110. if(errmsg == nil)
  111. break;
  112. if(strcmp(errmsg, Eio) != 0)
  113. return errmsg;
  114. (*ll->markblockbad)(ll, bestnewblock);
  115. }
  116. #ifdef LOGFSTEST
  117. if(logfstest.partialupdate) {
  118. print("skipping erase\n");
  119. logfstest.partialupdate = 0;
  120. return nil;
  121. }
  122. if(logfstest.updatenoerase) {
  123. print("skipping erase\n");
  124. logfstest.updatenoerase = 0;
  125. return nil;
  126. }
  127. #endif
  128. if(oldtag == LogfsTboot)
  129. lb->map[oldpath.path] = bestnewblock;
  130. return logfsbootfettleblock(lb, oldblock, LogfsTnone, ~0, &markedbad);
  131. }
  132. static char *
  133. logfsbootblockread(LogfsBoot *lb, void *buf, long block, LogfsLowLevelReadResult *blocke)
  134. {
  135. LogfsLowLevel *ll = lb->ll;
  136. char *errmsg;
  137. *blocke = LogfsLowLevelReadResultOk;
  138. errmsg = (*ll->readblock)(ll, buf, block, blocke);
  139. if(errmsg)
  140. return errmsg;
  141. if(*blocke != LogfsLowLevelReadResultOk) {
  142. char *errmsg = logfsbootblocktransfer(lb, buf, block, 1);
  143. if(errmsg)
  144. return errmsg;
  145. }
  146. if(*blocke == LogfsLowLevelReadResultHardError)
  147. return Eio;
  148. return nil;
  149. }
  150. char *
  151. logfsbootread(LogfsBoot *lb, void *buf, long n, ulong offset)
  152. {
  153. int i;
  154. if(lb->trace > 0)
  155. print("logfsbootread(0x%.8lux, 0x%lx, 0x%lux)\n", (ulong)buf, n, offset);
  156. if(offset % lb->blocksize || n % lb->blocksize)
  157. return Eio;
  158. n /= lb->blocksize;
  159. offset /= lb->blocksize;
  160. if(offset + n > lb->bootblocks)
  161. return Eio;
  162. for(i = 0; i < n; i++) {
  163. LogfsLowLevelReadResult result;
  164. char *errmsg = logfsbootblockread(lb, buf, lb->map[offset + i], &result);
  165. if(errmsg)
  166. return errmsg;
  167. buf = (uchar *)buf + lb->blocksize;
  168. }
  169. return nil;
  170. }
  171. static char *
  172. logfsbootblockreplace(LogfsBoot *lb, void *buf, ulong logicalblock)
  173. {
  174. uchar *oldblockbuf;
  175. ulong oldblock;
  176. char *errmsg;
  177. LogfsLowLevelReadResult result;
  178. oldblock = lb->map[logicalblock];
  179. oldblockbuf = logfsrealloc(nil, lb->blocksize);
  180. if(oldblockbuf == nil)
  181. return Enomem;
  182. errmsg = logfsbootblockread(lb, oldblockbuf, oldblock, &result);
  183. if(errmsg == nil && memcmp(oldblockbuf, buf, lb->blocksize) != 0)
  184. errmsg = logfsbootblocktransfer(lb, buf, oldblock, 0);
  185. logfsfreemem(oldblockbuf);
  186. return errmsg;
  187. }
  188. char *
  189. logfsbootwrite(LogfsBoot *lb, void *buf, long n, ulong offset)
  190. {
  191. int i;
  192. if(lb->trace > 0)
  193. print("logfsbootwrite(0x%.8lux, 0x%lux, 0x%lux)\n", (ulong)buf, n, offset);
  194. /*
  195. * don't even get started on a write if the power has failed
  196. */
  197. if(offset % lb->blocksize || n % lb->blocksize)
  198. return Eio;
  199. n /= lb->blocksize;
  200. offset /= lb->blocksize;
  201. if(offset + n > lb->bootblocks)
  202. return Eio;
  203. for(i = 0; i < n; i++) {
  204. logfsbootblockreplace(lb, buf, offset + i);
  205. buf = (uchar *)buf + lb->blocksize;
  206. }
  207. return nil;
  208. }
  209. char *
  210. logfsbootio(LogfsBoot *lb, void *buf, long n, ulong offset, int write)
  211. {
  212. return (write ? logfsbootwrite : logfsbootread)(lb, buf, n, offset);
  213. }
  214. static char *
  215. eraseandformatblock(LogfsBoot *lb, long block, int trace)
  216. {
  217. char *errmsg;
  218. int markedbad;
  219. errmsg = logfsbootfettleblock(lb, block, LogfsTnone, ~0, &markedbad);
  220. if(errmsg)
  221. return errmsg;
  222. if(markedbad && trace > 1)
  223. print("erase/format failed - marked bad\n");
  224. return nil;
  225. }
  226. char *
  227. logfsbootopen(LogfsLowLevel *ll, long base, long limit, int trace, int printbad, LogfsBoot **lbp)
  228. {
  229. long *reversemap;
  230. ulong blocksize;
  231. ulong blocks;
  232. long i;
  233. long bootblockmax;
  234. LogfsBoot *lb = nil;
  235. ulong baseblock;
  236. char *errmsg;
  237. // int bootgenshift = ll->pathbits- LogfsBootGenBits;
  238. // ulong bootpathmask = (1 << (ll->pathbits - LogfsBootGenBits)) - 1;
  239. long expectedbootblocks;
  240. errmsg = (*ll->open)(ll, base, limit, trace, 1, &expectedbootblocks);
  241. if(errmsg)
  242. return errmsg;
  243. bootblockmax = -1;
  244. blocks = ll->blocks;
  245. baseblock = (*ll->getbaseblock)(ll);
  246. blocksize = (*ll->getblocksize)(ll);
  247. for(i = 0; i < blocks; i++) {
  248. if((*ll->getblocktag)(ll, i) == LogfsTboot) {
  249. long path = (*ll->getblockpath)(ll, i);
  250. LogfsBootPath lp;
  251. LOGFSSPLITBOOTPATHEX(bootgenshift, bootpathmask, &lp, path);
  252. if((long)lp.path > bootblockmax)
  253. bootblockmax = lp.path;
  254. }
  255. }
  256. if(bootblockmax + 1 >= blocks) {
  257. if(printbad)
  258. print("logfsbootinit: bootblockmax %ld exceeds number of blocks\n", bootblockmax);
  259. return Ecorrupt;
  260. }
  261. if(bootblockmax < 0) {
  262. if(printbad)
  263. print("logfsbootopen: no boot area\n");
  264. return Ecorrupt;
  265. }
  266. if(bootblockmax + 1 != expectedbootblocks) {
  267. if(printbad)
  268. print("logfsbootopen: wrong number of bootblocks (found %lud, expected %lud)\n",
  269. bootblockmax + 1, expectedbootblocks);
  270. }
  271. reversemap = logfsrealloc(nil, sizeof(*reversemap) * (bootblockmax + 1));
  272. if(reversemap == nil)
  273. return Enomem;
  274. for(i = 0; i <= bootblockmax; i++)
  275. reversemap[i] = -1;
  276. for(i = 0; i < blocks; i++) {
  277. LogfsBootPath ipath;
  278. long rm;
  279. ulong ip;
  280. if((*ll->getblocktag)(ll, i) != LogfsTboot)
  281. continue;
  282. ip = (*ll->getblockpath)(ll, i);
  283. LOGFSSPLITBOOTPATHEX(bootgenshift, bootpathmask, &ipath, ip);
  284. rm = reversemap[ipath.path];
  285. if(rm != -1) {
  286. if(printbad)
  287. print("logfsbootopen: blockaddr 0x%.8lux: path %ld(%d): duplicate\n",
  288. blocksize * (baseblock + i), ipath.path, ipath.gen);
  289. /*
  290. * resolve collision
  291. * if this one is partial, then erase it
  292. * if the existing one is partial, erase that
  293. * if both valid, give up
  294. */
  295. if((*ll->getblockpartialformatstatus)(ll, i)) {
  296. errmsg = eraseandformatblock(lb, i, trace);
  297. if(errmsg)
  298. goto error;
  299. }
  300. else if((*ll->getblockpartialformatstatus)(ll, rm)) {
  301. errmsg = eraseandformatblock(lb, rm, trace);
  302. if(errmsg)
  303. goto error;
  304. reversemap[ipath.path] = i;
  305. }
  306. else {
  307. int d;
  308. ulong rmp;
  309. LogfsBootPath rmpath;
  310. rmp = (*ll->getblockpath)(ll, rm);
  311. LOGFSSPLITBOOTPATHEX(bootgenshift, bootpathmask, &rmpath, rmp);
  312. d = (ipath.gen - rmpath.gen) & LogfsBootGenMask;
  313. if(printbad)
  314. print("i.gen = %d rm.gen = %d d = %d\n", ipath.gen, rmpath.gen, d);
  315. if(d == 1) {
  316. /* i is newer;
  317. * keep the OLDER one because
  318. * we might have had a write failure on the last page, but lost the
  319. * power before being able to mark the first page bad
  320. * if, worse, the auxiliary area's tag is the same for first and last page,
  321. * this looks like a successfully written page. so, we cannot believe the
  322. * data in the newer block unless we erased the old one, and then of
  323. * course, we wouldn't have a duplicate.
  324. */
  325. errmsg = eraseandformatblock(lb, i, trace);
  326. if(errmsg)
  327. goto error;
  328. }
  329. else if(d == LogfsBootGenMask) {
  330. /* rm is newer */
  331. errmsg = eraseandformatblock(lb, rm, trace);
  332. if(errmsg)
  333. goto error;
  334. reversemap[ipath.path] = i;
  335. }
  336. else {
  337. errmsg = Ecorrupt;
  338. goto error;
  339. }
  340. }
  341. }
  342. else
  343. reversemap[ipath.path] = i;
  344. }
  345. /*
  346. * final checks; not partial blocks, and no holes
  347. */
  348. for(i = 0; i <= bootblockmax; i++) {
  349. long rm;
  350. rm = reversemap[i];
  351. if(rm == -1) {
  352. if(printbad)
  353. print("logfsbootopen: missing boot block %ld\n", i);
  354. errmsg = Ecorrupt;
  355. goto error;
  356. }
  357. if((*ll->getblockpartialformatstatus)(ll, rm)) {
  358. if(printbad)
  359. print("logfsbootopen: boot block %ld partially written\n", rm);
  360. errmsg = Ecorrupt;
  361. goto error;
  362. }
  363. }
  364. /* the reverse map is consistent */
  365. lb = logfsrealloc(nil, sizeof(*lb));
  366. if(lb == nil) {
  367. errmsg = Enomem;
  368. goto error;
  369. }
  370. lb->blocksize = blocksize;
  371. lb->bootblocks = bootblockmax + 1;
  372. lb->map = reversemap;
  373. lb->trace = trace;
  374. lb->printbad = printbad;
  375. lb->ll = ll;
  376. lb->size = blocksize * lb->bootblocks;
  377. // lb->bootgenshift = bootgenshift;
  378. // lb->bootpathmask = bootpathmask;
  379. *lbp = lb;
  380. if(trace)
  381. print("logfsbootopen: success\n");
  382. return nil;
  383. error:
  384. logfsfreemem(reversemap);
  385. logfsfreemem(lb);
  386. return errmsg;
  387. }
  388. void
  389. logfsbootfree(LogfsBoot *lb)
  390. {
  391. if(lb) {
  392. logfsfreemem(lb->map);
  393. logfsfreemem(lb);
  394. }
  395. }
  396. char *
  397. logfsbootmap(LogfsBoot *lb, ulong laddress, ulong *lblockp, int *lboffsetp, int *lpagep, int *lpageoffsetp, ulong *pblockp, ulong *paddressp)
  398. {
  399. LogfsLowLevel *ll = lb->ll;
  400. ulong lblock;
  401. ulong lboffset, lpageoffset, lpage;
  402. ulong pblock;
  403. ulong paddress;
  404. lblock = laddress / lb->blocksize;
  405. if(lblock >= lb->bootblocks)
  406. return Eaddress;
  407. lboffset = laddress % lb->blocksize;
  408. pblock = lb->map[lblock];
  409. paddress = (*ll->calcrawaddress)(ll, pblock, lboffset);
  410. lpage = lboffset >> ll->l2pagesize;
  411. lpageoffset = lboffset & ((1 << ll->l2pagesize) - 1);
  412. if(lblockp)
  413. *lblockp = lblock;
  414. if(lboffsetp)
  415. *lboffsetp = lboffset;
  416. if(lpagep)
  417. *lpagep = lpage;
  418. if(lpageoffsetp)
  419. *lpageoffsetp = lpageoffset;
  420. if(pblockp)
  421. *pblockp = pblock;
  422. if(paddressp)
  423. *paddressp = paddress;
  424. return nil;
  425. }
  426. long
  427. logfsbootgetiosize(LogfsBoot *lb)
  428. {
  429. return lb->blocksize;
  430. }
  431. long
  432. logfsbootgetsize(LogfsBoot *lb)
  433. {
  434. return lb->size;
  435. }
  436. void
  437. logfsboottrace(LogfsBoot *lb, int level)
  438. {
  439. lb->trace = level;
  440. }