AHCI drive detection problem on Parallels Desktop 6

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
User avatar
ababo
Member
Member
Posts: 27
Joined: Thu Jun 13, 2013 8:20 am
Location: Ukraine

AHCI drive detection problem on Parallels Desktop 6

Post by ababo »

Hi guys,

I have a weird problem with AHCI drive detection on Parallels Desktop VM. My code follows explanation how to detect drives from http://wiki.osdev.org/AHCI. And it works pretty well on KVM/Qemu and VirtualBox. But on Parallels Desktop 6 I have a virtual drive (SATA 0:1) there which is not detected.

In particular the AHCI HBA is detected as PCI device, but every port from PI (ports implemented) has DET=0 and signature=0xFFFFFFFF. At the same time I know that Linux finds the drive in same VM configuration.

Here is my code:

Code: Select all

#define DRIVE_MAGIC 0x5105820974944592

#define MAX_DRIVES 8
#define MAX_PORTS 32

#define DET_PRESENT 3
#define IPM_ACTIVE 1

#define SIG_ATA 0x00000101
#define SIG_ATAPI 0xEB140101
#define SIG_SEMB 0xC33C0101
#define SIG_PM 0x96690101

struct drive {
  uint64_t magic;
  struct drive *next;
  struct mutex lock;
  int stamp;
  int hba_pci_address : 24;
  int hba_slot : 5;
  int connected : 1;
  int in_use : 1;
  int used : 1;

};

struct port {
  uint32_t clb, clbu, fb, fbu, is, ie, cmd, reserved0, tfd, sig, ssts, sctl;
  uint32_t serr, sact, ci, sntf, fbs;
  uint32_t reserved1[11], vendor[4];
};

struct hba {
  uint32_t cap, ghc, is, pi, vs, ccc_ctl, ccc_pts, em_loc, em_ctl, cap2, bohc;
  uint8_t reserved[0xA0-0x2C], vendor[0x100-0xA0];
  struct port ports[];
};

static struct drive drives[MAX_DRIVES];
static struct ahci_driver driver;
static struct mutex lock;

const struct ahci_driver *get_ahci_driver(void) {
  return &driver;
}

static void next_drive(int device, struct hba *hba, int port) {
  LOG_DEBUG("SATA drive detected on port %d", port);
}

static void next_hba(int device) {
  uint64_t bar5 = read_pci_field(device, PCI_FIELD_BAR5) & ~0x1FFF;
  map_page(bar5, bar5, PAGE_MAPPING_WRITE | PAGE_MAPPING_PWT |
           PAGE_MAPPING_PCD, 0);
  struct hba *hba = (struct hba*)bar5;

  uint32_t val = hba->ghc;
  BIT_ARRAY_SET(&val, 0);
  BIT_ARRAY_SET(&val, 31);
  hba->ghc = val;

  for (uint32_t pi = hba->pi, port = 0; port < MAX_PORTS; port++)
    if (BIT_ARRAY_GET(&pi, port)) {
      uint32_t val = hba->ports[port].cmd;
      BIT_ARRAY_RESET(&val, 0);
      BIT_ARRAY_RESET(&val, 4);
      hba->ports[port].cmd = val;


      uint32_t ssts = hba->ports[port].ssts;
      uint32_t det = ssts & 0x0F, ipm = (ssts >> 8) & 0x0F;
      uint32_t sig = hba->ports[port].sig;
      if (det == DET_PRESENT && ipm == IPM_ACTIVE &&
          sig != SIG_ATAPI && sig != SIG_SEMB && sig != SIG_PM)
        next_drive(device, hba, port);
    }
}

static err_code scan_devices(void) {
  err_code err = ERR_NONE;
  acquire_mutex(&lock);

  for (int i = 0; i < MAX_DRIVES; i++)
    if (drives[i].in_use)
      drives[i].connected = false;

  scan_pci(next_hba, PCI_TYPE_SERIAL_ATA);

  for (int i = 0; i < MAX_DRIVES; i++)
    if (drives[i].in_use && !drives[i].connected) {
      drives[i].in_use = false;

    }

  release_mutex(&lock);
  return err;
}

static err_code get_next_device(device_id *id) {

  return ERR_NO_MORE;
}


void init_ahci(void) {
  memset(drives, 0, sizeof(drives));

  driver.storage_driver.driver = (struct driver) {
    .type = DRIVER_TYPE_STORAGE, .scan_devices = scan_devices,
    .get_next_device = get_next_device,
  };

  if (create_mutex(&lock))
    LOG_ERROR("failed to create mutex")
  else if (scan_devices())
    LOG_ERROR("failed to scan drives")
  else if (add_driver((struct driver*)&driver, NULL))
    LOG_ERROR("failed to add driver")
  else
    LOG_DEBUG("done");
}
Can you suggest my something? Thanks in advance.
Last edited by ababo on Sun Jun 16, 2013 6:07 am, edited 1 time in total.
User avatar
sortie
Member
Member
Posts: 931
Joined: Wed Mar 21, 2012 3:01 pm
Libera.chat IRC: sortie

Re: AHCI drive detection problem on Parallels Desktop 6

Post by sortie »

Have you read the specification and followed how it tells you to initialize controllers carefully? [Sorry, before breakfast, I'll perhaps compare your code with mine later (even though I'm also just starting out)]
User avatar
ababo
Member
Member
Posts: 27
Joined: Thu Jun 13, 2013 8:20 am
Location: Ukraine

