disk: Add logic to determine fastest transfer size
diff --git a/stage23/drivers/disk.s2.c b/stage23/drivers/disk.s2.c
index 523fe86a..29407211 100644
--- a/stage23/drivers/disk.s2.c
+++ b/stage23/drivers/disk.s2.c
@@ -8,7 +8,9 @@
# include <efi.h>
#endif
#include <lib/blib.h>
+#include <lib/print.h>
#include <mm/pmm.h>
+#include <sys/cpu.h>
#if bios == 1
@@ -33,9 +35,56 @@ struct dap {
static struct dap dap = {0};
-#define XFER_BUF_SIZE 16384
+#define XFER_BUF_SIZE 65536
static void *xfer_buf = NULL;
+static size_t fastest_xfer_size(struct volume *volume) {
+ if (xfer_buf == NULL)
+ xfer_buf = conv_mem_alloc(XFER_BUF_SIZE);
+
+ size_t fastest_size = 1;
+ uint64_t last_speed = (uint64_t)-1;
+
+ static const size_t xfer_sizes[] = { 1, 2, 4, 8, 16, 24, 32, 48, 64, 128 };
+
+ for (size_t i = 0; i < SIZEOF_ARRAY(xfer_sizes); i++) {
+ if (xfer_sizes[i] * volume->sector_size > XFER_BUF_SIZE) {
+ break;
+ }
+
+ dap.size = 16;
+ dap.count = xfer_sizes[i];
+ dap.segment = rm_seg(xfer_buf);
+ dap.offset = rm_off(xfer_buf);
+ dap.lba = 0;
+
+ struct rm_regs r = {0};
+ r.eax = 0x4200;
+ r.edx = volume->drive;
+ r.esi = (uint32_t)rm_off(&dap);
+ r.ds = rm_seg(&dap);
+
+ uint64_t start_timestamp = rdtsc();
+ rm_int(0x13, &r, &r);
+ uint64_t end_timestamp = rdtsc();
+
+ if (r.eflags & EFLAGS_CF) {
+ int ah = (r.eax >> 8) & 0xff;
+ printv("Disk error %x. Drive %x", ah, volume->drive);
+ continue;
+ }
+
+ uint64_t speed = (end_timestamp - start_timestamp) / xfer_sizes[i];
+
+ if (speed < last_speed) {
+ last_speed = speed;
+ fastest_size = xfer_sizes[i];
+ }
+ }
+
+ return fastest_size;
+}
+
bool disk_read_sectors(struct volume *volume, void *buf, uint64_t block, size_t count) {
if (count * volume->sector_size > XFER_BUF_SIZE)
panic("XFER");
@@ -111,6 +160,8 @@ void disk_create_index(void) {
}
}
+ block.fastest_xfer_size = 8;
+
volume_count++;
for (int part = 0; ; part++) {
@@ -170,6 +221,8 @@ void disk_create_index(void) {
block->index = hdd_indices++;
}
+ block->fastest_xfer_size = fastest_xfer_size(block);
+
if (gpt_get_guid(&block->guid, block)) {
block->guid_valid = true;
}
@@ -321,6 +374,8 @@ void disk_create_index(void) {
block.first_sect = 0;
block.sect_count = block_io->Media->LastBlock + 1;
+ block.fastest_xfer_size = 8;
+
for (int part = 0; ; part++) {
struct volume trash = {0};
int ret = part_get(&trash, &block, part);
@@ -382,6 +437,9 @@ void disk_create_index(void) {
block->guid_valid = true;
}
+ // TODO: get fastest xfer size also for UEFI?
+ block->fastest_xfer_size = 8;
+
volume_index[volume_index_i++] = block;
for (int part = 0; ; part++) {
diff --git a/stage23/lib/part.h b/stage23/lib/part.h
index 5fbf03d9..72fa2b18 100644
--- a/stage23/lib/part.h
+++ b/stage23/lib/part.h
@@ -20,6 +20,8 @@ struct volume {
int drive;
#endif
+ size_t fastest_xfer_size;
+
int index;
bool is_optical;
diff --git a/stage23/lib/part.s2.c b/stage23/lib/part.s2.c
index 8b53a74e..23e346cf 100644
--- a/stage23/lib/part.s2.c
+++ b/stage23/lib/part.s2.c
@@ -11,8 +11,6 @@
#include <mm/pmm.h>
#include <fs/file.h>
-#define BLOCK_SIZE_IN_SECTORS 8
-
enum {
CACHE_NOT_READY = 0,
CACHE_READY
@@ -26,11 +24,11 @@ static bool cache_block(struct volume *volume, uint64_t block) {
if (volume->cache == NULL)
volume->cache =
- ext_mem_alloc(BLOCK_SIZE_IN_SECTORS * volume->sector_size);
+ ext_mem_alloc(volume->fastest_xfer_size * volume->sector_size);
if (!disk_read_sectors(volume, volume->cache,
- volume->first_sect + block * BLOCK_SIZE_IN_SECTORS,
- BLOCK_SIZE_IN_SECTORS))
+ volume->first_sect + block * volume->fastest_xfer_size,
+ volume->fastest_xfer_size))
return false;
volume->cache_status = CACHE_READY;
@@ -44,7 +42,7 @@ bool volume_read(struct volume *volume, void *buffer, uint64_t loc, uint64_t cou
panic("Attempted volume_read() on pxe");
}
- uint64_t block_size = BLOCK_SIZE_IN_SECTORS * volume->sector_size;
+ uint64_t block_size = volume->fastest_xfer_size * volume->sector_size;
uint64_t progress = 0;
while (progress < count) {
@@ -154,6 +152,7 @@ static int gpt_get_part(struct volume *ret, struct volume *volume, int partition
ret->efi_handle = volume->efi_handle;
#elif bios == 1
ret->drive = volume->drive;
+ ret->fastest_xfer_size = volume->fastest_xfer_size;
#endif
ret->index = volume->index;
ret->is_optical = volume->is_optical;
@@ -214,6 +213,7 @@ static int mbr_get_logical_part(struct volume *ret, struct volume *extended_part
ret->efi_handle = extended_part->efi_handle;
#elif bios == 1
ret->drive = extended_part->drive;
+ ret->fastest_xfer_size = extended_part->fastest_xfer_size;
#endif
ret->index = extended_part->index;
ret->is_optical = extended_part->is_optical;
@@ -292,6 +292,7 @@ static int mbr_get_part(struct volume *ret, struct volume *volume, int partition
extended_part.efi_handle = volume->efi_handle;
#elif bios == 1
extended_part.drive = volume->drive;
+ extended_part.fastest_xfer_size = volume->fastest_xfer_size;
#endif
extended_part.index = volume->index;
extended_part.is_optical = volume->is_optical;
@@ -318,6 +319,7 @@ static int mbr_get_part(struct volume *ret, struct volume *volume, int partition
ret->efi_handle = volume->efi_handle;
#elif bios == 1
ret->drive = volume->drive;
+ ret->fastest_xfer_size = volume->fastest_xfer_size;
#endif
ret->index = volume->index;
ret->is_optical = volume->is_optical;
diff --git a/stage23/sys/cpu.h b/stage23/sys/cpu.h
index 6e6efba3..b913ecc1 100644
--- a/stage23/sys/cpu.h
+++ b/stage23/sys/cpu.h
@@ -154,6 +154,12 @@ static inline void wrmsr(uint32_t msr, uint64_t value) {
: "memory");
}
+inline uint64_t rdtsc(void) {
+ uint32_t edx, eax;
+ asm volatile ("rdtsc" : "=a" (eax), "=d" (edx));
+ return ((uint64_t)edx << 32) | eax;
+}
+
#define write_cr(reg, val) ({ \
asm volatile ("mov %0, %%cr" reg :: "r" (val) : "memory"); \
})
