// SPDX-License-Identifier: MPL-2.0 #include "devs/apic/ioapic.h" #include "memory/memory.h" #include "kernel/log.h" typedef struct { sh_ioapic_DEVICE **by_ioapic_id; sh_uint64 max_ioapic_id; sh_uint64 ioapic_count; } ioapic_devs_template; ioapic_devs_template ioapic_devs={0}; static inline sh_uint32 sh_ioapic_read(sh_ioapic_DEVICE *ioapic,sh_uint32 reg) { *(volatile sh_uint32*)((sh_uint8*)ioapic->base+SH_IOAPIC_OFFSET_IOREGSEL)=reg; return *(volatile sh_uint32*)((sh_uint8*)ioapic->base+SH_IOAPIC_OFFSET_IOWIN); } static inline void sh_ioapic_write(sh_ioapic_DEVICE *ioapic,sh_uint32 reg,sh_uint32 value) { *(volatile sh_uint32*)((sh_uint8*)ioapic->base+SH_IOAPIC_OFFSET_IOREGSEL)=reg; *(volatile sh_uint32*)((sh_uint8*)ioapic->base+SH_IOAPIC_OFFSET_IOWIN)=value; } SH_STATUS sh_ioapic_init(sh_uint32 base,sh_uint8 ioapic_id,sh_uint32 gsi_base,sh_ioapic_DEVICE *ioapic) { if (ioapic==SH_NULLPTR || base==0) return SH_STATUS_INVALID_PARAMETER; sh_uint64 page=base& ~0xfffull; SH_STATUS status=sh_memory_identity_map(page,1,SH_PAGE_RW | SH_PAGE_PRESENT | SH_PAGE_NX | SH_PAGE_PCD); if (sh_status_error(status) && status!=SH_STATUS_ERROR_VA_FULLY_MAPPED) return status; ioapic->base=(volatile sh_uint32*)(sh_uint64)base; ioapic->ioapic_id=ioapic_id; ioapic->gsi_base=gsi_base; ioapic->gsi_count=0; ioapic->lock=SH_LOCK_LOCAL(); sh_uint32 ver=sh_ioapic_read(ioapic,SH_IOAPIC_REGISTER_IOAPICVER); sh_uint32 max_redir=(ver>>16)&0xFF; ioapic->gsi_count=max_redir+1; sh_log_fdebug(SH_LOG_SOURCE_ACPI,"IOAPIC with id %1u: [%4u - %4u]\n",ioapic->ioapic_id,ioapic->gsi_base,ioapic->gsi_base+ioapic->gsi_count-1); sh_uint32 id_reg=sh_ioapic_read(ioapic,SH_IOAPIC_REGISTER_IOAPICID); sh_uint8 hw_id=(id_reg>>24)&0x0F; if (hw_id!=ioapic_id) { sh_log_fwarning(SH_LOG_SOURCE_ACPI,"IOAPIC of IOAPIC id %1u doesn't provide the same id in his hardware register: %4u.\n",ioapic_id,hw_id); } return SH_STATUS_SUCCESS; } SH_STATUS sh_ioapic_init_devs(sh_uint8 max_ioapic_id) { sh_ioapic_DEVICE **ioapic_ptr_by_ioapic_id=sh_malloc((max_ioapic_id+1)*sizeof(sh_ioapic_DEVICE*)); if (!ioapic_ptr_by_ioapic_id) return SH_STATUS_ERROR_NULLPTR_RETURNED; sh_mem_set_8((sh_uint8*)ioapic_ptr_by_ioapic_id,0x0,(max_ioapic_id+1)*sizeof(sh_ioapic_DEVICE*)); ioapic_devs.by_ioapic_id=ioapic_ptr_by_ioapic_id; ioapic_devs.max_ioapic_id=max_ioapic_id; return SH_STATUS_SUCCESS; } SH_STATUS sh_ioapic_bind(sh_ioapic_DEVICE *ioapic_dev) { if (ioapic_dev==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; if (ioapic_devs.by_ioapic_id==SH_NULLPTR) return SH_STATUS_DEVS_NOT_INITIALIZED; ioapic_devs.by_ioapic_id[ioapic_dev->ioapic_id]=ioapic_dev; ioapic_devs.ioapic_count++; return SH_STATUS_SUCCESS; } sh_ioapic_DEVICE *sh_ioapic_get_dev_ioapic_id(sh_uint64 ioapic_id) { if (ioapic_id>ioapic_devs.max_ioapic_id) return SH_NULLPTR; return ioapic_devs.by_ioapic_id[ioapic_id]; } SH_STATUS sh_ioapic_read_ioredtbl_entry(sh_ioapic_DEVICE *ioapic_dev,sh_uint32 gsi,sh_ioapic_IOREDTBL_ENTRY *entry) { if (!ioapic_dev || !entry) return SH_STATUS_INVALID_PARAMETER; if (gsigsi_base) return SH_STATUS_INVALID_PARAMETER; sh_uint32 index=gsi-ioapic_dev->gsi_base; if (index>=ioapic_dev->gsi_count) return SH_STATUS_INVALID_PARAMETER; sh_uint32 low_reg=SH_IOAPIC_REGISTER_IOREDTBL_BASE+(index*2); sh_uint32 high_reg=low_reg+1; sh_spin_lock(&ioapic_dev->lock); entry->low=sh_ioapic_read(ioapic_dev,low_reg); entry->high=sh_ioapic_read(ioapic_dev,high_reg); sh_spin_unlock(&ioapic_dev->lock); return SH_STATUS_SUCCESS; } SH_STATUS sh_ioapic_mask_gsi(sh_ioapic_DEVICE *dev,sh_uint32 gsi) { if (!dev) return SH_STATUS_INVALID_PARAMETER; sh_ioapic_IOREDTBL_ENTRY e; SH_STATUS status=sh_ioapic_read_ioredtbl_entry(dev,gsi,&e); if (status!=SH_STATUS_SUCCESS) return status; e.low|=(1<<16); return sh_ioapic_write_ioredtbl_entry(dev,gsi,&e); } SH_STATUS sh_ioapic_unmask_gsi(sh_ioapic_DEVICE *dev,sh_uint32 gsi) { if (!dev) return SH_STATUS_INVALID_PARAMETER; sh_ioapic_IOREDTBL_ENTRY e; SH_STATUS status=sh_ioapic_read_ioredtbl_entry(dev,gsi,&e); if (status!=SH_STATUS_SUCCESS) return status; e.low&= ~(1U<<16); return sh_ioapic_write_ioredtbl_entry(dev,gsi,&e); } SH_STATUS sh_ioapic_write_ioredtbl_entry(sh_ioapic_DEVICE *ioapic_dev,sh_uint32 gsi,sh_ioapic_IOREDTBL_ENTRY *entry) { if (!ioapic_dev || !entry) return SH_STATUS_INVALID_PARAMETER; if (gsigsi_base) return SH_STATUS_INVALID_PARAMETER; sh_uint32 index=gsi-ioapic_dev->gsi_base; if (index>=ioapic_dev->gsi_count) return SH_STATUS_INVALID_PARAMETER; sh_uint32 low_reg=SH_IOAPIC_REGISTER_IOREDTBL_BASE+(index*2); sh_uint32 high_reg=low_reg+1; sh_spin_lock(&ioapic_dev->lock); sh_ioapic_write(ioapic_dev,high_reg,entry->high); sh_ioapic_write(ioapic_dev,low_reg,entry->low); sh_spin_unlock(&ioapic_dev->lock); return SH_STATUS_SUCCESS; } sh_ioapic_DEVICE *sh_ioapic_get_dev_by_gsi(sh_uint32 gsi) { if (ioapic_devs.by_ioapic_id==SH_NULLPTR) return SH_NULLPTR; for (sh_uint64 i=0;i<=ioapic_devs.max_ioapic_id;i++) { sh_ioapic_DEVICE *dev=ioapic_devs.by_ioapic_id[i]; if (!dev) continue; if (gsi>=dev->gsi_base && gsi<(dev->gsi_base+dev->gsi_count)) { return dev; } } return SH_NULLPTR; } SH_STATUS sh_ioapic_mask_all() { if (ioapic_devs.by_ioapic_id==SH_NULLPTR) return SH_STATUS_DEVS_NOT_INITIALIZED; for (sh_uint64 i=0;i<=ioapic_devs.max_ioapic_id;i++) { sh_ioapic_DEVICE *dev=ioapic_devs.by_ioapic_id[i]; if (!dev) continue; for (sh_uint32 j=0;jgsi_count;j++) { sh_uint32 gsi=dev->gsi_base+j; SH_STATUS status=sh_ioapic_mask_gsi(dev,gsi); if (sh_status_error(status)) return status; } } return SH_STATUS_SUCCESS; } SH_STATUS sh_ioapic_devs_query(char *sub_path,sh_devs_RESULT *result) { if (sub_path==SH_NULLPTR || result==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; sh_uint64 path_len=sh_string_len(sub_path); if (path_len==0 || path_len>256) return SH_STATUS_INVALID_PARAMETER; if (ioapic_devs.by_ioapic_id==SH_NULLPTR) return SH_STATUS_DEVS_NOT_INITIALIZED; if (sh_string_compare(sub_path,"/count",6)) { if (path_len==6) { result->type=SH_DEVS_VALUE; result->value=ioapic_devs.ioapic_count; return SH_STATUS_SUCCESS; } else { return SH_STATUS_NOT_FOUND; } } else if (sh_string_compare(sub_path,"/max-ioapic-id",14)) { if (path_len==14) { result->type=SH_DEVS_VALUE; result->value=ioapic_devs.max_ioapic_id; return SH_STATUS_SUCCESS; } else { return SH_STATUS_NOT_FOUND; } } else if (sh_string_compare(sub_path,"/by-ioapic-id/",14)) { if (path_len>14 && path_len<=34) { sh_uint64 remaining_text=sh_string_len(sub_path)-sh_string_len("/by-ioapic-id/"); char ioapic_id_str[22]; sh_string_substring(sub_path,sh_string_len("/by-ioapic-id/"),remaining_text,ioapic_id_str); sh_uint64 ioapic_id=sh_string_to_uint64(ioapic_id_str); if (ioapic_id>ioapic_devs.max_ioapic_id) return SH_STATUS_NOT_FOUND; result->type=SH_DEVS_IOAPIC; result->value=(sh_uint64)ioapic_devs.by_ioapic_id[ioapic_id]; return SH_STATUS_SUCCESS; } else { return SH_STATUS_NOT_FOUND; } } else if (sh_string_compare(sub_path,"/by-gsi/",8)) { if (path_len>8 && path_len<=28) { sh_uint64 remaining_text=sh_string_len(sub_path)-sh_string_len("/by-gsi/"); char gsi_str[22]; sh_string_substring(sub_path,sh_string_len("/by-gsi/"),remaining_text,gsi_str); sh_uint64 gsi=sh_string_to_uint64(gsi_str); result->type=SH_DEVS_IOAPIC; result->value=(sh_uint64)sh_ioapic_get_dev_by_gsi((sh_uint32)gsi); return SH_STATUS_SUCCESS; } else { return SH_STATUS_NOT_FOUND; } } else { return SH_STATUS_NOT_FOUND; } return SH_STATUS_SUCCESS; }