Re: AHCI drive detection problem on Parallels Desktop 6

Post by ababo »

Actually detection works well on KVM/Qemu and VirtualBox without any initialization (as I know BIOS must init controller on startup). Only on Parallels I have this problem even with the initialization part.
User avatar
sortie
Member
Member
Posts: 931
Joined: Wed Mar 21, 2012 3:01 pm
Libera.chat IRC: sortie

Re: AHCI drive detection problem on Parallels Desktop 6

Post by sortie »

Yeah, the spec is a bit clear on this. The BIOS must initialize the controller, but you must take over control and finish the initialization yourself. In a worst case you have to do a reset of the controller and start over, but normally you can just continue what the BIOS started.
User avatar
ababo
Member
Member
Posts: 27
Joined: Thu Jun 13, 2013 8:20 am
Location: Ukraine

Re: AHCI drive detection problem on Parallels Desktop 6

Post by ababo »

I reset controller and put it into AHCI mode:

Code: Select all

uint32_t val = hba->ghc;
BIT_ARRAY_SET(&val, 0);
BIT_ARRAY_SET(&val, 31);
hba->ghc = val;
Then I do fore each port mentioned in PI before the actual detection (probing DET):

Code: Select all

uint32_t val = hba->ports[port].cmd;
BIT_ARRAY_RESET(&val, 0);
BIT_ARRAY_RESET(&val, 4);
hba->ports[port].cmd = val;
Where am I wrong?
User avatar
Shaun
Member
Member
Posts: 43
Joined: Mon Sep 17, 2012 3:14 am
Contact:

Re: AHCI drive detection problem on Parallels Desktop 6

Post by Shaun »

ababo wrote:I reset controller and put it into AHCI mode:

Code: Select all

uint32_t val = hba->ghc;
BIT_ARRAY_SET(&val, 0);
BIT_ARRAY_SET(&val, 31);
hba->ghc = val;
Then I do fore each port mentioned in PI before the actual detection (probing DET):

Code: Select all

uint32_t val = hba->ports[port].cmd;
BIT_ARRAY_RESET(&val, 0);
BIT_ARRAY_RESET(&val, 4);
hba->ports[port].cmd = val;
Where am I wrong?
I think you need a check on hba->ghc bit 0 to see whether the bit 0 become zero or not after hardware reset complete

say

if (hba->ghc & 0x01)
{
//reset failed, may be you need do more homework,
//for example, figuring out whether the controller supports PM control or not, you can search linux kernel by
//pci_enable_device() called by ahci_init_one() for more details, the reason why we should check PM control is that some bios(say,thinkpad)
// may initialize the controller power state in D3 cold state when machine starts up, and thus may cause the controller does not work,
//so, we should set the power state in D0 before we do any initialization works, good luck!
//BTW, if you solved this problem, please let me know. :D
}
User avatar
ababo
Member
Member
Posts: 27
Joined: Thu Jun 13, 2013 8:20 am
Location: Ukraine

Re: AHCI drive detection problem on Parallels Desktop 6

Post by ababo »

Unfortunately on Parallels I get ghc reset bit cleared.
User avatar
ababo
Member
Member
Posts: 27
Joined: Thu Jun 13, 2013 8:20 am
Location: Ukraine

Re: AHCI drive detection problem on Parallels Desktop 6

Post by ababo »

ababo wrote://reset failed, may be you need do more homework,
//for example, figuring out whether the controller supports PM control or not, you can search linux kernel by
//pci_enable_device() called by ahci_init_one() for more details, the reason why we should check PM control is that some bios(say,thinkpad)
// may initialize the controller power state in D3 cold state when machine starts up, and thus may cause the controller does not work,
//so, we should set the power state in D0 before we do any initialization works, good luck!
//BTW, if you solved this problem, please let me know.
I have found that my code doesn't work neither on newer KVM/QEMU nor on a real HP hardware. In both cases hba->ghc & 0x01 == 1. Thus I need to perform some initialization myself. I tried to reset ghc bit 0 and to wait for 500ms: didn't help (ghc reset bit remains 1). Two PS (power state) bits are set to 0 which means the HBA is in D0 state. Can you give me some advice?
User avatar
IanSeyler
Member
Member
Posts: 326
Joined: Mon Jul 28, 2008 9:46 am
Location: Ontario, Canada
Contact:

Re: AHCI drive detection problem on Parallels Desktop 6

Post by IanSeyler »

It looks like BareMetal OS has a similar problem with Parallels Desktop 8. The BareMetal AHCI driver works correctly on real hardware, VirtualBox, and QEMU.
BareMetal OS - http://www.returninfinity.com/
Mono-tasking 64-bit OS for x86-64 based computers, written entirely in Assembly
User avatar
ababo
Member
Member
Posts: 27
Joined: Thu Jun 13, 2013 8:20 am
Location: Ukraine

Re: AHCI drive detection problem on Parallels Desktop 6

Post by ababo »

Yes, after a little fix I got my code working on all recent versions of KVM/QEMU, Dropbox and on a real hardware, but not on Parallels Desktop 6.

But how Linux does detect virtual SATA drive on Parallels Desktop? When it starts I can see the drive in Disk Utility (as SATA, not IDE device).
Post Reply