|
@@ -35,6 +35,8 @@ FILE_LICENCE ( GPL2_OR_LATER );
|
35
|
35
|
#include <ipxe/timer.h>
|
36
|
36
|
#include <ipxe/acpi.h>
|
37
|
37
|
#include <ipxe/sanboot.h>
|
|
38
|
+#include <ipxe/device.h>
|
|
39
|
+#include <ipxe/pci.h>
|
38
|
40
|
#include <realmode.h>
|
39
|
41
|
#include <bios.h>
|
40
|
42
|
#include <biosint.h>
|
|
@@ -654,8 +656,10 @@ static int int13_extension_check ( struct int13_drive *int13 __unused,
|
654
|
656
|
if ( ix86->regs.bx == 0x55aa ) {
|
655
|
657
|
DBGC2 ( int13, "INT13 extensions installation check\n" );
|
656
|
658
|
ix86->regs.bx = 0xaa55;
|
657
|
|
- ix86->regs.cx = INT13_EXTENSION_LINEAR;
|
658
|
|
- return INT13_EXTENSION_VER_1_X;
|
|
659
|
+ ix86->regs.cx = ( INT13_EXTENSION_LINEAR |
|
|
660
|
+ INT13_EXTENSION_EDD |
|
|
661
|
+ INT13_EXTENSION_64BIT );
|
|
662
|
+ return INT13_EXTENSION_VER_3_0;
|
659
|
663
|
} else {
|
660
|
664
|
return -INT13_STATUS_INVALID;
|
661
|
665
|
}
|
|
@@ -678,25 +682,56 @@ static int int13_extended_rw ( struct int13_drive *int13,
|
678
|
682
|
userptr_t buffer,
|
679
|
683
|
size_t len ) ) {
|
680
|
684
|
struct int13_disk_address addr;
|
|
685
|
+ uint8_t bufsize;
|
681
|
686
|
uint64_t lba;
|
682
|
687
|
unsigned long count;
|
683
|
688
|
userptr_t buffer;
|
684
|
689
|
int rc;
|
685
|
690
|
|
|
691
|
+ /* Get buffer size */
|
|
692
|
+ get_real ( bufsize, ix86->segs.ds,
|
|
693
|
+ ( ix86->regs.si + offsetof ( typeof ( addr ), bufsize ) ) );
|
|
694
|
+ if ( bufsize < offsetof ( typeof ( addr ), buffer_phys ) ) {
|
|
695
|
+ DBGC2 ( int13, "<invalid buffer size %#02x\n>\n", bufsize );
|
|
696
|
+ return -INT13_STATUS_INVALID;
|
|
697
|
+ }
|
|
698
|
+
|
686
|
699
|
/* Read parameters from disk address structure */
|
687
|
|
- copy_from_real ( &addr, ix86->segs.ds, ix86->regs.si, sizeof ( addr ));
|
|
700
|
+ memset ( &addr, 0, sizeof ( addr ) );
|
|
701
|
+ copy_from_real ( &addr, ix86->segs.ds, ix86->regs.si, bufsize );
|
688
|
702
|
lba = addr.lba;
|
689
|
|
- count = addr.count;
|
690
|
|
- buffer = real_to_user ( addr.buffer.segment, addr.buffer.offset );
|
|
703
|
+ DBGC2 ( int13, "LBA %08llx <-> ", ( ( unsigned long long ) lba ) );
|
|
704
|
+ if ( ( addr.count == 0xff ) ||
|
|
705
|
+ ( ( addr.buffer.segment == 0xffff ) &&
|
|
706
|
+ ( addr.buffer.offset == 0xffff ) ) ) {
|
|
707
|
+ buffer = phys_to_user ( addr.buffer_phys );
|
|
708
|
+ DBGC2 ( int13, "%08llx",
|
|
709
|
+ ( ( unsigned long long ) addr.buffer_phys ) );
|
|
710
|
+ } else {
|
|
711
|
+ buffer = real_to_user ( addr.buffer.segment,
|
|
712
|
+ addr.buffer.offset );
|
|
713
|
+ DBGC2 ( int13, "%04x:%04x", addr.buffer.segment,
|
|
714
|
+ addr.buffer.offset );
|
|
715
|
+ }
|
|
716
|
+ if ( addr.count <= 0x7f ) {
|
|
717
|
+ count = addr.count;
|
|
718
|
+ } else if ( addr.count == 0xff ) {
|
|
719
|
+ count = addr.long_count;
|
|
720
|
+ } else {
|
|
721
|
+ DBGC2 ( int13, " <invalid count %#02x>\n", addr.count );
|
|
722
|
+ return -INT13_STATUS_INVALID;
|
|
723
|
+ }
|
|
724
|
+ DBGC2 ( int13, " (count %ld)\n", count );
|
691
|
725
|
|
692
|
|
- DBGC2 ( int13, "LBA %08llx <-> %04x:%04x (count %ld)\n",
|
693
|
|
- ( ( unsigned long long ) lba ), addr.buffer.segment,
|
694
|
|
- addr.buffer.offset, count );
|
695
|
|
-
|
696
|
726
|
/* Read from / write to block device */
|
697
|
727
|
if ( ( rc = int13_rw ( int13, lba, count, buffer, block_rw ) ) != 0 ) {
|
698
|
728
|
DBGC ( int13, "INT13 drive %02x extended I/O failed: %s\n",
|
699
|
729
|
int13->drive, strerror ( rc ) );
|
|
730
|
+ /* Record that no blocks were transferred successfully */
|
|
731
|
+ addr.count = 0;
|
|
732
|
+ put_real ( addr.count, ix86->segs.ds,
|
|
733
|
+ ( ix86->regs.si +
|
|
734
|
+ offsetof ( typeof ( addr ), count ) ) );
|
700
|
735
|
return -INT13_STATUS_READ_ERROR;
|
701
|
736
|
}
|
702
|
737
|
|
|
@@ -729,6 +764,117 @@ static int int13_extended_write ( struct int13_drive *int13,
|
729
|
764
|
return int13_extended_rw ( int13, ix86, block_write );
|
730
|
765
|
}
|
731
|
766
|
|
|
767
|
+/**
|
|
768
|
+ * INT 13, 44 - Verify sectors
|
|
769
|
+ *
|
|
770
|
+ * @v int13 Emulated drive
|
|
771
|
+ * @v ds:si Disk address packet
|
|
772
|
+ * @ret status Status code
|
|
773
|
+ */
|
|
774
|
+static int int13_extended_verify ( struct int13_drive *int13,
|
|
775
|
+ struct i386_all_regs *ix86 ) {
|
|
776
|
+ struct int13_disk_address addr;
|
|
777
|
+ uint64_t lba;
|
|
778
|
+ unsigned long count;
|
|
779
|
+
|
|
780
|
+ /* Read parameters from disk address structure */
|
|
781
|
+ if ( DBG_EXTRA ) {
|
|
782
|
+ copy_from_real ( &addr, ix86->segs.ds, ix86->regs.si,
|
|
783
|
+ sizeof ( addr ));
|
|
784
|
+ lba = addr.lba;
|
|
785
|
+ count = addr.count;
|
|
786
|
+ DBGC2 ( int13, "Verify: LBA %08llx (count %ld)\n",
|
|
787
|
+ ( ( unsigned long long ) lba ), count );
|
|
788
|
+ }
|
|
789
|
+
|
|
790
|
+ /* We have no mechanism for verifying sectors */
|
|
791
|
+ return -INT13_STATUS_INVALID;
|
|
792
|
+}
|
|
793
|
+
|
|
794
|
+/**
|
|
795
|
+ * INT 13, 44 - Extended seek
|
|
796
|
+ *
|
|
797
|
+ * @v int13 Emulated drive
|
|
798
|
+ * @v ds:si Disk address packet
|
|
799
|
+ * @ret status Status code
|
|
800
|
+ */
|
|
801
|
+int int13_extended_seek ( struct int13_drive *int13,
|
|
802
|
+ struct i386_all_regs *ix86 ) {
|
|
803
|
+ struct int13_disk_address addr;
|
|
804
|
+ uint64_t lba;
|
|
805
|
+ unsigned long count;
|
|
806
|
+
|
|
807
|
+ /* Read parameters from disk address structure */
|
|
808
|
+ if ( DBG_EXTRA ) {
|
|
809
|
+ copy_from_real ( &addr, ix86->segs.ds, ix86->regs.si,
|
|
810
|
+ sizeof ( addr ));
|
|
811
|
+ lba = addr.lba;
|
|
812
|
+ count = addr.count;
|
|
813
|
+ DBGC2 ( int13, "Seek: LBA %08llx (count %ld)\n",
|
|
814
|
+ ( ( unsigned long long ) lba ), count );
|
|
815
|
+ }
|
|
816
|
+
|
|
817
|
+ /* Ignore and return success */
|
|
818
|
+ return 0;
|
|
819
|
+}
|
|
820
|
+
|
|
821
|
+/**
|
|
822
|
+ * Build device path information
|
|
823
|
+ *
|
|
824
|
+ * @v int13 Emulated drive
|
|
825
|
+ * @v dpi Device path information
|
|
826
|
+ * @ret rc Return status code
|
|
827
|
+ */
|
|
828
|
+static int int13_device_path_info ( struct int13_drive *int13,
|
|
829
|
+ struct edd_device_path_information *dpi ) {
|
|
830
|
+ struct device *device;
|
|
831
|
+ struct device_description *desc;
|
|
832
|
+ unsigned int i;
|
|
833
|
+ uint8_t sum = 0;
|
|
834
|
+ int rc;
|
|
835
|
+
|
|
836
|
+ /* Get underlying hardware device */
|
|
837
|
+ device = identify_device ( &int13->block );
|
|
838
|
+ if ( ! device ) {
|
|
839
|
+ DBGC ( int13, "INT13 drive %02x cannot identify hardware "
|
|
840
|
+ "device\n", int13->drive );
|
|
841
|
+ return -ENODEV;
|
|
842
|
+ }
|
|
843
|
+
|
|
844
|
+ /* Fill in bus type and interface path */
|
|
845
|
+ desc = &device->desc;
|
|
846
|
+ switch ( desc->bus_type ) {
|
|
847
|
+ case BUS_TYPE_PCI:
|
|
848
|
+ dpi->host_bus_type.type = EDD_BUS_TYPE_PCI;
|
|
849
|
+ dpi->interface_path.pci.bus = PCI_BUS ( desc->location );
|
|
850
|
+ dpi->interface_path.pci.slot = PCI_SLOT ( desc->location );
|
|
851
|
+ dpi->interface_path.pci.function = PCI_FUNC ( desc->location );
|
|
852
|
+ dpi->interface_path.pci.channel = 0xff; /* unused */
|
|
853
|
+ break;
|
|
854
|
+ default:
|
|
855
|
+ DBGC ( int13, "INT13 drive %02x unrecognised bus type %d\n",
|
|
856
|
+ int13->drive, desc->bus_type );
|
|
857
|
+ return -ENOTSUP;
|
|
858
|
+ }
|
|
859
|
+
|
|
860
|
+ /* Get EDD block device description */
|
|
861
|
+ if ( ( rc = edd_describe ( &int13->block, &dpi->interface_type,
|
|
862
|
+ &dpi->device_path ) ) != 0 ) {
|
|
863
|
+ DBGC ( int13, "INT13 drive %02x cannot identify block device: "
|
|
864
|
+ "%s\n", int13->drive, strerror ( rc ) );
|
|
865
|
+ return rc;
|
|
866
|
+ }
|
|
867
|
+
|
|
868
|
+ /* Fill in common fields and fix checksum */
|
|
869
|
+ dpi->key = EDD_DEVICE_PATH_INFO_KEY;
|
|
870
|
+ dpi->len = sizeof ( *dpi );
|
|
871
|
+ for ( i = 0 ; i < sizeof ( *dpi ) ; i++ )
|
|
872
|
+ sum += *( ( ( uint8_t * ) dpi ) + i );
|
|
873
|
+ dpi->checksum -= sum;
|
|
874
|
+
|
|
875
|
+ return 0;
|
|
876
|
+}
|
|
877
|
+
|
732
|
878
|
/**
|
733
|
879
|
* INT 13, 48 - Get extended parameters
|
734
|
880
|
*
|
|
@@ -738,21 +884,62 @@ static int int13_extended_write ( struct int13_drive *int13,
|
738
|
884
|
*/
|
739
|
885
|
static int int13_get_extended_parameters ( struct int13_drive *int13,
|
740
|
886
|
struct i386_all_regs *ix86 ) {
|
741
|
|
- struct int13_disk_parameters params = {
|
742
|
|
- .bufsize = sizeof ( params ),
|
743
|
|
- .flags = INT13_FL_DMA_TRANSPARENT,
|
744
|
|
- .cylinders = int13->cylinders,
|
745
|
|
- .heads = int13->heads,
|
746
|
|
- .sectors_per_track = int13->sectors_per_track,
|
747
|
|
- .sectors = int13->capacity.blocks,
|
748
|
|
- .sector_size = int13->capacity.blksize,
|
749
|
|
- };
|
750
|
|
-
|
751
|
|
- DBGC2 ( int13, "Get extended drive parameters to %04x:%04x\n",
|
752
|
|
- ix86->segs.ds, ix86->regs.si );
|
|
887
|
+ struct int13_disk_parameters params;
|
|
888
|
+ struct segoff address;
|
|
889
|
+ size_t len = sizeof ( params );
|
|
890
|
+ uint16_t bufsize;
|
|
891
|
+ int rc;
|
|
892
|
+
|
|
893
|
+ /* Get buffer size */
|
|
894
|
+ get_real ( bufsize, ix86->segs.ds,
|
|
895
|
+ ( ix86->regs.si + offsetof ( typeof ( params ), bufsize )));
|
|
896
|
+
|
|
897
|
+ DBGC2 ( int13, "Get extended drive parameters to %04x:%04x+%02x\n",
|
|
898
|
+ ix86->segs.ds, ix86->regs.si, bufsize );
|
|
899
|
+
|
|
900
|
+ /* Build drive parameters */
|
|
901
|
+ memset ( ¶ms, 0, sizeof ( params ) );
|
|
902
|
+ params.flags = INT13_FL_DMA_TRANSPARENT;
|
|
903
|
+ if ( ( int13->cylinders < 1024 ) &&
|
|
904
|
+ ( int13->capacity.blocks <= INT13_MAX_CHS_SECTORS ) ) {
|
|
905
|
+ params.flags |= INT13_FL_CHS_VALID;
|
|
906
|
+ }
|
|
907
|
+ params.cylinders = int13->cylinders;
|
|
908
|
+ params.heads = int13->heads;
|
|
909
|
+ params.sectors_per_track = int13->sectors_per_track;
|
|
910
|
+ params.sectors = int13->capacity.blocks;
|
|
911
|
+ params.sector_size = int13->capacity.blksize;
|
|
912
|
+ memset ( ¶ms.dpte, 0xff, sizeof ( params.dpte ) );
|
|
913
|
+ if ( ( rc = int13_device_path_info ( int13, ¶ms.dpi ) ) != 0 ) {
|
|
914
|
+ DBGC ( int13, "INT13 drive %02x could not provide device "
|
|
915
|
+ "path information: %s\n",
|
|
916
|
+ int13->drive, strerror ( rc ) );
|
|
917
|
+ len = offsetof ( typeof ( params ), dpi );
|
|
918
|
+ }
|
|
919
|
+
|
|
920
|
+ /* Calculate returned "buffer size" (which will be less than
|
|
921
|
+ * the length actually copied if device path information is
|
|
922
|
+ * present).
|
|
923
|
+ */
|
|
924
|
+ if ( bufsize < offsetof ( typeof ( params ), dpte ) )
|
|
925
|
+ return -INT13_STATUS_INVALID;
|
|
926
|
+ if ( bufsize < offsetof ( typeof ( params ), dpi ) ) {
|
|
927
|
+ params.bufsize = offsetof ( typeof ( params ), dpte );
|
|
928
|
+ } else {
|
|
929
|
+ params.bufsize = offsetof ( typeof ( params ), dpi );
|
|
930
|
+ }
|
|
931
|
+
|
|
932
|
+ DBGC ( int13, "INT 13 drive %02x described using extended "
|
|
933
|
+ "parameters:\n", int13->drive );
|
|
934
|
+ address.segment = ix86->segs.ds;
|
|
935
|
+ address.offset = ix86->regs.si;
|
|
936
|
+ DBGC_HDA ( int13, address, ¶ms, len );
|
|
937
|
+
|
|
938
|
+ /* Return drive parameters */
|
|
939
|
+ if ( len > bufsize )
|
|
940
|
+ len = bufsize;
|
|
941
|
+ copy_to_real ( ix86->segs.ds, ix86->regs.si, ¶ms, len );
|
753
|
942
|
|
754
|
|
- copy_to_real ( ix86->segs.ds, ix86->regs.si, ¶ms,
|
755
|
|
- sizeof ( params ) );
|
756
|
943
|
return 0;
|
757
|
944
|
}
|
758
|
945
|
|
|
@@ -814,6 +1001,12 @@ static __asmcall void int13 ( struct i386_all_regs *ix86 ) {
|
814
|
1001
|
case INT13_EXTENDED_WRITE:
|
815
|
1002
|
status = int13_extended_write ( int13, ix86 );
|
816
|
1003
|
break;
|
|
1004
|
+ case INT13_EXTENDED_VERIFY:
|
|
1005
|
+ status = int13_extended_verify ( int13, ix86 );
|
|
1006
|
+ break;
|
|
1007
|
+ case INT13_EXTENDED_SEEK:
|
|
1008
|
+ status = int13_extended_seek ( int13, ix86 );
|
|
1009
|
+ break;
|
817
|
1010
|
case INT13_GET_EXTENDED_PARAMETERS:
|
818
|
1011
|
status = int13_get_extended_parameters ( int13, ix86 );
|
819
|
1012
|
break;
|