/*-
 * Copyright (c) 1999 Takanori Watanabe
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	$Id: acpitblrd.c,v 1.9 1999/04/27 01:49:39 takawata Exp $
 */
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#define MEMDEVICE "/dev/mem"
#define BIOS_START 0xa0000
#define BIOS_END 0x100000
#define BIOS_ALIGN 16 
#include "aml_elem.h"
#include "aml_parse.h"
int fd;
#define GETVAL32(a) (a)[0]+(a)[1]*0x100+(a)[2]*0x10000+(a)[3]*0x1000000
#define GETVAL16(a) (a)[0]+(a)[1]*0x100
#define PRINTFIX(a) do{\
                       int k;\
	               for(k=0;k<sizeof(a);k++)\
			 putchar(a[k]);\
		       putchar('\n');\
		       }while(0)
#define DUMPFIX(a) do{\
                       int k;\
	               for(k=0;k<sizeof(a);k++)\
			 printf("%2x ",a[k]);\
		       putchar('\n');\
		       }while(0)
struct root_system_desc{
  u_char signature[8];
  u_char sum;
  u_char oem[6];
  u_char res;
  u_char addr[4];
};
struct SDT{
  u_char signature[4];
  u_char len[4];
  u_char rev;
  u_char check;
  u_char oemid[6];
  u_char oemtblid[8];
  u_char oemrev[4];
  u_char creator[4];
  u_char crerev[4];
#define SIZEOF_SDT_HDR 36 /*struct size except body*/
  void *body;
};
#define ADDR_SIZE 4
struct FACP{
  u_char facs_ptr[4];
  u_char dsdt_ptr[4];
  u_char int_model;
  u_char reserved1;
  u_char sci_int[2];
  u_char smi_cmd[4];
  u_char acpi_enable;
  u_char acpi_disable;
  u_char s4biosreq;
  u_char reserved15;
  u_char pm1a_evtblk[4];
  u_char pm1b_evtblk[4];
  u_char pm1a_cntblk[4];
  u_char pm1b_cntblk[4];
  u_char pm2_cntblk[4];
  u_char pm_tmr_blk[4];
  u_char gpe0_blk[4];
  u_char gpe1_blk[4];
  u_char pm1_evtlen;
  u_char pm1_cntlen;
  u_char pm2_cntlen;
  u_char pm_tmr_len;
  u_char gpe0_len;
  u_char gpe1_len;
  u_char gpe1_base;
  u_char reserved2;
  u_char p_lvl2_lat[2];
  u_char p_lvl3_lat[2];
  u_char flash_size[2];
  u_char flash_stride[2];
  u_char duty_off;
  u_char duty_width;
  u_char day_alrm;
  u_char mon_alrm;
  u_char century;
  u_char reserved3;
  u_char flags[4];
};
struct FACS{
  char signature[4];
  char len[4];
  char hard_sig[4];
  char firm_wake_vec[4];
  char g_lock[4];
  char flags[4];
  char reserved[40];
};
struct facpdata{
  struct FACP *raw;
  struct SDT *dsdt;
  struct FACS *facs;
};
struct SDT *get_sdt(long off);
typedef int (*sdtfunc)(struct SDT *);
int defaultfunc(struct SDT *);
int facpfunc(struct SDT *);
int dsdtfunc(struct SDT *);
struct {
  char signature[4];
  sdtfunc func;
}proctable[]={
  {"APIC",defaultfunc},
  {"DSDT",dsdtfunc},
  {"FACP",facpfunc},
  {"PSDT",defaultfunc},
  {"RSDT",defaultfunc},
  {"FACS",defaultfunc},
  {"",NULL}
};

