edd.c 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /* -*- linux-c -*- ------------------------------------------------------- *
  3. *
  4. * Copyright (C) 1991, 1992 Linus Torvalds
  5. * Copyright 2007 rPath, Inc. - All Rights Reserved
  6. * Copyright 2009 Intel Corporation; author H. Peter Anvin
  7. *
  8. * ----------------------------------------------------------------------- */
  9. /*
  10. * Get EDD BIOS disk information
  11. */
  12. #include "boot.h"
  13. #include <linux/edd.h>
  14. #include "string.h"
  15. #if defined(CONFIG_EDD) || defined(CONFIG_EDD_MODULE)
  16. /*
  17. * Read the MBR (first sector) from a specific device.
  18. */
  19. static int read_mbr(u8 devno, void *buf)
  20. {
  21. struct biosregs ireg, oreg;
  22. initregs(&ireg);
  23. ireg.ax = 0x0201; /* Legacy Read, one sector */
  24. ireg.cx = 0x0001; /* Sector 0-0-1 */
  25. ireg.dl = devno;
  26. ireg.bx = (size_t)buf;
  27. intcall(0x13, &ireg, &oreg);
  28. return -(oreg.eflags & X86_EFLAGS_CF); /* 0 or -1 */
  29. }
  30. static u32 read_mbr_sig(u8 devno, struct edd_info *ei, u32 *mbrsig)
  31. {
  32. int sector_size;
  33. char *mbrbuf_ptr, *mbrbuf_end;
  34. u32 buf_base, mbr_base;
  35. extern char _end[];
  36. u16 mbr_magic;
  37. sector_size = ei->params.bytes_per_sector;
  38. if (!sector_size)
  39. sector_size = 512; /* Best available guess */
  40. /* Produce a naturally aligned buffer on the heap */
  41. buf_base = (ds() << 4) + (u32)&_end;
  42. mbr_base = (buf_base+sector_size-1) & ~(sector_size-1);
  43. mbrbuf_ptr = _end + (mbr_base-buf_base);
  44. mbrbuf_end = mbrbuf_ptr + sector_size;
  45. /* Make sure we actually have space on the heap... */
  46. if (!(boot_params.hdr.loadflags & CAN_USE_HEAP))
  47. return -1;
  48. if (mbrbuf_end > (char *)(size_t)boot_params.hdr.heap_end_ptr)
  49. return -1;
  50. memset(mbrbuf_ptr, 0, sector_size);
  51. if (read_mbr(devno, mbrbuf_ptr))
  52. return -1;
  53. *mbrsig = *(u32 *)&mbrbuf_ptr[EDD_MBR_SIG_OFFSET];
  54. mbr_magic = *(u16 *)&mbrbuf_ptr[510];
  55. /* check for valid MBR magic */
  56. return mbr_magic == 0xAA55 ? 0 : -1;
  57. }
  58. static int get_edd_info(u8 devno, struct edd_info *ei)
  59. {
  60. struct biosregs ireg, oreg;
  61. memset(ei, 0, sizeof(*ei));
  62. /* Check Extensions Present */
  63. initregs(&ireg);
  64. ireg.ah = 0x41;
  65. ireg.bx = EDDMAGIC1;
  66. ireg.dl = devno;
  67. intcall(0x13, &ireg, &oreg);
  68. if (oreg.eflags & X86_EFLAGS_CF)
  69. return -1; /* No extended information */
  70. if (oreg.bx != EDDMAGIC2)
  71. return -1;
  72. ei->device = devno;
  73. ei->version = oreg.ah; /* EDD version number */
  74. ei->interface_support = oreg.cx; /* EDD functionality subsets */
  75. /* Extended Get Device Parameters */
  76. ei->params.length = sizeof(ei->params);
  77. ireg.ah = 0x48;
  78. ireg.si = (size_t)&ei->params;
  79. intcall(0x13, &ireg, &oreg);
  80. /* Get legacy CHS parameters */
  81. /* Ralf Brown recommends setting ES:DI to 0:0 */
  82. ireg.ah = 0x08;
  83. ireg.es = 0;
  84. intcall(0x13, &ireg, &oreg);
  85. if (!(oreg.eflags & X86_EFLAGS_CF)) {
  86. ei->legacy_max_cylinder = oreg.ch + ((oreg.cl & 0xc0) << 2);
  87. ei->legacy_max_head = oreg.dh;
  88. ei->legacy_sectors_per_track = oreg.cl & 0x3f;
  89. }
  90. return 0;
  91. }
  92. void query_edd(void)
  93. {
  94. char eddarg[8];
  95. int do_mbr = 1;
  96. #ifdef CONFIG_EDD_OFF
  97. int do_edd = 0;
  98. #else
  99. int do_edd = 1;
  100. #endif
  101. int be_quiet;
  102. int devno;
  103. struct edd_info ei, *edp;
  104. u32 *mbrptr;
  105. if (cmdline_find_option("edd", eddarg, sizeof(eddarg)) > 0) {
  106. if (!strcmp(eddarg, "skipmbr") || !strcmp(eddarg, "skip")) {
  107. do_edd = 1;
  108. do_mbr = 0;
  109. }
  110. else if (!strcmp(eddarg, "off"))
  111. do_edd = 0;
  112. else if (!strcmp(eddarg, "on"))
  113. do_edd = 1;
  114. }
  115. be_quiet = cmdline_find_option_bool("quiet");
  116. edp = boot_params.eddbuf;
  117. mbrptr = boot_params.edd_mbr_sig_buffer;
  118. if (!do_edd)
  119. return;
  120. /* Bugs in OnBoard or AddOnCards Bios may hang the EDD probe,
  121. * so give a hint if this happens.
  122. */
  123. if (!be_quiet)
  124. printf("Probing EDD (edd=off to disable)... ");
  125. for (devno = 0x80; devno < 0x80+EDD_MBR_SIG_MAX; devno++) {
  126. /*
  127. * Scan the BIOS-supported hard disks and query EDD
  128. * information...
  129. */
  130. if (!get_edd_info(devno, &ei)
  131. && boot_params.eddbuf_entries < EDDMAXNR) {
  132. memcpy(edp, &ei, sizeof(ei));
  133. edp++;
  134. boot_params.eddbuf_entries++;
  135. }
  136. if (do_mbr && !read_mbr_sig(devno, &ei, mbrptr++))
  137. boot_params.edd_mbr_sig_buf_entries = devno-0x80+1;
  138. }
  139. if (!be_quiet)
  140. printf("ok\n");
  141. }
  142. #endif