struct FACS *get_facs(long off){
  struct FACS *facs;
  lseek(fd,off,SEEK_SET);
  facs=malloc(sizeof(struct FACS));
  read(fd,facs,sizeof(struct FACS));
  if(bcmp(facs,"FACS",4)!=0){
    free(facs);
    return NULL;
  }
  return facs;
}
int dsdtfunc(struct SDT *dsdt){
  FILE *fp;
  char *data;
  struct aml_env aenv;
  int i;
  data=dsdt->body;
  printf("Differentiated System Description Table :\n");
  printsdt(dsdt);
#if 0
  fp=fopen("dsdt.dat","w+");
  for(i=0;i<GETVAL32(dsdt->len)-SIZEOF_SDT_HDR;i++){
    putc(data[i],fp);
  }
  fclose(fp);
#else
  aenv.code=data;
  aenv.length=data+GETVAL32(dsdt->len)-SIZEOF_SDT_HDR;
  aenv.path="\\";
  TermParse(&aenv);
#endif
}
int facsfunc(struct FACS *facs){
  if(facs==NULL){
    fprintf(stderr,"FACS corrupted\n");
    return -1;
  }
  printf("Firmware ACPI Control Status:\n");
  PRINTFIX(facs->hard_sig);
  DUMPFIX(facs->firm_wake_vec);
  printf("GlobalLock:%x Flag:%x\n",GETVAL32(facs->g_lock),
	 GETVAL32(facs->flags));
  return 0;
}
int facpfunc(struct SDT *sdt){
  int i;
  struct FACP *facp;
  struct facpdata *facpdat;
  printf("Fixed ACPI Control Table:\n");
  printsdt(sdt);
  facpdat=malloc(sizeof(struct facpdata));
  facp=sdt->body;
  printf("facs_ptr:%x\n",GETVAL32(facp->facs_ptr));
  printf("dsdt_ptr:%x\n",GETVAL32(facp->dsdt_ptr));
  printf("int_model: %s\n",(facp->int_model==0)?"Dual PIC":"Multiple APIC.");
  printf("sci int:%x\n",GETVAL16(facp->sci_int));
  printf("smi command port:%x\n",GETVAL32(facp->smi_cmd));
  printf("acpi_enable:%x\n",facp->acpi_enable);
  printf("acpi_disable:%x\n",facp->acpi_disable);
  printf("s4biosreq:%x\n",facp->s4biosreq);
  printf("pm1a register block:%x\n",GETVAL32(facp->pm1a_evtblk));
  printf("pm1b register block:%x\n",GETVAL32(facp->pm1b_evtblk));
  printf("pm1a control block:%x\n",GETVAL32(facp->pm1a_cntblk));
  printf("pm1b control block:%x\n",GETVAL32(facp->pm1b_cntblk));
  printf("pm2 control block:%x\n",GETVAL32(facp->pm2_cntblk));
  printf("pm timer block:%x\n",GETVAL32(facp->pm_tmr_blk));
  printf("GP0 register block:%x\n",GETVAL32(facp->gpe0_blk));
  printf("GP1 register block:%x\n",GETVAL32(facp->gpe1_blk));
  printf("pm1 register len:%x\n",facp->pm1_evtlen);
  printf("pm1 control len:%x\n",facp->pm1_cntlen);
  printf("pm2 control len:%x\n",facp->pm2_cntlen);
  printf("pm timer len:%x\n",facp->pm_tmr_len);
  printf("gpe0 len:%x\n",facp->gpe0_len);
  printf("gpe1 len:%x\n",facp->gpe1_len);
  printf("gpe1 base:%x\n",facp->gpe1_base);
  printf("p_lvl2_lat:%d ms\n",GETVAL16(facp->p_lvl2_lat));
  printf("p_lvl3_lat:%d ms\n",GETVAL16(facp->p_lvl3_lat));
  printf("flash_size:%d \n",GETVAL16(facp->flash_size));
  printf("flash_stride:%d \n",GETVAL16(facp->flash_stride));
  printf("duty offset %d\n",facp->duty_off);
  printf("duty width %d\n",facp->duty_width );
  printf("flag %x\n",facp->flags);
  sdt->body=facpdat;
  facpdat->raw=facp;
  facpdat->dsdt=get_sdt(GETVAL32(facp->dsdt_ptr));
  facpdat->facs=get_facs(GETVAL32(facp->facs_ptr));
  printf("lvl2lat:%d lvl3lat:%d\n",GETVAL16(facp->p_lvl2_lat),
	 GETVAL16(facp->p_lvl3_lat));
  printf("==================\n");
  facsfunc(facpdat->facs);
  printf("==================\n");
  dsdtfunc(facpdat->dsdt);
}
int printsdt(struct SDT *sdt){
  int i;
  printf("size:%d\n",GETVAL32(sdt->len));
  printf("oemid:");
  PRINTFIX(sdt->oemid);  
  printf("Product:");
  PRINTFIX(sdt->oemtblid);
  printf("OEM Revision:");
  DUMPFIX(sdt->oemrev);
  printf("Creator:");
  PRINTFIX(sdt->creator);
  printf("Creator Revision:");
  DUMPFIX(sdt->crerev);
  printf("\n");
}
int defaultfunc(struct SDT *sdt){
  int i;
  unsigned char *body;
  body=sdt->body;
  printf("Unknown Block:\nSignature:");
  PRINTFIX(sdt->signature);
  printsdt(sdt);

  for(i=0;i<(GETVAL32(sdt->len)-SIZEOF_SDT_HDR);i++){
    printf("%2x ",body[i]);
  }
  printf("\n");
  return 0; 
}
struct SDT *get_sdt(long off)
{
  struct SDT *res=NULL;
  char *body=NULL;
  u_char cksm,*ckbf=NULL;
  int size,i;
  res=malloc(sizeof(struct SDT));
  if(lseek(fd,off,SEEK_SET)==-1)
    goto error;
  if(read(fd,res,SIZEOF_SDT_HDR)!=SIZEOF_SDT_HDR)
     goto error;
  size=GETVAL32(res->len);
  body=malloc(size);
  if(read(fd,body,size-SIZEOF_SDT_HDR)!=(size-SIZEOF_SDT_HDR))
    goto error;
  for(cksm=0,ckbf=(char *)res,i=0;i<SIZEOF_SDT_HDR;i++){
    cksm+=ckbf[i];
  }
  for(ckbf=body,i=0;i<size-SIZEOF_SDT_HDR;i++){
    cksm+=ckbf[i];
  }
  if(cksm==0){
    res->body=body;
    return res;
  }
error:
  free(body);
  free(res);
  return NULL;
}
main()
{

  int seek_offset,i,sdts;
  struct root_system_desc rsdp;
  u_char cksm;
  u_char *generic_buffer;
  struct SDT *rsdt,**sdt_array;
  if((fd=open(MEMDEVICE,O_RDONLY))==-1){
    perror("open");
    exit(-1);
  }
  for(seek_offset=BIOS_START;seek_offset<BIOS_END;seek_offset+=BIOS_ALIGN){
    lseek(fd,seek_offset,SEEK_SET);
    read(fd,&rsdp,sizeof(rsdp));
    if(bcmp(rsdp.signature,"RSD PTR ",sizeof(rsdp.signature))!=0){
      continue;
    }
    for(generic_buffer=(char *)&rsdp,cksm=0,i=0;i<sizeof(rsdp);i++){
      cksm+=generic_buffer[i];
    }
    if(cksm==0){
      break;
    }
  }
  if(seek_offset>=0x100000){
    fprintf(stderr,"NO RSD PTR Signature Found%x\n",seek_offset);
    exit(-1);
  }
  if((rsdt=get_sdt(GETVAL32(rsdp.addr)))==NULL){
    fprintf(stderr,"Cannot read block. may be already clobbered\n");
    exit(-1);
  }else if(bcmp(rsdt->signature,"RSDT",sizeof(rsdt->signature))!=0){
    fprintf(stderr,"Not valid signature\n");
    exit(-1);
  }
  generic_buffer=rsdt->body;
  sdts=(GETVAL32(rsdt->len)-SIZEOF_SDT_HDR)/ADDR_SIZE;
  sdt_array=calloc(sizeof(struct SDT *),sdts);
  for(i=0;i<sdts;i++){
    sdt_array[i]=get_sdt(GETVAL32(generic_buffer+i*ADDR_SIZE));
  }
  free(rsdt->body);
  rsdt->body=sdt_array;
  for(i=0;i<sdts;i++){
    int j;
    printf("==================\n");
    for(j=0;proctable[j].func!=NULL;j++){
      if(bcmp(proctable[j].signature,sdt_array[i]->signature,4)==0){
	(*proctable[j].func)(sdt_array[i]);
	break;
      }
    }
    if(proctable[j].func==NULL){
      printf("FALL BACK TO DEFAULT\n");
      defaultfunc(sdt_array[i]);
    }
  }
}
  

