Re: [PATCH v3 4/5] efi: call get_event_log before ExitBootServices
t; +#include > + > +/* > + * Reserve the memory associated with the TPM Event Log configuration table. > + */ > +int __init efi_tpm_eventlog_init(void) > +{ > + struct linux_efi_tpm_eventlog *log_tbl; > + unsigned int tbl_size; > + > + if (efi.tpm_log == EFI_INVALID_TABLE_ADDR) > + return 0; > + > + log_tbl = early_memremap(efi.tpm_log, sizeof(*log_tbl)); > + if (!log_tbl) { > + pr_err("Failed to map TPM Event Log table @ 0x%lx\n", > + efi.tpm_log); > + efi.tpm_log = EFI_INVALID_TABLE_ADDR; > + return -ENOMEM; > + } > + > + tbl_size = sizeof(*log_tbl) + log_tbl->size; > + memblock_reserve(efi.tpm_log, tbl_size); > + early_memunmap(log_tbl, sizeof(*log_tbl)); > + return 0; > +} > + > diff --git a/include/linux/efi.h b/include/linux/efi.h > index 8dc3d94a3e3c..c5805eb601b1 100644 > --- a/include/linux/efi.h > +++ b/include/linux/efi.h > @@ -472,6 +472,39 @@ typedef struct { > u64 get_all; > } apple_properties_protocol_64_t; > > +typedef struct { > + u32 get_capability; > + u32 get_event_log; > + u32 hash_log_extend_event; > + u32 submit_command; > + u32 get_active_pcr_banks; > + u32 set_active_pcr_banks; > + u32 get_result_of_set_active_pcr_banks; > +} efi_tcg2_protocol_32_t; > + > +typedef struct { > + u64 get_capability; > + u64 get_event_log; > + u64 hash_log_extend_event; > + u64 submit_command; > + u64 get_active_pcr_banks; > + u64 set_active_pcr_banks; > + u64 get_result_of_set_active_pcr_banks; > +} efi_tcg2_protocol_64_t; > + > +typedef u32 efi_tcg2_event_log_format; > + > +typedef struct { > + void *get_capability; > + efi_status_t (*get_event_log)(efi_handle_t, efi_tcg2_event_log_format, > + efi_physical_addr_t *, efi_physical_addr_t *, efi_bool_t *); > + void *hash_log_extend_event; > + void *submit_command; > + void *get_active_pcr_banks; > + void *set_active_pcr_banks; > + void *get_result_of_set_active_pcr_banks; > +} efi_tcg2_protocol_t; > + > /* > * Types and defines for EFI ResetSystem > */ > @@ -622,6 +655,7 @@ void efi_native_runtime_setup(void); > #define EFI_MEMORY_ATTRIBUTES_TABLE_GUID EFI_GUID(0xdcfa911d, 0x26eb, > 0x469f, 0xa2, 0x20, 0x38, 0xb7, 0xdc, 0x46, 0x12, 0x20) > #define EFI_CONSOLE_OUT_DEVICE_GUIDEFI_GUID(0xd3b36f2c, 0xd551, > 0x11d4, 0x9a, 0x46, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d) > #define APPLE_PROPERTIES_PROTOCOL_GUID EFI_GUID(0x91bd12fe, 0xf6c3, > 0x44fb, 0xa5, 0xb7, 0x51, 0x22, 0xab, 0x30, 0x3a, 0xe0) > +#define EFI_TCG2_PROTOCOL_GUID EFI_GUID(0x607f766c, 0x7455, > 0x42be, 0x93, 0x0b, 0xe4, 0xd7, 0x6d, 0xb2, 0x72, 0x0f) > > #define EFI_IMAGE_SECURITY_DATABASE_GUID EFI_GUID(0xd719b2cb, 0x3d3a, > 0x4596, 0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f) > #define EFI_SHIM_LOCK_GUID EFI_GUID(0x605dab50, 0xe046, > 0x4300, 0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23) > @@ -634,6 +668,7 @@ void efi_native_runtime_setup(void); > #define LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID EFI_GUID(0xe03fc20a, 0x85dc, > 0x406e, 0xb9, 0x0e, 0x4a, 0xb5, 0x02, 0x37, 0x1d, 0x95) > #define LINUX_EFI_LOADER_ENTRY_GUIDEFI_GUID(0x4a67b082, 0x0a4c, > 0x41cf, 0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f) > #define LINUX_EFI_RANDOM_SEED_TABLE_GUID EFI_GUID(0x1ce1e5bc, 0x7ceb, > 0x42f2, 0x81, 0xe5, 0x8a, 0xad, 0xf1, 0x80, 0xf5, 0x7b) > +#define LINUX_EFI_TPM_EVENT_LOG_GUID EFI_GUID(0xb7799cb0, 0xeca2, > 0x4943, 0x96, 0x67, 0x1f, 0xae, 0x07, 0xb7, 0x47, 0xfa) > > typedef struct { > efi_guid_t guid; > @@ -908,6 +943,7 @@ extern struct efi { > unsigned long properties_table; /* properties table */ > unsigned long mem_attr_table; /* memory attributes table */ > unsigned long rng_seed; /* UEFI firmware random seed */ > + unsigned long tpm_log; /* TPM2 Event Log table */ > efi_get_time_t *get_time; > efi_set_time_t *set_time; > efi_get_wakeup_time_t *get_wakeup_time; > @@ -1504,6 +1540,8 @@ static inline void > efi_enable_reset_attack_mitigation(efi_system_table_t *sys_table_arg) { } > #endif > > +void efi_retrieve_tpm2_eventlog(efi_system_table_t *sys_table); > + > /* > * Arch code can implement the following three template macros, avoiding > * reptition for the void/non-void return cases of {__,}efi_call_virt(): > @@ -1571,4 +1609,12 @@ struct linux_efi_random_seed { > u8 bits[]; > }; > > +struct linux_efi_tpm_eventlog { > + u32 size; > + u8 version; > + u8 log[]; > +}; > + > +extern int efi_tpm_eventlog_init(void); > + > #endif /* _LINUX_EFI_H */ > -- > 2.14.1.821.g8fa685d3b7-goog > -- Marc-André Lureau
Re: [PATCH v3 4/5] efi: call get_event_log before ExitBootServices
+int __init efi_tpm_eventlog_init(void) > +{ > + struct linux_efi_tpm_eventlog *log_tbl; > + unsigned int tbl_size; > + > + if (efi.tpm_log == EFI_INVALID_TABLE_ADDR) > + return 0; > + > + log_tbl = early_memremap(efi.tpm_log, sizeof(*log_tbl)); > + if (!log_tbl) { > + pr_err("Failed to map TPM Event Log table @ 0x%lx\n", > + efi.tpm_log); > + efi.tpm_log = EFI_INVALID_TABLE_ADDR; > + return -ENOMEM; > + } > + > + tbl_size = sizeof(*log_tbl) + log_tbl->size; > + memblock_reserve(efi.tpm_log, tbl_size); > + early_memunmap(log_tbl, sizeof(*log_tbl)); > + return 0; > +} > + > diff --git a/include/linux/efi.h b/include/linux/efi.h > index 8dc3d94a3e3c..c5805eb601b1 100644 > --- a/include/linux/efi.h > +++ b/include/linux/efi.h > @@ -472,6 +472,39 @@ typedef struct { > u64 get_all; > } apple_properties_protocol_64_t; > > +typedef struct { > + u32 get_capability; > + u32 get_event_log; > + u32 hash_log_extend_event; > + u32 submit_command; > + u32 get_active_pcr_banks; > + u32 set_active_pcr_banks; > + u32 get_result_of_set_active_pcr_banks; > +} efi_tcg2_protocol_32_t; > + > +typedef struct { > + u64 get_capability; > + u64 get_event_log; > + u64 hash_log_extend_event; > + u64 submit_command; > + u64 get_active_pcr_banks; > + u64 set_active_pcr_banks; > + u64 get_result_of_set_active_pcr_banks; > +} efi_tcg2_protocol_64_t; > + > +typedef u32 efi_tcg2_event_log_format; > + > +typedef struct { > + void *get_capability; > + efi_status_t (*get_event_log)(efi_handle_t, efi_tcg2_event_log_format, > + efi_physical_addr_t *, efi_physical_addr_t *, efi_bool_t *); > + void *hash_log_extend_event; > + void *submit_command; > + void *get_active_pcr_banks; > + void *set_active_pcr_banks; > + void *get_result_of_set_active_pcr_banks; > +} efi_tcg2_protocol_t; > + > /* > * Types and defines for EFI ResetSystem > */ > @@ -622,6 +655,7 @@ void efi_native_runtime_setup(void); > #define EFI_MEMORY_ATTRIBUTES_TABLE_GUID EFI_GUID(0xdcfa911d, 0x26eb, > 0x469f, 0xa2, 0x20, 0x38, 0xb7, 0xdc, 0x46, 0x12, 0x20) > #define EFI_CONSOLE_OUT_DEVICE_GUIDEFI_GUID(0xd3b36f2c, 0xd551, > 0x11d4, 0x9a, 0x46, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d) > #define APPLE_PROPERTIES_PROTOCOL_GUID EFI_GUID(0x91bd12fe, 0xf6c3, > 0x44fb, 0xa5, 0xb7, 0x51, 0x22, 0xab, 0x30, 0x3a, 0xe0) > +#define EFI_TCG2_PROTOCOL_GUID EFI_GUID(0x607f766c, 0x7455, > 0x42be, 0x93, 0x0b, 0xe4, 0xd7, 0x6d, 0xb2, 0x72, 0x0f) > > #define EFI_IMAGE_SECURITY_DATABASE_GUID EFI_GUID(0xd719b2cb, 0x3d3a, > 0x4596, 0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f) > #define EFI_SHIM_LOCK_GUID EFI_GUID(0x605dab50, 0xe046, > 0x4300, 0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23) > @@ -634,6 +668,7 @@ void efi_native_runtime_setup(void); > #define LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID EFI_GUID(0xe03fc20a, 0x85dc, > 0x406e, 0xb9, 0x0e, 0x4a, 0xb5, 0x02, 0x37, 0x1d, 0x95) > #define LINUX_EFI_LOADER_ENTRY_GUIDEFI_GUID(0x4a67b082, 0x0a4c, > 0x41cf, 0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f) > #define LINUX_EFI_RANDOM_SEED_TABLE_GUID EFI_GUID(0x1ce1e5bc, 0x7ceb, > 0x42f2, 0x81, 0xe5, 0x8a, 0xad, 0xf1, 0x80, 0xf5, 0x7b) > +#define LINUX_EFI_TPM_EVENT_LOG_GUID EFI_GUID(0xb7799cb0, 0xeca2, > 0x4943, 0x96, 0x67, 0x1f, 0xae, 0x07, 0xb7, 0x47, 0xfa) > > typedef struct { > efi_guid_t guid; > @@ -908,6 +943,7 @@ extern struct efi { > unsigned long properties_table; /* properties table */ > unsigned long mem_attr_table; /* memory attributes table */ > unsigned long rng_seed; /* UEFI firmware random seed */ > + unsigned long tpm_log; /* TPM2 Event Log table */ > efi_get_time_t *get_time; > efi_set_time_t *set_time; > efi_get_wakeup_time_t *get_wakeup_time; > @@ -1504,6 +1540,8 @@ static inline void > efi_enable_reset_attack_mitigation(efi_system_table_t *sys_table_arg) { } > #endif > > +void efi_retrieve_tpm2_eventlog(efi_system_table_t *sys_table); > + > /* > * Arch code can implement the following three template macros, avoiding > * reptition for the void/non-void return cases of {__,}efi_call_virt(): > @@ -1571,4 +1609,12 @@ struct linux_efi_random_seed { > u8 bits[]; > }; > > +struct linux_efi_tpm_eventlog { > + u32 size; > + u8 version; > + u8 log[]; > +}; > + > +extern int efi_tpm_eventlog_init(void); > + > #endif /* _LINUX_EFI_H */ > -- > 2.14.1.821.g8fa685d3b7-goog > -- Marc-André Lureau
Re: [PATCH v16 09/11] crash: export paddr_vmcoreinfo_note()
Hi On Fri, Mar 2, 2018 at 7:03 PM, Michael S. Tsirkin <m...@redhat.com> wrote: > On Fri, Mar 02, 2018 at 10:32:04AM +0800, Dave Young wrote: >> Hi >> >> On 02/28/18 at 04:06pm, Marc-André Lureau wrote: >> > The following patch is going to use the symbol from the fw_cfg module, >> > to call the function and write the note location details in the >> > vmcoreinfo entry, so qemu can produce dumps with the vmcoreinfo note. >> > >> > CC: Andrew Morton <a...@linux-foundation.org> >> > CC: Baoquan He <b...@redhat.com> >> > CC: Dave Young <dyo...@redhat.com> >> > CC: Dave Young <dyo...@redhat.com> >> >> Above duplicated a CC, btw, I remember both Baoquan and me acked it >> before, you can find the old email and carry the acked-by. > > I fixed it but all this is submitter's job really. Thanks, my bad, I missed it from v6. > >> > CC: Hari Bathini <hbath...@linux.vnet.ibm.com> >> > CC: Tony Luck <tony.l...@intel.com> >> > CC: Vivek Goyal <vgo...@redhat.com> >> > Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> >> > Acked-by: Gabriel Somlo <so...@cmu.edu> >> > --- >> > kernel/crash_core.c | 1 + >> > 1 file changed, 1 insertion(+) >> > >> > diff --git a/kernel/crash_core.c b/kernel/crash_core.c >> > index 4f63597c824d..a93590cdd9e1 100644 >> > --- a/kernel/crash_core.c >> > +++ b/kernel/crash_core.c >> > @@ -376,6 +376,7 @@ phys_addr_t __weak paddr_vmcoreinfo_note(void) >> > { >> > return __pa(vmcoreinfo_note); >> > } >> > +EXPORT_SYMBOL(paddr_vmcoreinfo_note); >> > >> > static int __init crash_save_vmcoreinfo_init(void) >> > { >> > -- >> > 2.16.1.73.g5832b7e9f2 >> > >> >> Thanks >> Dave
Re: [PATCH v16 09/11] crash: export paddr_vmcoreinfo_note()
Hi On Fri, Mar 2, 2018 at 7:03 PM, Michael S. Tsirkin wrote: > On Fri, Mar 02, 2018 at 10:32:04AM +0800, Dave Young wrote: >> Hi >> >> On 02/28/18 at 04:06pm, Marc-André Lureau wrote: >> > The following patch is going to use the symbol from the fw_cfg module, >> > to call the function and write the note location details in the >> > vmcoreinfo entry, so qemu can produce dumps with the vmcoreinfo note. >> > >> > CC: Andrew Morton >> > CC: Baoquan He >> > CC: Dave Young >> > CC: Dave Young >> >> Above duplicated a CC, btw, I remember both Baoquan and me acked it >> before, you can find the old email and carry the acked-by. > > I fixed it but all this is submitter's job really. Thanks, my bad, I missed it from v6. > >> > CC: Hari Bathini >> > CC: Tony Luck >> > CC: Vivek Goyal >> > Signed-off-by: Marc-André Lureau >> > Acked-by: Gabriel Somlo >> > --- >> > kernel/crash_core.c | 1 + >> > 1 file changed, 1 insertion(+) >> > >> > diff --git a/kernel/crash_core.c b/kernel/crash_core.c >> > index 4f63597c824d..a93590cdd9e1 100644 >> > --- a/kernel/crash_core.c >> > +++ b/kernel/crash_core.c >> > @@ -376,6 +376,7 @@ phys_addr_t __weak paddr_vmcoreinfo_note(void) >> > { >> > return __pa(vmcoreinfo_note); >> > } >> > +EXPORT_SYMBOL(paddr_vmcoreinfo_note); >> > >> > static int __init crash_save_vmcoreinfo_init(void) >> > { >> > -- >> > 2.16.1.73.g5832b7e9f2 >> > >> >> Thanks >> Dave
Re: rmi4 defunct on T460p [was: [git pull] Input updates for v4.15-rc8]
Hi On Tue, Feb 13, 2018 at 9:22 AM, Jiri Slaby <jsl...@suse.cz> wrote: > On 02/09/2018, 02:39 AM, Dmitry Torokhov wrote: >> On Thu, Feb 08, 2018 at 11:04:22AM +0100, Jiri Slaby wrote: >>> On 01/19/2018, 06:42 PM, Dmitry Torokhov wrote: >>>> Please pull from: >>>> >>>> git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git for-linus >>>> >>>> to receive updates for the input subsystem. You will get: >>>> >>> ... >>>> - touchpad on Lenovo T640p is switched over to SMbus/RMI >>> ... >>> >>>> 王振杰 (1): >>>> Input: synaptics - Lenovo Thinkpad T460p devices should use RMI >>> >>> Hi, >>> >>> one of openSUSE users with T460p reports that the touchpad is defunct >>> after resume in 4.15. psmouse.synaptics_intertouch=0 works around the >>> problem. >>> >>> The problem is: >>> kernel: rmi4_smbus 8-002c: failed to get SMBus version number! >>> kernel: rmi4_physical rmi4-00: rmi_driver_reset_handler: Failed to read >>> current IRQ mask. >>> kernel: rmi4_f01 rmi4-00.fn01: Failed to restore normal operation: -16. >>> kernel: rmi4_f01 rmi4-00.fn01: Resume failed with code -16. >>> kernel: rmi4_physical rmi4-00: Failed to suspend functions: -16 >>> kernel: rmi4_smbus 8-002c: Failed to resume device: -16 >>> kernel: rmi4_f03 rmi4-00.fn03: rmi_f03_pt_write: Failed to write to F03 >>> TX register (-16). >>> >>> The downstream bug is at: >>> https://bugzilla.suse.com/show_bug.cgi?id=1079862 >>> >>> Any ideas? >> >> Hmm, maybe the seuence between psmouse and rmi/smbus is not quite right >> on resume on that box. Can you ask the reporter to: >> >> echo 1 > /sys/power/pm_print_times >> suspend/resume > > He uploaded it here: > https://bugzilla.suse.com/attachment.cgi?id=759933 > Same problem (with f27 and upstream kernel), is there anything else we can do to help? Should we open a bug on http://bugzilla.kernel.org ? thanks -- Marc-André Lureau
Re: rmi4 defunct on T460p [was: [git pull] Input updates for v4.15-rc8]
Hi On Tue, Feb 13, 2018 at 9:22 AM, Jiri Slaby wrote: > On 02/09/2018, 02:39 AM, Dmitry Torokhov wrote: >> On Thu, Feb 08, 2018 at 11:04:22AM +0100, Jiri Slaby wrote: >>> On 01/19/2018, 06:42 PM, Dmitry Torokhov wrote: >>>> Please pull from: >>>> >>>> git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git for-linus >>>> >>>> to receive updates for the input subsystem. You will get: >>>> >>> ... >>>> - touchpad on Lenovo T640p is switched over to SMbus/RMI >>> ... >>> >>>> 王振杰 (1): >>>> Input: synaptics - Lenovo Thinkpad T460p devices should use RMI >>> >>> Hi, >>> >>> one of openSUSE users with T460p reports that the touchpad is defunct >>> after resume in 4.15. psmouse.synaptics_intertouch=0 works around the >>> problem. >>> >>> The problem is: >>> kernel: rmi4_smbus 8-002c: failed to get SMBus version number! >>> kernel: rmi4_physical rmi4-00: rmi_driver_reset_handler: Failed to read >>> current IRQ mask. >>> kernel: rmi4_f01 rmi4-00.fn01: Failed to restore normal operation: -16. >>> kernel: rmi4_f01 rmi4-00.fn01: Resume failed with code -16. >>> kernel: rmi4_physical rmi4-00: Failed to suspend functions: -16 >>> kernel: rmi4_smbus 8-002c: Failed to resume device: -16 >>> kernel: rmi4_f03 rmi4-00.fn03: rmi_f03_pt_write: Failed to write to F03 >>> TX register (-16). >>> >>> The downstream bug is at: >>> https://bugzilla.suse.com/show_bug.cgi?id=1079862 >>> >>> Any ideas? >> >> Hmm, maybe the seuence between psmouse and rmi/smbus is not quite right >> on resume on that box. Can you ask the reporter to: >> >> echo 1 > /sys/power/pm_print_times >> suspend/resume > > He uploaded it here: > https://bugzilla.suse.com/attachment.cgi?id=759933 > Same problem (with f27 and upstream kernel), is there anything else we can do to help? Should we open a bug on http://bugzilla.kernel.org ? thanks -- Marc-André Lureau
Re: [PATCH v16 06/11] fw_cfg: handle fw_cfg_read_blob() error
Hi On Wed, Feb 28, 2018 at 6:32 PM, Michael S. Tsirkinwrote: >> @@ -557,7 +566,10 @@ static int fw_cfg_sysfs_probe(struct platform_device >> *pdev) >> goto err_probe; >> >> /* get revision number, add matching top-level attribute */ >> - fw_cfg_read_blob(FW_CFG_ID, , 0, sizeof(rev)); >> + err = fw_cfg_read_blob(FW_CFG_ID, , 0, sizeof(rev)); >> + if (err < 0) >> + goto err_probe; >> + >> fw_cfg_rev = le32_to_cpu(rev); >> err = sysfs_create_file(fw_cfg_top_ko, _cfg_rev_attr.attr); >> if (err) > > So on this one, maybe we should just drop this attribute. > Does anyone use it? > Removing it will speed up boot slightly. We can't skip reading FW_CFG_ID (rev) as we need it to check DMA support. I don't mind if we remove the sysfs entry, but I doubt it makes a difference in boot time. >> -- >> 2.16.1.73.g5832b7e9f2
Re: [PATCH v16 06/11] fw_cfg: handle fw_cfg_read_blob() error
Hi On Wed, Feb 28, 2018 at 6:32 PM, Michael S. Tsirkin wrote: >> @@ -557,7 +566,10 @@ static int fw_cfg_sysfs_probe(struct platform_device >> *pdev) >> goto err_probe; >> >> /* get revision number, add matching top-level attribute */ >> - fw_cfg_read_blob(FW_CFG_ID, , 0, sizeof(rev)); >> + err = fw_cfg_read_blob(FW_CFG_ID, , 0, sizeof(rev)); >> + if (err < 0) >> + goto err_probe; >> + >> fw_cfg_rev = le32_to_cpu(rev); >> err = sysfs_create_file(fw_cfg_top_ko, _cfg_rev_attr.attr); >> if (err) > > So on this one, maybe we should just drop this attribute. > Does anyone use it? > Removing it will speed up boot slightly. We can't skip reading FW_CFG_ID (rev) as we need it to check DMA support. I don't mind if we remove the sysfs entry, but I doubt it makes a difference in boot time. >> -- >> 2.16.1.73.g5832b7e9f2
Re: [PATCH v15 11/11] RFC: fw_cfg: do DMA read operation
Hi On Wed, Feb 28, 2018 at 6:17 PM, Michael S. Tsirkin <m...@redhat.com> wrote: > On Wed, Feb 28, 2018 at 04:41:51PM +0100, Marc-André Lureau wrote: >> I don't know if it's always safe to enable dma in read_raw(), how >> could we know? Is there a check we could use to choose one or ther >> other (and thus avoiding explicit dma/readfn argument)? > > IMHO the way to go is not to try to do zero copy. > Allocate a buffer and DMA there, then copy. Sounds fine to me, I'll resend this patch separately if the rest from v16 is applied. thanks
Re: [PATCH v15 11/11] RFC: fw_cfg: do DMA read operation
Hi On Wed, Feb 28, 2018 at 6:17 PM, Michael S. Tsirkin wrote: > On Wed, Feb 28, 2018 at 04:41:51PM +0100, Marc-André Lureau wrote: >> I don't know if it's always safe to enable dma in read_raw(), how >> could we know? Is there a check we could use to choose one or ther >> other (and thus avoiding explicit dma/readfn argument)? > > IMHO the way to go is not to try to do zero copy. > Allocate a buffer and DMA there, then copy. Sounds fine to me, I'll resend this patch separately if the rest from v16 is applied. thanks
Re: [PATCH v15 11/11] RFC: fw_cfg: do DMA read operation
Hi On Wed, Feb 28, 2018 at 4:48 PM, Michael S. Tsirkin <m...@redhat.com> wrote: > On Wed, Feb 28, 2018 at 04:41:51PM +0100, Marc-André Lureau wrote: >> I don't know if it's always safe to enable dma in read_raw(), how >> could we know? Is there a check we could use to choose one or ther >> other (and thus avoiding explicit dma/readfn argument)? > > I'm not sure - but does it really matter? Is anyone reading large files > like this in production where speed matters? > Why even bother with DMA? The difference is quite significante for not so small files, as shown above. And if they access the fw_cfg entries at boot time, or when starting things etc, this may speed things up.
Re: [PATCH v15 11/11] RFC: fw_cfg: do DMA read operation
Hi On Wed, Feb 28, 2018 at 4:48 PM, Michael S. Tsirkin wrote: > On Wed, Feb 28, 2018 at 04:41:51PM +0100, Marc-André Lureau wrote: >> I don't know if it's always safe to enable dma in read_raw(), how >> could we know? Is there a check we could use to choose one or ther >> other (and thus avoiding explicit dma/readfn argument)? > > I'm not sure - but does it really matter? Is anyone reading large files > like this in production where speed matters? > Why even bother with DMA? The difference is quite significante for not so small files, as shown above. And if they access the fw_cfg entries at boot time, or when starting things etc, this may speed things up.
Re: [PATCH v15 11/11] RFC: fw_cfg: do DMA read operation
On Wed, Feb 28, 2018 at 4:35 PM, Michael S. Tsirkin <m...@redhat.com> wrote: > On Wed, Feb 28, 2018 at 01:27:02PM +0100, Marc-André Lureau wrote: >> Hi >> >> On Tue, Feb 27, 2018 at 1:04 AM, Michael S. Tsirkin <m...@redhat.com> wrote: >> > On Thu, Feb 15, 2018 at 10:33:12PM +0100, Marc-André Lureau wrote: >> >> Modify fw_cfg_read_blob() to use DMA if the device supports it. >> >> Return errors, because the operation may fail. >> >> >> >> So far, only one call in fw_cfg_register_dir_entries() is using >> >> kmalloc'ed buf and is thus clearly eligible to DMA read. >> >> >> >> Initially, I didn't implement DMA read to speed up boot time, but as a >> >> first step before introducing DMA write (since read operations were >> >> already presents). Even more, I didn't realize fw-cfg entries were >> >> being read by the kernel during boot by default. But actally fw-cfg >> >> entries are being populated during module probe. I knew DMA improved a >> >> lot bios boot time (the main reason the DMA interface was added >> >> afaik). Let see the time it would take to read the whole ACPI >> >> tables (128kb allocated) >> >> >> >> # time cat /sys/firmware/qemu_fw_cfg/by_name/etc/acpi/tables/raw >> >> - with DMA: sys 0m0.003s >> >> - without DMA (-global fw_cfg.dma_enabled=off): sys 0m7.674s >> >> >> >> FW_CFG_FILE_DIR (0x19) is the only "file" that is read during kernel >> >> boot to populate sysfs qemu_fw_cfg directory, and it is quite >> >> small (1-2kb). Since it does not expose itself, in order to measure >> >> the time it takes to read such small file, I took a comparable sized >> >> file of 2048 bytes and exposed it (-fw_cfg test,file=file with a >> >> modified read_raw enabling DMA) >> >> >> >> # perf stat -r 100 cat /sys/firmware/qemu_fw_cfg/by_name/test/raw >> >> >/dev/null >> >> - with DMA: >> >> 0.636037 task-clock (msec) #0.141 CPUs >> >> utilized( +- 1.19% ) >> >> - without DMA: >> >> 6.430128 task-clock (msec) #0.622 CPUs >> >> utilized( +- 0.22% ) >> >> >> >> That's a few msec saved during boot by enabling DMA read (the gain >> >> would be more substantial if other & bigger fw-cfg entries are read by >> >> others from sysfs, unfortunately, it's not clear if we can always >> >> enable DMA there) >> >> >> >> Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> >> >> --- >> >> drivers/firmware/qemu_fw_cfg.c | 61 >> >> ++ >> >> 1 file changed, 50 insertions(+), 11 deletions(-) >> >> >> >> diff --git a/drivers/firmware/qemu_fw_cfg.c >> >> b/drivers/firmware/qemu_fw_cfg.c >> >> index 3015e77aebca..94df57e9be66 100644 >> >> --- a/drivers/firmware/qemu_fw_cfg.c >> >> +++ b/drivers/firmware/qemu_fw_cfg.c >> >> @@ -124,12 +124,47 @@ static ssize_t fw_cfg_dma_transfer(void *address, >> >> u32 length, u32 control) >> >> return ret; >> >> } >> >> >> >> +/* with acpi & dev locks taken */ >> >> +static ssize_t fw_cfg_read_blob_dma(u16 key, >> >> + void *buf, loff_t pos, size_t count) >> >> +{ >> >> + ssize_t ret; >> >> + >> >> + if (pos == 0) { >> >> + ret = fw_cfg_dma_transfer(buf, count, key << 16 >> >> + | FW_CFG_DMA_CTL_SELECT >> >> + | FW_CFG_DMA_CTL_READ); >> >> + } else { >> >> + fw_cfg_sel_endianness(key); >> >> + ret = fw_cfg_dma_transfer(NULL, pos, FW_CFG_DMA_CTL_SKIP); >> >> + if (ret < 0) >> >> + return ret; >> >> + ret = fw_cfg_dma_transfer(buf, count, >> >> + FW_CFG_DMA_CTL_READ); >> >> + } >> >> + >> >> + return ret; >> >> +} >> >> + >> >> +/* with acpi & dev locks taken */ >> >> +static ssize_t fw_cfg_read_blob_io(u16 key, >> >> + void *buf, loff
Re: [PATCH v15 11/11] RFC: fw_cfg: do DMA read operation
On Wed, Feb 28, 2018 at 4:35 PM, Michael S. Tsirkin wrote: > On Wed, Feb 28, 2018 at 01:27:02PM +0100, Marc-André Lureau wrote: >> Hi >> >> On Tue, Feb 27, 2018 at 1:04 AM, Michael S. Tsirkin wrote: >> > On Thu, Feb 15, 2018 at 10:33:12PM +0100, Marc-André Lureau wrote: >> >> Modify fw_cfg_read_blob() to use DMA if the device supports it. >> >> Return errors, because the operation may fail. >> >> >> >> So far, only one call in fw_cfg_register_dir_entries() is using >> >> kmalloc'ed buf and is thus clearly eligible to DMA read. >> >> >> >> Initially, I didn't implement DMA read to speed up boot time, but as a >> >> first step before introducing DMA write (since read operations were >> >> already presents). Even more, I didn't realize fw-cfg entries were >> >> being read by the kernel during boot by default. But actally fw-cfg >> >> entries are being populated during module probe. I knew DMA improved a >> >> lot bios boot time (the main reason the DMA interface was added >> >> afaik). Let see the time it would take to read the whole ACPI >> >> tables (128kb allocated) >> >> >> >> # time cat /sys/firmware/qemu_fw_cfg/by_name/etc/acpi/tables/raw >> >> - with DMA: sys 0m0.003s >> >> - without DMA (-global fw_cfg.dma_enabled=off): sys 0m7.674s >> >> >> >> FW_CFG_FILE_DIR (0x19) is the only "file" that is read during kernel >> >> boot to populate sysfs qemu_fw_cfg directory, and it is quite >> >> small (1-2kb). Since it does not expose itself, in order to measure >> >> the time it takes to read such small file, I took a comparable sized >> >> file of 2048 bytes and exposed it (-fw_cfg test,file=file with a >> >> modified read_raw enabling DMA) >> >> >> >> # perf stat -r 100 cat /sys/firmware/qemu_fw_cfg/by_name/test/raw >> >> >/dev/null >> >> - with DMA: >> >> 0.636037 task-clock (msec) #0.141 CPUs >> >> utilized( +- 1.19% ) >> >> - without DMA: >> >> 6.430128 task-clock (msec) #0.622 CPUs >> >> utilized( +- 0.22% ) >> >> >> >> That's a few msec saved during boot by enabling DMA read (the gain >> >> would be more substantial if other & bigger fw-cfg entries are read by >> >> others from sysfs, unfortunately, it's not clear if we can always >> >> enable DMA there) >> >> >> >> Signed-off-by: Marc-André Lureau >> >> --- >> >> drivers/firmware/qemu_fw_cfg.c | 61 >> >> ++ >> >> 1 file changed, 50 insertions(+), 11 deletions(-) >> >> >> >> diff --git a/drivers/firmware/qemu_fw_cfg.c >> >> b/drivers/firmware/qemu_fw_cfg.c >> >> index 3015e77aebca..94df57e9be66 100644 >> >> --- a/drivers/firmware/qemu_fw_cfg.c >> >> +++ b/drivers/firmware/qemu_fw_cfg.c >> >> @@ -124,12 +124,47 @@ static ssize_t fw_cfg_dma_transfer(void *address, >> >> u32 length, u32 control) >> >> return ret; >> >> } >> >> >> >> +/* with acpi & dev locks taken */ >> >> +static ssize_t fw_cfg_read_blob_dma(u16 key, >> >> + void *buf, loff_t pos, size_t count) >> >> +{ >> >> + ssize_t ret; >> >> + >> >> + if (pos == 0) { >> >> + ret = fw_cfg_dma_transfer(buf, count, key << 16 >> >> + | FW_CFG_DMA_CTL_SELECT >> >> + | FW_CFG_DMA_CTL_READ); >> >> + } else { >> >> + fw_cfg_sel_endianness(key); >> >> + ret = fw_cfg_dma_transfer(NULL, pos, FW_CFG_DMA_CTL_SKIP); >> >> + if (ret < 0) >> >> + return ret; >> >> + ret = fw_cfg_dma_transfer(buf, count, >> >> + FW_CFG_DMA_CTL_READ); >> >> + } >> >> + >> >> + return ret; >> >> +} >> >> + >> >> +/* with acpi & dev locks taken */ >> >> +static ssize_t fw_cfg_read_blob_io(u16 key, >> >> + void *buf, loff_t pos, size_t count) >> >> +{ >> >> + fw_cfg_sel_endianness(key);
[PATCH v16 01/11] fw_cfg: fix sparse warnings in fw_cfg_sel_endianness()
Dispatch to the appropriate iowrite() instead of casting restricted type to u16. - if fw_cfg_is_mmio: before: iowrite16(cpu_to_be16(key)) after: iowrite16be(key) - if !fw_cfg_is_mmio: before: iowrite16(cpu_to_le16(key)) after: iowrite16(key) which is equivalent on little-endian systems, where fw_cfg IO is supported. Fixes: $ make C=1 CF=-D__CHECK_ENDIAN__ drivers/firmware/qemu_fw_cfg.o drivers/firmware/qemu_fw_cfg.c:55:33: warning: restricted __be16 degrades to integer drivers/firmware/qemu_fw_cfg.c:55:52: warning: restricted __le16 degrades to integer Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> --- drivers/firmware/qemu_fw_cfg.c | 9 ++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index a41b572eeeb1..e7ea2b3b1d11 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -68,9 +68,12 @@ static void __iomem *fw_cfg_reg_data; static DEFINE_MUTEX(fw_cfg_dev_lock); /* pick appropriate endianness for selector key */ -static inline u16 fw_cfg_sel_endianness(u16 key) +static void fw_cfg_sel_endianness(u16 key) { - return fw_cfg_is_mmio ? cpu_to_be16(key) : cpu_to_le16(key); + if (fw_cfg_is_mmio) + iowrite16be(key, fw_cfg_reg_ctrl); + else + iowrite16(key, fw_cfg_reg_ctrl); } /* read chunk of given fw_cfg blob (caller responsible for sanity-check) */ @@ -92,7 +95,7 @@ static inline void fw_cfg_read_blob(u16 key, } mutex_lock(_cfg_dev_lock); - iowrite16(fw_cfg_sel_endianness(key), fw_cfg_reg_ctrl); + fw_cfg_sel_endianness(key); while (pos-- > 0) ioread8(fw_cfg_reg_data); ioread8_rep(fw_cfg_reg_data, buf, count); -- 2.16.1.73.g5832b7e9f2
[PATCH v16 01/11] fw_cfg: fix sparse warnings in fw_cfg_sel_endianness()
Dispatch to the appropriate iowrite() instead of casting restricted type to u16. - if fw_cfg_is_mmio: before: iowrite16(cpu_to_be16(key)) after: iowrite16be(key) - if !fw_cfg_is_mmio: before: iowrite16(cpu_to_le16(key)) after: iowrite16(key) which is equivalent on little-endian systems, where fw_cfg IO is supported. Fixes: $ make C=1 CF=-D__CHECK_ENDIAN__ drivers/firmware/qemu_fw_cfg.o drivers/firmware/qemu_fw_cfg.c:55:33: warning: restricted __be16 degrades to integer drivers/firmware/qemu_fw_cfg.c:55:52: warning: restricted __le16 degrades to integer Signed-off-by: Marc-André Lureau --- drivers/firmware/qemu_fw_cfg.c | 9 ++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index a41b572eeeb1..e7ea2b3b1d11 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -68,9 +68,12 @@ static void __iomem *fw_cfg_reg_data; static DEFINE_MUTEX(fw_cfg_dev_lock); /* pick appropriate endianness for selector key */ -static inline u16 fw_cfg_sel_endianness(u16 key) +static void fw_cfg_sel_endianness(u16 key) { - return fw_cfg_is_mmio ? cpu_to_be16(key) : cpu_to_le16(key); + if (fw_cfg_is_mmio) + iowrite16be(key, fw_cfg_reg_ctrl); + else + iowrite16(key, fw_cfg_reg_ctrl); } /* read chunk of given fw_cfg blob (caller responsible for sanity-check) */ @@ -92,7 +95,7 @@ static inline void fw_cfg_read_blob(u16 key, } mutex_lock(_cfg_dev_lock); - iowrite16(fw_cfg_sel_endianness(key), fw_cfg_reg_ctrl); + fw_cfg_sel_endianness(key); while (pos-- > 0) ioread8(fw_cfg_reg_data); ioread8_rep(fw_cfg_reg_data, buf, count); -- 2.16.1.73.g5832b7e9f2
[PATCH v16 04/11] fw_cfg: fix sparse warnings around FW_CFG_FILE_DIR read
Use struct fw_cfg_files to read the directory size, fixing the sparse warnings: drivers/firmware/qemu_fw_cfg.c:485:17: warning: cast to restricted __be32 Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> --- drivers/firmware/qemu_fw_cfg.c | 7 --- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 0eb155fdfb35..00ad9b862414 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -496,19 +496,20 @@ static int fw_cfg_register_file(const struct fw_cfg_file *f) static int fw_cfg_register_dir_entries(void) { int ret = 0; + __be32 files_count; u32 count, i; struct fw_cfg_file *dir; size_t dir_size; - fw_cfg_read_blob(FW_CFG_FILE_DIR, , 0, sizeof(count)); - count = be32_to_cpu(count); + fw_cfg_read_blob(FW_CFG_FILE_DIR, _count, 0, sizeof(files_count)); + count = be32_to_cpu(files_count); dir_size = count * sizeof(struct fw_cfg_file); dir = kmalloc(dir_size, GFP_KERNEL); if (!dir) return -ENOMEM; - fw_cfg_read_blob(FW_CFG_FILE_DIR, dir, sizeof(count), dir_size); + fw_cfg_read_blob(FW_CFG_FILE_DIR, dir, sizeof(files_count), dir_size); for (i = 0; i < count; i++) { ret = fw_cfg_register_file([i]); -- 2.16.1.73.g5832b7e9f2
[PATCH v16 04/11] fw_cfg: fix sparse warnings around FW_CFG_FILE_DIR read
Use struct fw_cfg_files to read the directory size, fixing the sparse warnings: drivers/firmware/qemu_fw_cfg.c:485:17: warning: cast to restricted __be32 Signed-off-by: Marc-André Lureau --- drivers/firmware/qemu_fw_cfg.c | 7 --- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 0eb155fdfb35..00ad9b862414 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -496,19 +496,20 @@ static int fw_cfg_register_file(const struct fw_cfg_file *f) static int fw_cfg_register_dir_entries(void) { int ret = 0; + __be32 files_count; u32 count, i; struct fw_cfg_file *dir; size_t dir_size; - fw_cfg_read_blob(FW_CFG_FILE_DIR, , 0, sizeof(count)); - count = be32_to_cpu(count); + fw_cfg_read_blob(FW_CFG_FILE_DIR, _count, 0, sizeof(files_count)); + count = be32_to_cpu(files_count); dir_size = count * sizeof(struct fw_cfg_file); dir = kmalloc(dir_size, GFP_KERNEL); if (!dir) return -ENOMEM; - fw_cfg_read_blob(FW_CFG_FILE_DIR, dir, sizeof(count), dir_size); + fw_cfg_read_blob(FW_CFG_FILE_DIR, dir, sizeof(files_count), dir_size); for (i = 0; i < count; i++) { ret = fw_cfg_register_file([i]); -- 2.16.1.73.g5832b7e9f2
[PATCH v16 06/11] fw_cfg: handle fw_cfg_read_blob() error
fw_cfg_read_blob() may fail, but does not return error. This may lead to surprising behaviours, like populating zero file entries (in register_file() or during read). Return an error if ACPI locking failed. Also, the following DMA read/write extension will add more error paths that should be handled appropriately. Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> --- drivers/firmware/qemu_fw_cfg.c | 32 ++-- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 0cc71d028ae3..45bfc389b226 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -77,8 +77,8 @@ static void fw_cfg_sel_endianness(u16 key) } /* read chunk of given fw_cfg blob (caller responsible for sanity-check) */ -static void fw_cfg_read_blob(u16 key, - void *buf, loff_t pos, size_t count) +static ssize_t fw_cfg_read_blob(u16 key, + void *buf, loff_t pos, size_t count) { u32 glk = -1U; acpi_status status; @@ -91,7 +91,7 @@ static void fw_cfg_read_blob(u16 key, /* Should never get here */ WARN(1, "fw_cfg_read_blob: Failed to lock ACPI!\n"); memset(buf, 0, count); - return; + return -EINVAL; } mutex_lock(_cfg_dev_lock); @@ -102,6 +102,7 @@ static void fw_cfg_read_blob(u16 key, mutex_unlock(_cfg_dev_lock); acpi_release_global_lock(glk); + return count; } /* clean up fw_cfg device i/o */ @@ -183,8 +184,9 @@ static int fw_cfg_do_platform_probe(struct platform_device *pdev) } /* verify fw_cfg device signature */ - fw_cfg_read_blob(FW_CFG_SIGNATURE, sig, 0, FW_CFG_SIG_SIZE); - if (memcmp(sig, "QEMU", FW_CFG_SIG_SIZE) != 0) { + if (fw_cfg_read_blob(FW_CFG_SIGNATURE, sig, + 0, FW_CFG_SIG_SIZE) < 0 || + memcmp(sig, "QEMU", FW_CFG_SIG_SIZE) != 0) { fw_cfg_io_cleanup(); return -ENODEV; } @@ -344,8 +346,7 @@ static ssize_t fw_cfg_sysfs_read_raw(struct file *filp, struct kobject *kobj, if (count > entry->size - pos) count = entry->size - pos; - fw_cfg_read_blob(entry->select, buf, pos, count); - return count; + return fw_cfg_read_blob(entry->select, buf, pos, count); } static struct bin_attribute fw_cfg_sysfs_attr_raw = { @@ -501,7 +502,11 @@ static int fw_cfg_register_dir_entries(void) struct fw_cfg_file *dir; size_t dir_size; - fw_cfg_read_blob(FW_CFG_FILE_DIR, _count, 0, sizeof(files_count)); + ret = fw_cfg_read_blob(FW_CFG_FILE_DIR, _count, + 0, sizeof(files_count)); + if (ret < 0) + return ret; + count = be32_to_cpu(files_count); dir_size = count * sizeof(struct fw_cfg_file); @@ -509,7 +514,10 @@ static int fw_cfg_register_dir_entries(void) if (!dir) return -ENOMEM; - fw_cfg_read_blob(FW_CFG_FILE_DIR, dir, sizeof(files_count), dir_size); + ret = fw_cfg_read_blob(FW_CFG_FILE_DIR, dir, + sizeof(files_count), dir_size); + if (ret < 0) + goto end; for (i = 0; i < count; i++) { ret = fw_cfg_register_file([i]); @@ -517,6 +525,7 @@ static int fw_cfg_register_dir_entries(void) break; } +end: kfree(dir); return ret; } @@ -557,7 +566,10 @@ static int fw_cfg_sysfs_probe(struct platform_device *pdev) goto err_probe; /* get revision number, add matching top-level attribute */ - fw_cfg_read_blob(FW_CFG_ID, , 0, sizeof(rev)); + err = fw_cfg_read_blob(FW_CFG_ID, , 0, sizeof(rev)); + if (err < 0) + goto err_probe; + fw_cfg_rev = le32_to_cpu(rev); err = sysfs_create_file(fw_cfg_top_ko, _cfg_rev_attr.attr); if (err) -- 2.16.1.73.g5832b7e9f2
[PATCH v16 06/11] fw_cfg: handle fw_cfg_read_blob() error
fw_cfg_read_blob() may fail, but does not return error. This may lead to surprising behaviours, like populating zero file entries (in register_file() or during read). Return an error if ACPI locking failed. Also, the following DMA read/write extension will add more error paths that should be handled appropriately. Signed-off-by: Marc-André Lureau --- drivers/firmware/qemu_fw_cfg.c | 32 ++-- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 0cc71d028ae3..45bfc389b226 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -77,8 +77,8 @@ static void fw_cfg_sel_endianness(u16 key) } /* read chunk of given fw_cfg blob (caller responsible for sanity-check) */ -static void fw_cfg_read_blob(u16 key, - void *buf, loff_t pos, size_t count) +static ssize_t fw_cfg_read_blob(u16 key, + void *buf, loff_t pos, size_t count) { u32 glk = -1U; acpi_status status; @@ -91,7 +91,7 @@ static void fw_cfg_read_blob(u16 key, /* Should never get here */ WARN(1, "fw_cfg_read_blob: Failed to lock ACPI!\n"); memset(buf, 0, count); - return; + return -EINVAL; } mutex_lock(_cfg_dev_lock); @@ -102,6 +102,7 @@ static void fw_cfg_read_blob(u16 key, mutex_unlock(_cfg_dev_lock); acpi_release_global_lock(glk); + return count; } /* clean up fw_cfg device i/o */ @@ -183,8 +184,9 @@ static int fw_cfg_do_platform_probe(struct platform_device *pdev) } /* verify fw_cfg device signature */ - fw_cfg_read_blob(FW_CFG_SIGNATURE, sig, 0, FW_CFG_SIG_SIZE); - if (memcmp(sig, "QEMU", FW_CFG_SIG_SIZE) != 0) { + if (fw_cfg_read_blob(FW_CFG_SIGNATURE, sig, + 0, FW_CFG_SIG_SIZE) < 0 || + memcmp(sig, "QEMU", FW_CFG_SIG_SIZE) != 0) { fw_cfg_io_cleanup(); return -ENODEV; } @@ -344,8 +346,7 @@ static ssize_t fw_cfg_sysfs_read_raw(struct file *filp, struct kobject *kobj, if (count > entry->size - pos) count = entry->size - pos; - fw_cfg_read_blob(entry->select, buf, pos, count); - return count; + return fw_cfg_read_blob(entry->select, buf, pos, count); } static struct bin_attribute fw_cfg_sysfs_attr_raw = { @@ -501,7 +502,11 @@ static int fw_cfg_register_dir_entries(void) struct fw_cfg_file *dir; size_t dir_size; - fw_cfg_read_blob(FW_CFG_FILE_DIR, _count, 0, sizeof(files_count)); + ret = fw_cfg_read_blob(FW_CFG_FILE_DIR, _count, + 0, sizeof(files_count)); + if (ret < 0) + return ret; + count = be32_to_cpu(files_count); dir_size = count * sizeof(struct fw_cfg_file); @@ -509,7 +514,10 @@ static int fw_cfg_register_dir_entries(void) if (!dir) return -ENOMEM; - fw_cfg_read_blob(FW_CFG_FILE_DIR, dir, sizeof(files_count), dir_size); + ret = fw_cfg_read_blob(FW_CFG_FILE_DIR, dir, + sizeof(files_count), dir_size); + if (ret < 0) + goto end; for (i = 0; i < count; i++) { ret = fw_cfg_register_file([i]); @@ -517,6 +525,7 @@ static int fw_cfg_register_dir_entries(void) break; } +end: kfree(dir); return ret; } @@ -557,7 +566,10 @@ static int fw_cfg_sysfs_probe(struct platform_device *pdev) goto err_probe; /* get revision number, add matching top-level attribute */ - fw_cfg_read_blob(FW_CFG_ID, , 0, sizeof(rev)); + err = fw_cfg_read_blob(FW_CFG_ID, , 0, sizeof(rev)); + if (err < 0) + goto err_probe; + fw_cfg_rev = le32_to_cpu(rev); err = sysfs_create_file(fw_cfg_top_ko, _cfg_rev_attr.attr); if (err) -- 2.16.1.73.g5832b7e9f2
[PATCH v16 05/11] fw_cfg: remove inline from fw_cfg_read_blob()
The function is not small and getting bigger. Let the compiler decide instead. No profiling done, hopefully unnecessary. Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> --- drivers/firmware/qemu_fw_cfg.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 00ad9b862414..0cc71d028ae3 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -77,8 +77,8 @@ static void fw_cfg_sel_endianness(u16 key) } /* read chunk of given fw_cfg blob (caller responsible for sanity-check) */ -static inline void fw_cfg_read_blob(u16 key, - void *buf, loff_t pos, size_t count) +static void fw_cfg_read_blob(u16 key, + void *buf, loff_t pos, size_t count) { u32 glk = -1U; acpi_status status; -- 2.16.1.73.g5832b7e9f2
[PATCH v16 05/11] fw_cfg: remove inline from fw_cfg_read_blob()
The function is not small and getting bigger. Let the compiler decide instead. No profiling done, hopefully unnecessary. Signed-off-by: Marc-André Lureau --- drivers/firmware/qemu_fw_cfg.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 00ad9b862414..0cc71d028ae3 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -77,8 +77,8 @@ static void fw_cfg_sel_endianness(u16 key) } /* read chunk of given fw_cfg blob (caller responsible for sanity-check) */ -static inline void fw_cfg_read_blob(u16 key, - void *buf, loff_t pos, size_t count) +static void fw_cfg_read_blob(u16 key, + void *buf, loff_t pos, size_t count) { u32 glk = -1U; acpi_status status; -- 2.16.1.73.g5832b7e9f2
[PATCH v16 09/11] crash: export paddr_vmcoreinfo_note()
The following patch is going to use the symbol from the fw_cfg module, to call the function and write the note location details in the vmcoreinfo entry, so qemu can produce dumps with the vmcoreinfo note. CC: Andrew Morton <a...@linux-foundation.org> CC: Baoquan He <b...@redhat.com> CC: Dave Young <dyo...@redhat.com> CC: Dave Young <dyo...@redhat.com> CC: Hari Bathini <hbath...@linux.vnet.ibm.com> CC: Tony Luck <tony.l...@intel.com> CC: Vivek Goyal <vgo...@redhat.com> Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> Acked-by: Gabriel Somlo <so...@cmu.edu> --- kernel/crash_core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/crash_core.c b/kernel/crash_core.c index 4f63597c824d..a93590cdd9e1 100644 --- a/kernel/crash_core.c +++ b/kernel/crash_core.c @@ -376,6 +376,7 @@ phys_addr_t __weak paddr_vmcoreinfo_note(void) { return __pa(vmcoreinfo_note); } +EXPORT_SYMBOL(paddr_vmcoreinfo_note); static int __init crash_save_vmcoreinfo_init(void) { -- 2.16.1.73.g5832b7e9f2
[PATCH v16 09/11] crash: export paddr_vmcoreinfo_note()
The following patch is going to use the symbol from the fw_cfg module, to call the function and write the note location details in the vmcoreinfo entry, so qemu can produce dumps with the vmcoreinfo note. CC: Andrew Morton CC: Baoquan He CC: Dave Young CC: Dave Young CC: Hari Bathini CC: Tony Luck CC: Vivek Goyal Signed-off-by: Marc-André Lureau Acked-by: Gabriel Somlo --- kernel/crash_core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/crash_core.c b/kernel/crash_core.c index 4f63597c824d..a93590cdd9e1 100644 --- a/kernel/crash_core.c +++ b/kernel/crash_core.c @@ -376,6 +376,7 @@ phys_addr_t __weak paddr_vmcoreinfo_note(void) { return __pa(vmcoreinfo_note); } +EXPORT_SYMBOL(paddr_vmcoreinfo_note); static int __init crash_save_vmcoreinfo_init(void) { -- 2.16.1.73.g5832b7e9f2
[PATCH v16 10/11] fw_cfg: write vmcoreinfo details
If the "etc/vmcoreinfo" fw_cfg file is present and we are not running the kdump kernel, write the addr/size of the vmcoreinfo ELF note. The DMA operation is expected to run synchronously with today qemu, but the specification states that it may become async, so we run "control" field check in a loop for eventual changes. Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> --- drivers/firmware/qemu_fw_cfg.c | 145 ++- include/uapi/linux/qemu_fw_cfg.h | 31 + 2 files changed, 173 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index df028faa2d00..14fedbeca724 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -34,11 +34,17 @@ #include #include #include +#include +#include +#include MODULE_AUTHOR("Gabriel L. Somlo <so...@cmu.edu>"); MODULE_DESCRIPTION("QEMU fw_cfg sysfs support"); MODULE_LICENSE("GPL"); +/* fw_cfg revision attribute, in /sys/firmware/qemu_fw_cfg top-level dir. */ +static u32 fw_cfg_rev; + /* fw_cfg device i/o register addresses */ static bool fw_cfg_is_mmio; static phys_addr_t fw_cfg_p_base; @@ -60,6 +66,66 @@ static void fw_cfg_sel_endianness(u16 key) iowrite16(key, fw_cfg_reg_ctrl); } +#ifdef CONFIG_CRASH_CORE +static inline bool fw_cfg_dma_enabled(void) +{ + return (fw_cfg_rev & FW_CFG_VERSION_DMA) && fw_cfg_reg_dma; +} + +/* qemu fw_cfg device is sync today, but spec says it may become async */ +static void fw_cfg_wait_for_control(struct fw_cfg_dma_access *d) +{ + for (;;) { + u32 ctrl = be32_to_cpu(READ_ONCE(d->control)); + + /* do not reorder the read to d->control */ + rmb(); + if ((ctrl & ~FW_CFG_DMA_CTL_ERROR) == 0) + return; + + cpu_relax(); + } +} + +static ssize_t fw_cfg_dma_transfer(void *address, u32 length, u32 control) +{ + phys_addr_t dma; + struct fw_cfg_dma_access *d = NULL; + ssize_t ret = length; + + d = kmalloc(sizeof(*d), GFP_KERNEL); + if (!d) { + ret = -ENOMEM; + goto end; + } + + /* fw_cfg device does not need IOMMU protection, so use physical addresses */ + *d = (struct fw_cfg_dma_access) { + .address = cpu_to_be64(address ? virt_to_phys(address) : 0), + .length = cpu_to_be32(length), + .control = cpu_to_be32(control) + }; + + dma = virt_to_phys(d); + + iowrite32be((u64)dma >> 32, fw_cfg_reg_dma); + /* force memory to sync before notifying device via MMIO */ + wmb(); + iowrite32be(dma, fw_cfg_reg_dma + 4); + + fw_cfg_wait_for_control(d); + + if (be32_to_cpu(READ_ONCE(d->control)) & FW_CFG_DMA_CTL_ERROR) { + ret = -EIO; + } + +end: + kfree(d); + + return ret; +} +#endif + /* read chunk of given fw_cfg blob (caller responsible for sanity-check) */ static ssize_t fw_cfg_read_blob(u16 key, void *buf, loff_t pos, size_t count) @@ -89,6 +155,47 @@ static ssize_t fw_cfg_read_blob(u16 key, return count; } +#ifdef CONFIG_CRASH_CORE +/* write chunk of given fw_cfg blob (caller responsible for sanity-check) */ +static ssize_t fw_cfg_write_blob(u16 key, +void *buf, loff_t pos, size_t count) +{ + u32 glk = -1U; + acpi_status status; + ssize_t ret = count; + + /* If we have ACPI, ensure mutual exclusion against any potential +* device access by the firmware, e.g. via AML methods: +*/ + status = acpi_acquire_global_lock(ACPI_WAIT_FOREVER, ); + if (ACPI_FAILURE(status) && status != AE_NOT_CONFIGURED) { + /* Should never get here */ + WARN(1, "%s: Failed to lock ACPI!\n", __func__); + return -EINVAL; + } + + mutex_lock(_cfg_dev_lock); + if (pos == 0) { + ret = fw_cfg_dma_transfer(buf, count, key << 16 + | FW_CFG_DMA_CTL_SELECT + | FW_CFG_DMA_CTL_WRITE); + } else { + fw_cfg_sel_endianness(key); + ret = fw_cfg_dma_transfer(NULL, pos, FW_CFG_DMA_CTL_SKIP); + if (ret < 0) + goto end; + ret = fw_cfg_dma_transfer(buf, count, FW_CFG_DMA_CTL_WRITE); + } + +end: + mutex_unlock(_cfg_dev_lock); + + acpi_release_global_lock(glk); + + return ret; +} +#endif /* CONFIG_CRASH_CORE */ + /* clean up fw_cfg device i/o */ static void fw_cfg_io_cleanup(void) { @@ -188,9 +295,6 @@ static int fw_cfg_do_platform_probe(struct platform_device *pdev) return 0; } -/* fw_cfg revision attribut
[PATCH v16 10/11] fw_cfg: write vmcoreinfo details
If the "etc/vmcoreinfo" fw_cfg file is present and we are not running the kdump kernel, write the addr/size of the vmcoreinfo ELF note. The DMA operation is expected to run synchronously with today qemu, but the specification states that it may become async, so we run "control" field check in a loop for eventual changes. Signed-off-by: Marc-André Lureau --- drivers/firmware/qemu_fw_cfg.c | 145 ++- include/uapi/linux/qemu_fw_cfg.h | 31 + 2 files changed, 173 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index df028faa2d00..14fedbeca724 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -34,11 +34,17 @@ #include #include #include +#include +#include +#include MODULE_AUTHOR("Gabriel L. Somlo "); MODULE_DESCRIPTION("QEMU fw_cfg sysfs support"); MODULE_LICENSE("GPL"); +/* fw_cfg revision attribute, in /sys/firmware/qemu_fw_cfg top-level dir. */ +static u32 fw_cfg_rev; + /* fw_cfg device i/o register addresses */ static bool fw_cfg_is_mmio; static phys_addr_t fw_cfg_p_base; @@ -60,6 +66,66 @@ static void fw_cfg_sel_endianness(u16 key) iowrite16(key, fw_cfg_reg_ctrl); } +#ifdef CONFIG_CRASH_CORE +static inline bool fw_cfg_dma_enabled(void) +{ + return (fw_cfg_rev & FW_CFG_VERSION_DMA) && fw_cfg_reg_dma; +} + +/* qemu fw_cfg device is sync today, but spec says it may become async */ +static void fw_cfg_wait_for_control(struct fw_cfg_dma_access *d) +{ + for (;;) { + u32 ctrl = be32_to_cpu(READ_ONCE(d->control)); + + /* do not reorder the read to d->control */ + rmb(); + if ((ctrl & ~FW_CFG_DMA_CTL_ERROR) == 0) + return; + + cpu_relax(); + } +} + +static ssize_t fw_cfg_dma_transfer(void *address, u32 length, u32 control) +{ + phys_addr_t dma; + struct fw_cfg_dma_access *d = NULL; + ssize_t ret = length; + + d = kmalloc(sizeof(*d), GFP_KERNEL); + if (!d) { + ret = -ENOMEM; + goto end; + } + + /* fw_cfg device does not need IOMMU protection, so use physical addresses */ + *d = (struct fw_cfg_dma_access) { + .address = cpu_to_be64(address ? virt_to_phys(address) : 0), + .length = cpu_to_be32(length), + .control = cpu_to_be32(control) + }; + + dma = virt_to_phys(d); + + iowrite32be((u64)dma >> 32, fw_cfg_reg_dma); + /* force memory to sync before notifying device via MMIO */ + wmb(); + iowrite32be(dma, fw_cfg_reg_dma + 4); + + fw_cfg_wait_for_control(d); + + if (be32_to_cpu(READ_ONCE(d->control)) & FW_CFG_DMA_CTL_ERROR) { + ret = -EIO; + } + +end: + kfree(d); + + return ret; +} +#endif + /* read chunk of given fw_cfg blob (caller responsible for sanity-check) */ static ssize_t fw_cfg_read_blob(u16 key, void *buf, loff_t pos, size_t count) @@ -89,6 +155,47 @@ static ssize_t fw_cfg_read_blob(u16 key, return count; } +#ifdef CONFIG_CRASH_CORE +/* write chunk of given fw_cfg blob (caller responsible for sanity-check) */ +static ssize_t fw_cfg_write_blob(u16 key, +void *buf, loff_t pos, size_t count) +{ + u32 glk = -1U; + acpi_status status; + ssize_t ret = count; + + /* If we have ACPI, ensure mutual exclusion against any potential +* device access by the firmware, e.g. via AML methods: +*/ + status = acpi_acquire_global_lock(ACPI_WAIT_FOREVER, ); + if (ACPI_FAILURE(status) && status != AE_NOT_CONFIGURED) { + /* Should never get here */ + WARN(1, "%s: Failed to lock ACPI!\n", __func__); + return -EINVAL; + } + + mutex_lock(_cfg_dev_lock); + if (pos == 0) { + ret = fw_cfg_dma_transfer(buf, count, key << 16 + | FW_CFG_DMA_CTL_SELECT + | FW_CFG_DMA_CTL_WRITE); + } else { + fw_cfg_sel_endianness(key); + ret = fw_cfg_dma_transfer(NULL, pos, FW_CFG_DMA_CTL_SKIP); + if (ret < 0) + goto end; + ret = fw_cfg_dma_transfer(buf, count, FW_CFG_DMA_CTL_WRITE); + } + +end: + mutex_unlock(_cfg_dev_lock); + + acpi_release_global_lock(glk); + + return ret; +} +#endif /* CONFIG_CRASH_CORE */ + /* clean up fw_cfg device i/o */ static void fw_cfg_io_cleanup(void) { @@ -188,9 +295,6 @@ static int fw_cfg_do_platform_probe(struct platform_device *pdev) return 0; } -/* fw_cfg revision attribute, in /sys/firmware/qemu_fw_cfg top-level dir
[PATCH v16 11/11] RFC: fw_cfg: do DMA read operation
Modify fw_cfg_read_blob() to use DMA if the device supports it. Return errors, because the operation may fail. So far, only one call in fw_cfg_register_dir_entries() is using kmalloc'ed buf and is thus clearly eligible to DMA read. Initially, I didn't implement DMA read to speed up boot time, but as a first step before introducing DMA write (since read operations were already presents). Even more, I didn't realize fw-cfg entries were being read by the kernel during boot by default. But actally fw-cfg entries are being populated during module probe. I knew DMA improved a lot bios boot time (the main reason the DMA interface was added afaik). Let see the time it would take to read the whole ACPI tables (128kb allocated) # time cat /sys/firmware/qemu_fw_cfg/by_name/etc/acpi/tables/raw - with DMA: sys 0m0.003s - without DMA (-global fw_cfg.dma_enabled=off): sys 0m7.674s FW_CFG_FILE_DIR (0x19) is the only "file" that is read during kernel boot to populate sysfs qemu_fw_cfg directory, and it is quite small (1-2kb). Since it does not expose itself, in order to measure the time it takes to read such small file, I took a comparable sized file of 2048 bytes and exposed it (-fw_cfg test,file=file with a modified read_raw enabling DMA) # perf stat -r 100 cat /sys/firmware/qemu_fw_cfg/by_name/test/raw >/dev/null - with DMA: 0.636037 task-clock (msec) #0.141 CPUs utilized ( +- 1.19% ) - without DMA: 6.430128 task-clock (msec) #0.622 CPUs utilized ( +- 0.22% ) That's a few msec saved during boot by enabling DMA read (the gain would be more substantial if other & bigger fw-cfg entries are read by others from sysfs, unfortunately, it's not clear if we can always enable DMA there) Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> --- drivers/firmware/qemu_fw_cfg.c | 68 ++ 1 file changed, 55 insertions(+), 13 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 14fedbeca724..db1cba4f99bd 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -66,7 +66,6 @@ static void fw_cfg_sel_endianness(u16 key) iowrite16(key, fw_cfg_reg_ctrl); } -#ifdef CONFIG_CRASH_CORE static inline bool fw_cfg_dma_enabled(void) { return (fw_cfg_rev & FW_CFG_VERSION_DMA) && fw_cfg_reg_dma; @@ -124,14 +123,49 @@ static ssize_t fw_cfg_dma_transfer(void *address, u32 length, u32 control) return ret; } -#endif + +/* with acpi & dev locks taken */ +static ssize_t fw_cfg_read_blob_dma(u16 key, + void *buf, loff_t pos, size_t count) +{ + ssize_t ret; + + if (pos == 0) { + ret = fw_cfg_dma_transfer(buf, count, key << 16 + | FW_CFG_DMA_CTL_SELECT + | FW_CFG_DMA_CTL_READ); + } else { + fw_cfg_sel_endianness(key); + ret = fw_cfg_dma_transfer(NULL, pos, FW_CFG_DMA_CTL_SKIP); + if (ret < 0) + return ret; + ret = fw_cfg_dma_transfer(buf, count, + FW_CFG_DMA_CTL_READ); + } + + return ret; +} + +/* with acpi & dev locks taken */ +static ssize_t fw_cfg_read_blob_io(u16 key, + void *buf, loff_t pos, size_t count) +{ + fw_cfg_sel_endianness(key); + while (pos-- > 0) + ioread8(fw_cfg_reg_data); + ioread8_rep(fw_cfg_reg_data, buf, count); + return count; +} /* read chunk of given fw_cfg blob (caller responsible for sanity-check) */ static ssize_t fw_cfg_read_blob(u16 key, - void *buf, loff_t pos, size_t count) + void *buf, loff_t pos, size_t count, + ssize_t (*readfn)(u16 key, void *buf, + loff_t pos, size_t count)) { u32 glk = -1U; acpi_status status; + ssize_t ret; /* If we have ACPI, ensure mutual exclusion against any potential * device access by the firmware, e.g. via AML methods: @@ -145,14 +179,19 @@ static ssize_t fw_cfg_read_blob(u16 key, } mutex_lock(_cfg_dev_lock); - fw_cfg_sel_endianness(key); - while (pos-- > 0) - ioread8(fw_cfg_reg_data); - ioread8_rep(fw_cfg_reg_data, buf, count); + + /* fallback to IO if DMA is not available */ + if (readfn == fw_cfg_read_blob_dma && !fw_cfg_dma_enabled()) { + readfn = fw_cfg_read_blob_io; + } + + ret = readfn(key, buf, pos, count); + mutex_unlock(_cfg_dev_lock); acpi_release_global_lock(glk); - return count; + + return ret; } #ifdef CONFIG_CRASH_CORE @@ -286,7 +325,7 @@
[PATCH v16 11/11] RFC: fw_cfg: do DMA read operation
Modify fw_cfg_read_blob() to use DMA if the device supports it. Return errors, because the operation may fail. So far, only one call in fw_cfg_register_dir_entries() is using kmalloc'ed buf and is thus clearly eligible to DMA read. Initially, I didn't implement DMA read to speed up boot time, but as a first step before introducing DMA write (since read operations were already presents). Even more, I didn't realize fw-cfg entries were being read by the kernel during boot by default. But actally fw-cfg entries are being populated during module probe. I knew DMA improved a lot bios boot time (the main reason the DMA interface was added afaik). Let see the time it would take to read the whole ACPI tables (128kb allocated) # time cat /sys/firmware/qemu_fw_cfg/by_name/etc/acpi/tables/raw - with DMA: sys 0m0.003s - without DMA (-global fw_cfg.dma_enabled=off): sys 0m7.674s FW_CFG_FILE_DIR (0x19) is the only "file" that is read during kernel boot to populate sysfs qemu_fw_cfg directory, and it is quite small (1-2kb). Since it does not expose itself, in order to measure the time it takes to read such small file, I took a comparable sized file of 2048 bytes and exposed it (-fw_cfg test,file=file with a modified read_raw enabling DMA) # perf stat -r 100 cat /sys/firmware/qemu_fw_cfg/by_name/test/raw >/dev/null - with DMA: 0.636037 task-clock (msec) #0.141 CPUs utilized ( +- 1.19% ) - without DMA: 6.430128 task-clock (msec) #0.622 CPUs utilized ( +- 0.22% ) That's a few msec saved during boot by enabling DMA read (the gain would be more substantial if other & bigger fw-cfg entries are read by others from sysfs, unfortunately, it's not clear if we can always enable DMA there) Signed-off-by: Marc-André Lureau --- drivers/firmware/qemu_fw_cfg.c | 68 ++ 1 file changed, 55 insertions(+), 13 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 14fedbeca724..db1cba4f99bd 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -66,7 +66,6 @@ static void fw_cfg_sel_endianness(u16 key) iowrite16(key, fw_cfg_reg_ctrl); } -#ifdef CONFIG_CRASH_CORE static inline bool fw_cfg_dma_enabled(void) { return (fw_cfg_rev & FW_CFG_VERSION_DMA) && fw_cfg_reg_dma; @@ -124,14 +123,49 @@ static ssize_t fw_cfg_dma_transfer(void *address, u32 length, u32 control) return ret; } -#endif + +/* with acpi & dev locks taken */ +static ssize_t fw_cfg_read_blob_dma(u16 key, + void *buf, loff_t pos, size_t count) +{ + ssize_t ret; + + if (pos == 0) { + ret = fw_cfg_dma_transfer(buf, count, key << 16 + | FW_CFG_DMA_CTL_SELECT + | FW_CFG_DMA_CTL_READ); + } else { + fw_cfg_sel_endianness(key); + ret = fw_cfg_dma_transfer(NULL, pos, FW_CFG_DMA_CTL_SKIP); + if (ret < 0) + return ret; + ret = fw_cfg_dma_transfer(buf, count, + FW_CFG_DMA_CTL_READ); + } + + return ret; +} + +/* with acpi & dev locks taken */ +static ssize_t fw_cfg_read_blob_io(u16 key, + void *buf, loff_t pos, size_t count) +{ + fw_cfg_sel_endianness(key); + while (pos-- > 0) + ioread8(fw_cfg_reg_data); + ioread8_rep(fw_cfg_reg_data, buf, count); + return count; +} /* read chunk of given fw_cfg blob (caller responsible for sanity-check) */ static ssize_t fw_cfg_read_blob(u16 key, - void *buf, loff_t pos, size_t count) + void *buf, loff_t pos, size_t count, + ssize_t (*readfn)(u16 key, void *buf, + loff_t pos, size_t count)) { u32 glk = -1U; acpi_status status; + ssize_t ret; /* If we have ACPI, ensure mutual exclusion against any potential * device access by the firmware, e.g. via AML methods: @@ -145,14 +179,19 @@ static ssize_t fw_cfg_read_blob(u16 key, } mutex_lock(_cfg_dev_lock); - fw_cfg_sel_endianness(key); - while (pos-- > 0) - ioread8(fw_cfg_reg_data); - ioread8_rep(fw_cfg_reg_data, buf, count); + + /* fallback to IO if DMA is not available */ + if (readfn == fw_cfg_read_blob_dma && !fw_cfg_dma_enabled()) { + readfn = fw_cfg_read_blob_io; + } + + ret = readfn(key, buf, pos, count); + mutex_unlock(_cfg_dev_lock); acpi_release_global_lock(glk); - return count; + + return ret; } #ifdef CONFIG_CRASH_CORE @@ -286,7 +325,7 @@ static int fw_cfg_do_platform_pro
[PATCH v16 07/11] fw_cfg: add a public uapi header
Create a common header file for well-known values and structures to be shared by the Linux kernel with qemu or other projects. It is based from qemu/docs/specs/fw_cfg.txt which references qemu/include/hw/nvram/fw_cfg_keys.h "for the most up-to-date and authoritative list" & vmcoreinfo.txt. Those files don't have an explicit license, but qemu/hw/nvram/fw_cfg.c is BSD-license, so Michael S. Tsirkin suggested to use the same license. The patch intentionally left out DMA & vmcoreinfo structures & defines, which are added in the commits making usage of it. Suggested-by: Michael S. Tsirkin <m...@redhat.com> Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> --- The related qemu patch making use of it, to be submitted: https://github.com/elmarco/qemu/commit/4884fc9e9c4c4467a371e5a40f3181239e1b70f5 --- MAINTAINERS | 1 + drivers/firmware/qemu_fw_cfg.c | 22 ++ include/uapi/linux/qemu_fw_cfg.h | 66 3 files changed, 69 insertions(+), 20 deletions(-) create mode 100644 include/uapi/linux/qemu_fw_cfg.h diff --git a/MAINTAINERS b/MAINTAINERS index 93a12af4f180..0ce1b3e536fd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11353,6 +11353,7 @@ M: "Michael S. Tsirkin" <m...@redhat.com> L: qemu-de...@nongnu.org S: Maintained F: drivers/firmware/qemu_fw_cfg.c +F: include/uapi/linux/qemu_fw_cfg.h QIB DRIVER M: Dennis Dalessandro <dennis.dalessan...@intel.com> diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 45bfc389b226..5de6bb406fb6 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -32,30 +32,12 @@ #include #include #include +#include MODULE_AUTHOR("Gabriel L. Somlo <so...@cmu.edu>"); MODULE_DESCRIPTION("QEMU fw_cfg sysfs support"); MODULE_LICENSE("GPL"); -/* selector key values for "well-known" fw_cfg entries */ -#define FW_CFG_SIGNATURE 0x00 -#define FW_CFG_ID 0x01 -#define FW_CFG_FILE_DIR 0x19 - -/* size in bytes of fw_cfg signature */ -#define FW_CFG_SIG_SIZE 4 - -/* fw_cfg "file name" is up to 56 characters (including terminating nul) */ -#define FW_CFG_MAX_FILE_PATH 56 - -/* fw_cfg file directory entry type */ -struct fw_cfg_file { - u32 size; - u16 select; - u16 reserved; - char name[FW_CFG_MAX_FILE_PATH]; -}; - /* fw_cfg device i/o register addresses */ static bool fw_cfg_is_mmio; static phys_addr_t fw_cfg_p_base; @@ -616,7 +598,7 @@ MODULE_DEVICE_TABLE(of, fw_cfg_sysfs_mmio_match); #ifdef CONFIG_ACPI static const struct acpi_device_id fw_cfg_sysfs_acpi_match[] = { - { "QEMU0002", }, + { FW_CFG_ACPI_DEVICE_ID, }, {}, }; MODULE_DEVICE_TABLE(acpi, fw_cfg_sysfs_acpi_match); diff --git a/include/uapi/linux/qemu_fw_cfg.h b/include/uapi/linux/qemu_fw_cfg.h new file mode 100644 index ..c698ac3812f6 --- /dev/null +++ b/include/uapi/linux/qemu_fw_cfg.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +#ifndef _LINUX_FW_CFG_H +#define _LINUX_FW_CFG_H + +#include + +#define FW_CFG_ACPI_DEVICE_ID "QEMU0002" + +/* selector key values for "well-known" fw_cfg entries */ +#define FW_CFG_SIGNATURE 0x00 +#define FW_CFG_ID 0x01 +#define FW_CFG_UUID0x02 +#define FW_CFG_RAM_SIZE0x03 +#define FW_CFG_NOGRAPHIC 0x04 +#define FW_CFG_NB_CPUS 0x05 +#define FW_CFG_MACHINE_ID 0x06 +#define FW_CFG_KERNEL_ADDR 0x07 +#define FW_CFG_KERNEL_SIZE 0x08 +#define FW_CFG_KERNEL_CMDLINE 0x09 +#define FW_CFG_INITRD_ADDR 0x0a +#define FW_CFG_INITRD_SIZE 0x0b +#define FW_CFG_BOOT_DEVICE 0x0c +#define FW_CFG_NUMA0x0d +#define FW_CFG_BOOT_MENU 0x0e +#define FW_CFG_MAX_CPUS0x0f +#define FW_CFG_KERNEL_ENTRY0x10 +#define FW_CFG_KERNEL_DATA 0x11 +#define FW_CFG_INITRD_DATA 0x12 +#define FW_CFG_CMDLINE_ADDR0x13 +#define FW_CFG_CMDLINE_SIZE0x14 +#define FW_CFG_CMDLINE_DATA0x15 +#define FW_CFG_SETUP_ADDR 0x16 +#define FW_CFG_SETUP_SIZE 0x17 +#define FW_CFG_SETUP_DATA 0x18 +#define FW_CFG_FILE_DIR0x19 + +#define FW_CFG_FILE_FIRST 0x20 +#define FW_CFG_FILE_SLOTS_MIN 0x10 + +#define FW_CFG_WRITE_CHANNEL 0x4000 +#define FW_CFG_ARCH_LOCAL 0x8000 +#define FW_CFG_ENTRY_MASK (~(FW_CFG_WRITE_CHANNEL | FW_CFG_ARCH_LOCAL)) + +#define FW_CFG_INVALID 0x + +/* width in bytes of fw_cfg control register */ +#define FW_CFG_CTL_SIZE0x02 + +/* fw_cfg "file name" is up to 56 characters (including terminating nul) */ +#define FW_CFG_MAX_FILE_PATH 56 + +/* size in bytes of fw_cfg signature */ +#define FW_CFG_SIG_SIZE 4 + +/* FW_CFG_ID bits */ +#define FW_CFG_VERSION 0x01 + +/* fw_cfg file directory entry
[PATCH v16 08/11] fw_cfg: add DMA register
Add an optional kernel module (or command line) parameter using the following syntax: [qemu_fw_cfg.]ioport=@[::[:]] or [qemu_fw_cfg.]mmio=@[::[:]] and initializes the register address using given or default offset. Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> Reviewed-by: Gabriel Somlo <so...@cmu.edu> --- drivers/firmware/qemu_fw_cfg.c | 53 -- 1 file changed, 41 insertions(+), 12 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 5de6bb406fb6..df028faa2d00 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -10,20 +10,21 @@ * and select subsets of aarch64), a Device Tree node (on arm), or using * a kernel module (or command line) parameter with the following syntax: * - * [qemu_fw_cfg.]ioport=@[::] + * [qemu_fw_cfg.]ioport=@[::[:]] * or - * [qemu_fw_cfg.]mmio=@[::] + * [qemu_fw_cfg.]mmio=@[::[:]] * * where: * := size of ioport or mmio range * := physical base address of ioport or mmio range * := (optional) offset of control register * := (optional) offset of data register + * := (optional) offset of dma register * * e.g.: - * qemu_fw_cfg.ioport=2@0x510:0:1 (the default on x86) + * qemu_fw_cfg.ioport=12@0x510:0:1:4 (the default on x86) * or - * qemu_fw_cfg.mmio=0xA@0x902:8:0 (the default on arm) + * qemu_fw_cfg.mmio=16@0x902:8:0:16 (the default on arm) */ #include @@ -45,6 +46,7 @@ static resource_size_t fw_cfg_p_size; static void __iomem *fw_cfg_dev_base; static void __iomem *fw_cfg_reg_ctrl; static void __iomem *fw_cfg_reg_data; +static void __iomem *fw_cfg_reg_dma; /* atomic access to fw_cfg device (potentially slow i/o, so using mutex) */ static DEFINE_MUTEX(fw_cfg_dev_lock); @@ -104,12 +106,14 @@ static void fw_cfg_io_cleanup(void) # if (defined(CONFIG_ARM) || defined(CONFIG_ARM64)) # define FW_CFG_CTRL_OFF 0x08 # define FW_CFG_DATA_OFF 0x00 +# define FW_CFG_DMA_OFF 0x10 # elif (defined(CONFIG_PPC_PMAC) || defined(CONFIG_SPARC32)) /* ppc/mac,sun4m */ # define FW_CFG_CTRL_OFF 0x00 # define FW_CFG_DATA_OFF 0x02 # elif (defined(CONFIG_X86) || defined(CONFIG_SPARC64)) /* x86, sun4u */ # define FW_CFG_CTRL_OFF 0x00 # define FW_CFG_DATA_OFF 0x01 +# define FW_CFG_DMA_OFF 0x04 # else # error "QEMU FW_CFG not available on this architecture!" # endif @@ -119,7 +123,7 @@ static void fw_cfg_io_cleanup(void) static int fw_cfg_do_platform_probe(struct platform_device *pdev) { char sig[FW_CFG_SIG_SIZE]; - struct resource *range, *ctrl, *data; + struct resource *range, *ctrl, *data, *dma; /* acquire i/o range details */ fw_cfg_is_mmio = false; @@ -156,6 +160,7 @@ static int fw_cfg_do_platform_probe(struct platform_device *pdev) /* were custom register offsets provided (e.g. on the command line)? */ ctrl = platform_get_resource_byname(pdev, IORESOURCE_REG, "ctrl"); data = platform_get_resource_byname(pdev, IORESOURCE_REG, "data"); + dma = platform_get_resource_byname(pdev, IORESOURCE_REG, "dma"); if (ctrl && data) { fw_cfg_reg_ctrl = fw_cfg_dev_base + ctrl->start; fw_cfg_reg_data = fw_cfg_dev_base + data->start; @@ -165,6 +170,13 @@ static int fw_cfg_do_platform_probe(struct platform_device *pdev) fw_cfg_reg_data = fw_cfg_dev_base + FW_CFG_DATA_OFF; } + if (dma) + fw_cfg_reg_dma = fw_cfg_dev_base + dma->start; +#ifdef FW_CFG_DMA_OFF + else + fw_cfg_reg_dma = fw_cfg_dev_base + FW_CFG_DMA_OFF; +#endif + /* verify fw_cfg device signature */ if (fw_cfg_read_blob(FW_CFG_SIGNATURE, sig, 0, FW_CFG_SIG_SIZE) < 0 || @@ -630,6 +642,7 @@ static struct platform_device *fw_cfg_cmdline_dev; /* use special scanf/printf modifier for phys_addr_t, resource_size_t */ #define PH_ADDR_SCAN_FMT "@%" __PHYS_ADDR_PREFIX "i%n" \ ":%" __PHYS_ADDR_PREFIX "i" \ +":%" __PHYS_ADDR_PREFIX "i%n" \ ":%" __PHYS_ADDR_PREFIX "i%n" #define PH_ADDR_PR_1_FMT "0x%" __PHYS_ADDR_PREFIX "x@" \ @@ -639,12 +652,15 @@ static struct platform_device *fw_cfg_cmdline_dev; ":%" __PHYS_ADDR_PREFIX "u" \ ":%" __PHYS_ADDR_PREFIX "u" +#define PH_ADDR_PR_4_FMT PH_ADDR_PR_3_FMT \ +":%" __PHYS_ADDR_PREFIX "u" + static int fw_cfg_cmdline_set(const char *arg, const struct kernel_param *kp) { - struct resource res[3] = {}; + struct resource res[4]
[PATCH v16 08/11] fw_cfg: add DMA register
Add an optional kernel module (or command line) parameter using the following syntax: [qemu_fw_cfg.]ioport=@[::[:]] or [qemu_fw_cfg.]mmio=@[::[:]] and initializes the register address using given or default offset. Signed-off-by: Marc-André Lureau Reviewed-by: Gabriel Somlo --- drivers/firmware/qemu_fw_cfg.c | 53 -- 1 file changed, 41 insertions(+), 12 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 5de6bb406fb6..df028faa2d00 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -10,20 +10,21 @@ * and select subsets of aarch64), a Device Tree node (on arm), or using * a kernel module (or command line) parameter with the following syntax: * - * [qemu_fw_cfg.]ioport=@[::] + * [qemu_fw_cfg.]ioport=@[::[:]] * or - * [qemu_fw_cfg.]mmio=@[::] + * [qemu_fw_cfg.]mmio=@[::[:]] * * where: * := size of ioport or mmio range * := physical base address of ioport or mmio range * := (optional) offset of control register * := (optional) offset of data register + * := (optional) offset of dma register * * e.g.: - * qemu_fw_cfg.ioport=2@0x510:0:1 (the default on x86) + * qemu_fw_cfg.ioport=12@0x510:0:1:4 (the default on x86) * or - * qemu_fw_cfg.mmio=0xA@0x902:8:0 (the default on arm) + * qemu_fw_cfg.mmio=16@0x902:8:0:16 (the default on arm) */ #include @@ -45,6 +46,7 @@ static resource_size_t fw_cfg_p_size; static void __iomem *fw_cfg_dev_base; static void __iomem *fw_cfg_reg_ctrl; static void __iomem *fw_cfg_reg_data; +static void __iomem *fw_cfg_reg_dma; /* atomic access to fw_cfg device (potentially slow i/o, so using mutex) */ static DEFINE_MUTEX(fw_cfg_dev_lock); @@ -104,12 +106,14 @@ static void fw_cfg_io_cleanup(void) # if (defined(CONFIG_ARM) || defined(CONFIG_ARM64)) # define FW_CFG_CTRL_OFF 0x08 # define FW_CFG_DATA_OFF 0x00 +# define FW_CFG_DMA_OFF 0x10 # elif (defined(CONFIG_PPC_PMAC) || defined(CONFIG_SPARC32)) /* ppc/mac,sun4m */ # define FW_CFG_CTRL_OFF 0x00 # define FW_CFG_DATA_OFF 0x02 # elif (defined(CONFIG_X86) || defined(CONFIG_SPARC64)) /* x86, sun4u */ # define FW_CFG_CTRL_OFF 0x00 # define FW_CFG_DATA_OFF 0x01 +# define FW_CFG_DMA_OFF 0x04 # else # error "QEMU FW_CFG not available on this architecture!" # endif @@ -119,7 +123,7 @@ static void fw_cfg_io_cleanup(void) static int fw_cfg_do_platform_probe(struct platform_device *pdev) { char sig[FW_CFG_SIG_SIZE]; - struct resource *range, *ctrl, *data; + struct resource *range, *ctrl, *data, *dma; /* acquire i/o range details */ fw_cfg_is_mmio = false; @@ -156,6 +160,7 @@ static int fw_cfg_do_platform_probe(struct platform_device *pdev) /* were custom register offsets provided (e.g. on the command line)? */ ctrl = platform_get_resource_byname(pdev, IORESOURCE_REG, "ctrl"); data = platform_get_resource_byname(pdev, IORESOURCE_REG, "data"); + dma = platform_get_resource_byname(pdev, IORESOURCE_REG, "dma"); if (ctrl && data) { fw_cfg_reg_ctrl = fw_cfg_dev_base + ctrl->start; fw_cfg_reg_data = fw_cfg_dev_base + data->start; @@ -165,6 +170,13 @@ static int fw_cfg_do_platform_probe(struct platform_device *pdev) fw_cfg_reg_data = fw_cfg_dev_base + FW_CFG_DATA_OFF; } + if (dma) + fw_cfg_reg_dma = fw_cfg_dev_base + dma->start; +#ifdef FW_CFG_DMA_OFF + else + fw_cfg_reg_dma = fw_cfg_dev_base + FW_CFG_DMA_OFF; +#endif + /* verify fw_cfg device signature */ if (fw_cfg_read_blob(FW_CFG_SIGNATURE, sig, 0, FW_CFG_SIG_SIZE) < 0 || @@ -630,6 +642,7 @@ static struct platform_device *fw_cfg_cmdline_dev; /* use special scanf/printf modifier for phys_addr_t, resource_size_t */ #define PH_ADDR_SCAN_FMT "@%" __PHYS_ADDR_PREFIX "i%n" \ ":%" __PHYS_ADDR_PREFIX "i" \ +":%" __PHYS_ADDR_PREFIX "i%n" \ ":%" __PHYS_ADDR_PREFIX "i%n" #define PH_ADDR_PR_1_FMT "0x%" __PHYS_ADDR_PREFIX "x@" \ @@ -639,12 +652,15 @@ static struct platform_device *fw_cfg_cmdline_dev; ":%" __PHYS_ADDR_PREFIX "u" \ ":%" __PHYS_ADDR_PREFIX "u" +#define PH_ADDR_PR_4_FMT PH_ADDR_PR_3_FMT \ +":%" __PHYS_ADDR_PREFIX "u" + static int fw_cfg_cmdline_set(const char *arg, const struct kernel_param *kp) { - struct resource res[3] = {}; + struct resource res[4] = {}; char *str; phys_addr_t base; -
[PATCH v16 07/11] fw_cfg: add a public uapi header
Create a common header file for well-known values and structures to be shared by the Linux kernel with qemu or other projects. It is based from qemu/docs/specs/fw_cfg.txt which references qemu/include/hw/nvram/fw_cfg_keys.h "for the most up-to-date and authoritative list" & vmcoreinfo.txt. Those files don't have an explicit license, but qemu/hw/nvram/fw_cfg.c is BSD-license, so Michael S. Tsirkin suggested to use the same license. The patch intentionally left out DMA & vmcoreinfo structures & defines, which are added in the commits making usage of it. Suggested-by: Michael S. Tsirkin Signed-off-by: Marc-André Lureau --- The related qemu patch making use of it, to be submitted: https://github.com/elmarco/qemu/commit/4884fc9e9c4c4467a371e5a40f3181239e1b70f5 --- MAINTAINERS | 1 + drivers/firmware/qemu_fw_cfg.c | 22 ++ include/uapi/linux/qemu_fw_cfg.h | 66 3 files changed, 69 insertions(+), 20 deletions(-) create mode 100644 include/uapi/linux/qemu_fw_cfg.h diff --git a/MAINTAINERS b/MAINTAINERS index 93a12af4f180..0ce1b3e536fd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11353,6 +11353,7 @@ M: "Michael S. Tsirkin" L: qemu-de...@nongnu.org S: Maintained F: drivers/firmware/qemu_fw_cfg.c +F: include/uapi/linux/qemu_fw_cfg.h QIB DRIVER M: Dennis Dalessandro diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 45bfc389b226..5de6bb406fb6 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -32,30 +32,12 @@ #include #include #include +#include MODULE_AUTHOR("Gabriel L. Somlo "); MODULE_DESCRIPTION("QEMU fw_cfg sysfs support"); MODULE_LICENSE("GPL"); -/* selector key values for "well-known" fw_cfg entries */ -#define FW_CFG_SIGNATURE 0x00 -#define FW_CFG_ID 0x01 -#define FW_CFG_FILE_DIR 0x19 - -/* size in bytes of fw_cfg signature */ -#define FW_CFG_SIG_SIZE 4 - -/* fw_cfg "file name" is up to 56 characters (including terminating nul) */ -#define FW_CFG_MAX_FILE_PATH 56 - -/* fw_cfg file directory entry type */ -struct fw_cfg_file { - u32 size; - u16 select; - u16 reserved; - char name[FW_CFG_MAX_FILE_PATH]; -}; - /* fw_cfg device i/o register addresses */ static bool fw_cfg_is_mmio; static phys_addr_t fw_cfg_p_base; @@ -616,7 +598,7 @@ MODULE_DEVICE_TABLE(of, fw_cfg_sysfs_mmio_match); #ifdef CONFIG_ACPI static const struct acpi_device_id fw_cfg_sysfs_acpi_match[] = { - { "QEMU0002", }, + { FW_CFG_ACPI_DEVICE_ID, }, {}, }; MODULE_DEVICE_TABLE(acpi, fw_cfg_sysfs_acpi_match); diff --git a/include/uapi/linux/qemu_fw_cfg.h b/include/uapi/linux/qemu_fw_cfg.h new file mode 100644 index ..c698ac3812f6 --- /dev/null +++ b/include/uapi/linux/qemu_fw_cfg.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +#ifndef _LINUX_FW_CFG_H +#define _LINUX_FW_CFG_H + +#include + +#define FW_CFG_ACPI_DEVICE_ID "QEMU0002" + +/* selector key values for "well-known" fw_cfg entries */ +#define FW_CFG_SIGNATURE 0x00 +#define FW_CFG_ID 0x01 +#define FW_CFG_UUID0x02 +#define FW_CFG_RAM_SIZE0x03 +#define FW_CFG_NOGRAPHIC 0x04 +#define FW_CFG_NB_CPUS 0x05 +#define FW_CFG_MACHINE_ID 0x06 +#define FW_CFG_KERNEL_ADDR 0x07 +#define FW_CFG_KERNEL_SIZE 0x08 +#define FW_CFG_KERNEL_CMDLINE 0x09 +#define FW_CFG_INITRD_ADDR 0x0a +#define FW_CFG_INITRD_SIZE 0x0b +#define FW_CFG_BOOT_DEVICE 0x0c +#define FW_CFG_NUMA0x0d +#define FW_CFG_BOOT_MENU 0x0e +#define FW_CFG_MAX_CPUS0x0f +#define FW_CFG_KERNEL_ENTRY0x10 +#define FW_CFG_KERNEL_DATA 0x11 +#define FW_CFG_INITRD_DATA 0x12 +#define FW_CFG_CMDLINE_ADDR0x13 +#define FW_CFG_CMDLINE_SIZE0x14 +#define FW_CFG_CMDLINE_DATA0x15 +#define FW_CFG_SETUP_ADDR 0x16 +#define FW_CFG_SETUP_SIZE 0x17 +#define FW_CFG_SETUP_DATA 0x18 +#define FW_CFG_FILE_DIR0x19 + +#define FW_CFG_FILE_FIRST 0x20 +#define FW_CFG_FILE_SLOTS_MIN 0x10 + +#define FW_CFG_WRITE_CHANNEL 0x4000 +#define FW_CFG_ARCH_LOCAL 0x8000 +#define FW_CFG_ENTRY_MASK (~(FW_CFG_WRITE_CHANNEL | FW_CFG_ARCH_LOCAL)) + +#define FW_CFG_INVALID 0x + +/* width in bytes of fw_cfg control register */ +#define FW_CFG_CTL_SIZE0x02 + +/* fw_cfg "file name" is up to 56 characters (including terminating nul) */ +#define FW_CFG_MAX_FILE_PATH 56 + +/* size in bytes of fw_cfg signature */ +#define FW_CFG_SIG_SIZE 4 + +/* FW_CFG_ID bits */ +#define FW_CFG_VERSION 0x01 + +/* fw_cfg file directory entry type */ +struct fw_cfg_file { + __be32 size; + __be16 select; + __u16 reserved; + char name[FW_CFG_MAX_FILE_PATH]; +}; + +#endif -- 2.16.1.73.g5832b7e9f2
[PATCH v16 03/11] fw_cfg: fix sparse warning reading FW_CFG_ID
Use a restricted type for reading FW_CFG_ID, fixing sparse warning: drivers/firmware/qemu_fw_cfg.c:540:22: warning: cast to restricted __le32 Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> --- drivers/firmware/qemu_fw_cfg.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 7978844f6b37..0eb155fdfb35 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -530,6 +530,7 @@ static inline void fw_cfg_kobj_cleanup(struct kobject *kobj) static int fw_cfg_sysfs_probe(struct platform_device *pdev) { int err; + __le32 rev; /* NOTE: If we supported multiple fw_cfg devices, we'd first create * a subdirectory named after e.g. pdev->id, then hang per-device @@ -555,8 +556,8 @@ static int fw_cfg_sysfs_probe(struct platform_device *pdev) goto err_probe; /* get revision number, add matching top-level attribute */ - fw_cfg_read_blob(FW_CFG_ID, _cfg_rev, 0, sizeof(fw_cfg_rev)); - fw_cfg_rev = le32_to_cpu(fw_cfg_rev); + fw_cfg_read_blob(FW_CFG_ID, , 0, sizeof(rev)); + fw_cfg_rev = le32_to_cpu(rev); err = sysfs_create_file(fw_cfg_top_ko, _cfg_rev_attr.attr); if (err) goto err_rev; -- 2.16.1.73.g5832b7e9f2
[PATCH v16 03/11] fw_cfg: fix sparse warning reading FW_CFG_ID
Use a restricted type for reading FW_CFG_ID, fixing sparse warning: drivers/firmware/qemu_fw_cfg.c:540:22: warning: cast to restricted __le32 Signed-off-by: Marc-André Lureau --- drivers/firmware/qemu_fw_cfg.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 7978844f6b37..0eb155fdfb35 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -530,6 +530,7 @@ static inline void fw_cfg_kobj_cleanup(struct kobject *kobj) static int fw_cfg_sysfs_probe(struct platform_device *pdev) { int err; + __le32 rev; /* NOTE: If we supported multiple fw_cfg devices, we'd first create * a subdirectory named after e.g. pdev->id, then hang per-device @@ -555,8 +556,8 @@ static int fw_cfg_sysfs_probe(struct platform_device *pdev) goto err_probe; /* get revision number, add matching top-level attribute */ - fw_cfg_read_blob(FW_CFG_ID, _cfg_rev, 0, sizeof(fw_cfg_rev)); - fw_cfg_rev = le32_to_cpu(fw_cfg_rev); + fw_cfg_read_blob(FW_CFG_ID, , 0, sizeof(rev)); + fw_cfg_rev = le32_to_cpu(rev); err = sysfs_create_file(fw_cfg_top_ko, _cfg_rev_attr.attr); if (err) goto err_rev; -- 2.16.1.73.g5832b7e9f2
[PATCH v16 02/11] fw_cfg: fix sparse warnings with fw_cfg_file
Modify fw_cfg_sysfs_entry to store entry values, instead of reusing the restricted types. Fixes warnings such as: $ make C=1 CF=-D__CHECK_ENDIAN__ drivers/firmware/qemu_fw_cfg.o drivers/firmware/qemu_fw_cfg.c:491:29: warning: incorrect type in assignment (different base types) drivers/firmware/qemu_fw_cfg.c:491:29:expected restricted __be32 [usertype] size drivers/firmware/qemu_fw_cfg.c:491:29:got unsigned int drivers/firmware/qemu_fw_cfg.c:492:31: warning: incorrect type in assignment (different base types) drivers/firmware/qemu_fw_cfg.c:492:31:expected restricted __be16 [usertype] select drivers/firmware/qemu_fw_cfg.c:492:31:got int Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> --- drivers/firmware/qemu_fw_cfg.c | 28 +++- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index e7ea2b3b1d11..7978844f6b37 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -211,7 +211,9 @@ static const struct { /* fw_cfg_sysfs_entry type */ struct fw_cfg_sysfs_entry { struct kobject kobj; - struct fw_cfg_file f; + u32 size; + u16 select; + char name[FW_CFG_MAX_FILE_PATH]; struct list_head list; }; @@ -275,17 +277,17 @@ struct fw_cfg_sysfs_attribute fw_cfg_sysfs_attr_##_attr = { \ static ssize_t fw_cfg_sysfs_show_size(struct fw_cfg_sysfs_entry *e, char *buf) { - return sprintf(buf, "%u\n", e->f.size); + return sprintf(buf, "%u\n", e->size); } static ssize_t fw_cfg_sysfs_show_key(struct fw_cfg_sysfs_entry *e, char *buf) { - return sprintf(buf, "%u\n", e->f.select); + return sprintf(buf, "%u\n", e->select); } static ssize_t fw_cfg_sysfs_show_name(struct fw_cfg_sysfs_entry *e, char *buf) { - return sprintf(buf, "%s\n", e->f.name); + return sprintf(buf, "%s\n", e->name); } static FW_CFG_SYSFS_ATTR(size); @@ -336,13 +338,13 @@ static ssize_t fw_cfg_sysfs_read_raw(struct file *filp, struct kobject *kobj, { struct fw_cfg_sysfs_entry *entry = to_entry(kobj); - if (pos > entry->f.size) + if (pos > entry->size) return -EINVAL; - if (count > entry->f.size - pos) - count = entry->f.size - pos; + if (count > entry->size - pos) + count = entry->size - pos; - fw_cfg_read_blob(entry->f.select, buf, pos, count); + fw_cfg_read_blob(entry->select, buf, pos, count); return count; } @@ -461,11 +463,13 @@ static int fw_cfg_register_file(const struct fw_cfg_file *f) return -ENOMEM; /* set file entry information */ - memcpy(>f, f, sizeof(struct fw_cfg_file)); + entry->size = be32_to_cpu(f->size); + entry->select = be16_to_cpu(f->select); + memcpy(entry->name, f->name, FW_CFG_MAX_FILE_PATH); /* register entry under "/sys/firmware/qemu_fw_cfg/by_key/" */ err = kobject_init_and_add(>kobj, _cfg_sysfs_entry_ktype, - fw_cfg_sel_ko, "%d", entry->f.select); + fw_cfg_sel_ko, "%d", entry->select); if (err) goto err_register; @@ -475,7 +479,7 @@ static int fw_cfg_register_file(const struct fw_cfg_file *f) goto err_add_raw; /* try adding "/sys/firmware/qemu_fw_cfg/by_name/" symlink */ - fw_cfg_build_symlink(fw_cfg_fname_kset, >kobj, entry->f.name); + fw_cfg_build_symlink(fw_cfg_fname_kset, >kobj, entry->name); /* success, add entry to global cache */ fw_cfg_sysfs_cache_enlist(entry); @@ -507,8 +511,6 @@ static int fw_cfg_register_dir_entries(void) fw_cfg_read_blob(FW_CFG_FILE_DIR, dir, sizeof(count), dir_size); for (i = 0; i < count; i++) { - dir[i].size = be32_to_cpu(dir[i].size); - dir[i].select = be16_to_cpu(dir[i].select); ret = fw_cfg_register_file([i]); if (ret) break; -- 2.16.1.73.g5832b7e9f2
[PATCH v16 02/11] fw_cfg: fix sparse warnings with fw_cfg_file
Modify fw_cfg_sysfs_entry to store entry values, instead of reusing the restricted types. Fixes warnings such as: $ make C=1 CF=-D__CHECK_ENDIAN__ drivers/firmware/qemu_fw_cfg.o drivers/firmware/qemu_fw_cfg.c:491:29: warning: incorrect type in assignment (different base types) drivers/firmware/qemu_fw_cfg.c:491:29:expected restricted __be32 [usertype] size drivers/firmware/qemu_fw_cfg.c:491:29:got unsigned int drivers/firmware/qemu_fw_cfg.c:492:31: warning: incorrect type in assignment (different base types) drivers/firmware/qemu_fw_cfg.c:492:31:expected restricted __be16 [usertype] select drivers/firmware/qemu_fw_cfg.c:492:31:got int Signed-off-by: Marc-André Lureau --- drivers/firmware/qemu_fw_cfg.c | 28 +++- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index e7ea2b3b1d11..7978844f6b37 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -211,7 +211,9 @@ static const struct { /* fw_cfg_sysfs_entry type */ struct fw_cfg_sysfs_entry { struct kobject kobj; - struct fw_cfg_file f; + u32 size; + u16 select; + char name[FW_CFG_MAX_FILE_PATH]; struct list_head list; }; @@ -275,17 +277,17 @@ struct fw_cfg_sysfs_attribute fw_cfg_sysfs_attr_##_attr = { \ static ssize_t fw_cfg_sysfs_show_size(struct fw_cfg_sysfs_entry *e, char *buf) { - return sprintf(buf, "%u\n", e->f.size); + return sprintf(buf, "%u\n", e->size); } static ssize_t fw_cfg_sysfs_show_key(struct fw_cfg_sysfs_entry *e, char *buf) { - return sprintf(buf, "%u\n", e->f.select); + return sprintf(buf, "%u\n", e->select); } static ssize_t fw_cfg_sysfs_show_name(struct fw_cfg_sysfs_entry *e, char *buf) { - return sprintf(buf, "%s\n", e->f.name); + return sprintf(buf, "%s\n", e->name); } static FW_CFG_SYSFS_ATTR(size); @@ -336,13 +338,13 @@ static ssize_t fw_cfg_sysfs_read_raw(struct file *filp, struct kobject *kobj, { struct fw_cfg_sysfs_entry *entry = to_entry(kobj); - if (pos > entry->f.size) + if (pos > entry->size) return -EINVAL; - if (count > entry->f.size - pos) - count = entry->f.size - pos; + if (count > entry->size - pos) + count = entry->size - pos; - fw_cfg_read_blob(entry->f.select, buf, pos, count); + fw_cfg_read_blob(entry->select, buf, pos, count); return count; } @@ -461,11 +463,13 @@ static int fw_cfg_register_file(const struct fw_cfg_file *f) return -ENOMEM; /* set file entry information */ - memcpy(>f, f, sizeof(struct fw_cfg_file)); + entry->size = be32_to_cpu(f->size); + entry->select = be16_to_cpu(f->select); + memcpy(entry->name, f->name, FW_CFG_MAX_FILE_PATH); /* register entry under "/sys/firmware/qemu_fw_cfg/by_key/" */ err = kobject_init_and_add(>kobj, _cfg_sysfs_entry_ktype, - fw_cfg_sel_ko, "%d", entry->f.select); + fw_cfg_sel_ko, "%d", entry->select); if (err) goto err_register; @@ -475,7 +479,7 @@ static int fw_cfg_register_file(const struct fw_cfg_file *f) goto err_add_raw; /* try adding "/sys/firmware/qemu_fw_cfg/by_name/" symlink */ - fw_cfg_build_symlink(fw_cfg_fname_kset, >kobj, entry->f.name); + fw_cfg_build_symlink(fw_cfg_fname_kset, >kobj, entry->name); /* success, add entry to global cache */ fw_cfg_sysfs_cache_enlist(entry); @@ -507,8 +511,6 @@ static int fw_cfg_register_dir_entries(void) fw_cfg_read_blob(FW_CFG_FILE_DIR, dir, sizeof(count), dir_size); for (i = 0; i < count; i++) { - dir[i].size = be32_to_cpu(dir[i].size); - dir[i].select = be16_to_cpu(dir[i].select); ret = fw_cfg_register_file([i]); if (ret) break; -- 2.16.1.73.g5832b7e9f2
[PATCH v16 00/11] fw_cfg: add DMA operations & etc/vmcoreinfo support
Hi, This series adds DMA operations support to the qemu fw_cfg kernel module and populates "etc/vmcoreinfo" with vmcoreinfo location details (entry added since qemu 2.11 with -device vmcoreinfo). v16: - patch reordering - commit message updates - rename fw_cfg.h -> qemu_fw_cfg.h - fix warning when CONFIG_CRASH_CORE=n without RFC DMA read patch applied - use a function pointer rather than a dma hint to read_blob() v15: - fix fw_cfg.h uapi header #include - use BSD license for fw_cfg.h uapi header - move the uapi defines/structs for DMA & vmcoreinfo in the corresponding patch - use cpu_relax() instead of usleep_range(50, 100); - replace do { } while(true) by for (;;) - fix the rmb() call location - add a preliminary patch to handle error from fw_cfg_write_blob() - rewrite fw_cfg_sel_endianness() to wrap iowrite() calls v14: - add "fw_cfg: add a public uapi header" - fix sparse warnings & don't introduce new warnings - add memory barriers to force IO ordering - split fw_cfg_read_blob() in fw_cfg_read_blob_io() and fw_cfg_read_blob_dma() - add error handling to fw_cfg_read_blob() callers - minor stylistic changes v13: - reorder patch series, introduce DMA write before DMA read - do some measurements of DMA read speed-ups v12: - fix virt_to_phys(NULL) panic with CONFIG_DEBUG_VIRTUAL=y - do not use DMA read, except for kmalloc() memory we allocated ourself (only fw_cfg_register_dir_entries() so far) v11: - add #include in last patch, fixing kbuild .config test Marc-André Lureau (11): fw_cfg: fix sparse warnings in fw_cfg_sel_endianness() fw_cfg: fix sparse warnings with fw_cfg_file fw_cfg: fix sparse warning reading FW_CFG_ID fw_cfg: fix sparse warnings around FW_CFG_FILE_DIR read fw_cfg: remove inline from fw_cfg_read_blob() fw_cfg: handle fw_cfg_read_blob() error fw_cfg: add a public uapi header fw_cfg: add DMA register crash: export paddr_vmcoreinfo_note() fw_cfg: write vmcoreinfo details RFC: fw_cfg: do DMA read operation MAINTAINERS | 1 + drivers/firmware/qemu_fw_cfg.c | 339 +++ include/uapi/linux/qemu_fw_cfg.h | 97 +++ kernel/crash_core.c | 1 + 4 files changed, 374 insertions(+), 64 deletions(-) create mode 100644 include/uapi/linux/qemu_fw_cfg.h -- 2.16.1.73.g5832b7e9f2
[PATCH v16 00/11] fw_cfg: add DMA operations & etc/vmcoreinfo support
Hi, This series adds DMA operations support to the qemu fw_cfg kernel module and populates "etc/vmcoreinfo" with vmcoreinfo location details (entry added since qemu 2.11 with -device vmcoreinfo). v16: - patch reordering - commit message updates - rename fw_cfg.h -> qemu_fw_cfg.h - fix warning when CONFIG_CRASH_CORE=n without RFC DMA read patch applied - use a function pointer rather than a dma hint to read_blob() v15: - fix fw_cfg.h uapi header #include - use BSD license for fw_cfg.h uapi header - move the uapi defines/structs for DMA & vmcoreinfo in the corresponding patch - use cpu_relax() instead of usleep_range(50, 100); - replace do { } while(true) by for (;;) - fix the rmb() call location - add a preliminary patch to handle error from fw_cfg_write_blob() - rewrite fw_cfg_sel_endianness() to wrap iowrite() calls v14: - add "fw_cfg: add a public uapi header" - fix sparse warnings & don't introduce new warnings - add memory barriers to force IO ordering - split fw_cfg_read_blob() in fw_cfg_read_blob_io() and fw_cfg_read_blob_dma() - add error handling to fw_cfg_read_blob() callers - minor stylistic changes v13: - reorder patch series, introduce DMA write before DMA read - do some measurements of DMA read speed-ups v12: - fix virt_to_phys(NULL) panic with CONFIG_DEBUG_VIRTUAL=y - do not use DMA read, except for kmalloc() memory we allocated ourself (only fw_cfg_register_dir_entries() so far) v11: - add #include in last patch, fixing kbuild .config test Marc-André Lureau (11): fw_cfg: fix sparse warnings in fw_cfg_sel_endianness() fw_cfg: fix sparse warnings with fw_cfg_file fw_cfg: fix sparse warning reading FW_CFG_ID fw_cfg: fix sparse warnings around FW_CFG_FILE_DIR read fw_cfg: remove inline from fw_cfg_read_blob() fw_cfg: handle fw_cfg_read_blob() error fw_cfg: add a public uapi header fw_cfg: add DMA register crash: export paddr_vmcoreinfo_note() fw_cfg: write vmcoreinfo details RFC: fw_cfg: do DMA read operation MAINTAINERS | 1 + drivers/firmware/qemu_fw_cfg.c | 339 +++ include/uapi/linux/qemu_fw_cfg.h | 97 +++ kernel/crash_core.c | 1 + 4 files changed, 374 insertions(+), 64 deletions(-) create mode 100644 include/uapi/linux/qemu_fw_cfg.h -- 2.16.1.73.g5832b7e9f2
Re: [PATCH] fw_cfg: avoid unused function warning
Hi On Wed, Feb 28, 2018 at 2:33 PM, Arnd Bergmannwrote: > The newly introduced fw_cfg_dma_transfer() function is unused when > CONFIG_CRASH_CORE is disabled: > > drivers/firmware/qemu_fw_cfg.c:89:16: error: 'fw_cfg_dma_transfer' defined > but not used [-Werror=unused-function] > static ssize_t fw_cfg_dma_transfer(void *address, u32 length, u32 control) > > This moves it into the #ifdef section that hides its caller to avoid the > warning. > > Fixes: 47e78bfb5426 ("fw_cfg: write vmcoreinfo details") > Signed-off-by: Arnd Bergmann I don't know from which tree you found this, I suppose from mst/linux-next (which btw, I don't know which version got partially applied). This is a regression in v13, because dma read is last patch now, as RFC. Fixed in upcoming v16. thanks > --- > drivers/firmware/qemu_fw_cfg.c | 60 > +- > 1 file changed, 30 insertions(+), 30 deletions(-) > > diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c > index 3015e77aebca..f002bb40519b 100644 > --- a/drivers/firmware/qemu_fw_cfg.c > +++ b/drivers/firmware/qemu_fw_cfg.c > @@ -66,6 +66,36 @@ static void fw_cfg_sel_endianness(u16 key) > iowrite16(key, fw_cfg_reg_ctrl); > } > > +/* read chunk of given fw_cfg blob (caller responsible for sanity-check) */ > +static ssize_t fw_cfg_read_blob(u16 key, > + void *buf, loff_t pos, size_t count) > +{ > + u32 glk = -1U; > + acpi_status status; > + > + /* If we have ACPI, ensure mutual exclusion against any potential > +* device access by the firmware, e.g. via AML methods: > +*/ > + status = acpi_acquire_global_lock(ACPI_WAIT_FOREVER, ); > + if (ACPI_FAILURE(status) && status != AE_NOT_CONFIGURED) { > + /* Should never get here */ > + WARN(1, "fw_cfg_read_blob: Failed to lock ACPI!\n"); > + memset(buf, 0, count); > + return -EINVAL; > + } > + > + mutex_lock(_cfg_dev_lock); > + fw_cfg_sel_endianness(key); > + while (pos-- > 0) > + ioread8(fw_cfg_reg_data); > + ioread8_rep(fw_cfg_reg_data, buf, count); > + mutex_unlock(_cfg_dev_lock); > + > + acpi_release_global_lock(glk); > + return count; > +} > + > +#ifdef CONFIG_CRASH_CORE > static inline bool fw_cfg_dma_enabled(void) > { > return (fw_cfg_rev & FW_CFG_VERSION_DMA) && fw_cfg_reg_dma; > @@ -124,36 +154,6 @@ static ssize_t fw_cfg_dma_transfer(void *address, u32 > length, u32 control) > return ret; > } > > -/* read chunk of given fw_cfg blob (caller responsible for sanity-check) */ > -static ssize_t fw_cfg_read_blob(u16 key, > - void *buf, loff_t pos, size_t count) > -{ > - u32 glk = -1U; > - acpi_status status; > - > - /* If we have ACPI, ensure mutual exclusion against any potential > -* device access by the firmware, e.g. via AML methods: > -*/ > - status = acpi_acquire_global_lock(ACPI_WAIT_FOREVER, ); > - if (ACPI_FAILURE(status) && status != AE_NOT_CONFIGURED) { > - /* Should never get here */ > - WARN(1, "fw_cfg_read_blob: Failed to lock ACPI!\n"); > - memset(buf, 0, count); > - return -EINVAL; > - } > - > - mutex_lock(_cfg_dev_lock); > - fw_cfg_sel_endianness(key); > - while (pos-- > 0) > - ioread8(fw_cfg_reg_data); > - ioread8_rep(fw_cfg_reg_data, buf, count); > - mutex_unlock(_cfg_dev_lock); > - > - acpi_release_global_lock(glk); > - return count; > -} > - > -#ifdef CONFIG_CRASH_CORE > /* write chunk of given fw_cfg blob (caller responsible for sanity-check) */ > static ssize_t fw_cfg_write_blob(u16 key, > void *buf, loff_t pos, size_t count) > -- > 2.9.0 >
Re: [PATCH] fw_cfg: avoid unused function warning
Hi On Wed, Feb 28, 2018 at 2:33 PM, Arnd Bergmann wrote: > The newly introduced fw_cfg_dma_transfer() function is unused when > CONFIG_CRASH_CORE is disabled: > > drivers/firmware/qemu_fw_cfg.c:89:16: error: 'fw_cfg_dma_transfer' defined > but not used [-Werror=unused-function] > static ssize_t fw_cfg_dma_transfer(void *address, u32 length, u32 control) > > This moves it into the #ifdef section that hides its caller to avoid the > warning. > > Fixes: 47e78bfb5426 ("fw_cfg: write vmcoreinfo details") > Signed-off-by: Arnd Bergmann I don't know from which tree you found this, I suppose from mst/linux-next (which btw, I don't know which version got partially applied). This is a regression in v13, because dma read is last patch now, as RFC. Fixed in upcoming v16. thanks > --- > drivers/firmware/qemu_fw_cfg.c | 60 > +- > 1 file changed, 30 insertions(+), 30 deletions(-) > > diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c > index 3015e77aebca..f002bb40519b 100644 > --- a/drivers/firmware/qemu_fw_cfg.c > +++ b/drivers/firmware/qemu_fw_cfg.c > @@ -66,6 +66,36 @@ static void fw_cfg_sel_endianness(u16 key) > iowrite16(key, fw_cfg_reg_ctrl); > } > > +/* read chunk of given fw_cfg blob (caller responsible for sanity-check) */ > +static ssize_t fw_cfg_read_blob(u16 key, > + void *buf, loff_t pos, size_t count) > +{ > + u32 glk = -1U; > + acpi_status status; > + > + /* If we have ACPI, ensure mutual exclusion against any potential > +* device access by the firmware, e.g. via AML methods: > +*/ > + status = acpi_acquire_global_lock(ACPI_WAIT_FOREVER, ); > + if (ACPI_FAILURE(status) && status != AE_NOT_CONFIGURED) { > + /* Should never get here */ > + WARN(1, "fw_cfg_read_blob: Failed to lock ACPI!\n"); > + memset(buf, 0, count); > + return -EINVAL; > + } > + > + mutex_lock(_cfg_dev_lock); > + fw_cfg_sel_endianness(key); > + while (pos-- > 0) > + ioread8(fw_cfg_reg_data); > + ioread8_rep(fw_cfg_reg_data, buf, count); > + mutex_unlock(_cfg_dev_lock); > + > + acpi_release_global_lock(glk); > + return count; > +} > + > +#ifdef CONFIG_CRASH_CORE > static inline bool fw_cfg_dma_enabled(void) > { > return (fw_cfg_rev & FW_CFG_VERSION_DMA) && fw_cfg_reg_dma; > @@ -124,36 +154,6 @@ static ssize_t fw_cfg_dma_transfer(void *address, u32 > length, u32 control) > return ret; > } > > -/* read chunk of given fw_cfg blob (caller responsible for sanity-check) */ > -static ssize_t fw_cfg_read_blob(u16 key, > - void *buf, loff_t pos, size_t count) > -{ > - u32 glk = -1U; > - acpi_status status; > - > - /* If we have ACPI, ensure mutual exclusion against any potential > -* device access by the firmware, e.g. via AML methods: > -*/ > - status = acpi_acquire_global_lock(ACPI_WAIT_FOREVER, ); > - if (ACPI_FAILURE(status) && status != AE_NOT_CONFIGURED) { > - /* Should never get here */ > - WARN(1, "fw_cfg_read_blob: Failed to lock ACPI!\n"); > - memset(buf, 0, count); > - return -EINVAL; > - } > - > - mutex_lock(_cfg_dev_lock); > - fw_cfg_sel_endianness(key); > - while (pos-- > 0) > - ioread8(fw_cfg_reg_data); > - ioread8_rep(fw_cfg_reg_data, buf, count); > - mutex_unlock(_cfg_dev_lock); > - > - acpi_release_global_lock(glk); > - return count; > -} > - > -#ifdef CONFIG_CRASH_CORE > /* write chunk of given fw_cfg blob (caller responsible for sanity-check) */ > static ssize_t fw_cfg_write_blob(u16 key, > void *buf, loff_t pos, size_t count) > -- > 2.9.0 >
Re: [PATCH v15 11/11] RFC: fw_cfg: do DMA read operation
Hi On Tue, Feb 27, 2018 at 1:04 AM, Michael S. Tsirkin <m...@redhat.com> wrote: > On Thu, Feb 15, 2018 at 10:33:12PM +0100, Marc-André Lureau wrote: >> Modify fw_cfg_read_blob() to use DMA if the device supports it. >> Return errors, because the operation may fail. >> >> So far, only one call in fw_cfg_register_dir_entries() is using >> kmalloc'ed buf and is thus clearly eligible to DMA read. >> >> Initially, I didn't implement DMA read to speed up boot time, but as a >> first step before introducing DMA write (since read operations were >> already presents). Even more, I didn't realize fw-cfg entries were >> being read by the kernel during boot by default. But actally fw-cfg >> entries are being populated during module probe. I knew DMA improved a >> lot bios boot time (the main reason the DMA interface was added >> afaik). Let see the time it would take to read the whole ACPI >> tables (128kb allocated) >> >> # time cat /sys/firmware/qemu_fw_cfg/by_name/etc/acpi/tables/raw >> - with DMA: sys 0m0.003s >> - without DMA (-global fw_cfg.dma_enabled=off): sys 0m7.674s >> >> FW_CFG_FILE_DIR (0x19) is the only "file" that is read during kernel >> boot to populate sysfs qemu_fw_cfg directory, and it is quite >> small (1-2kb). Since it does not expose itself, in order to measure >> the time it takes to read such small file, I took a comparable sized >> file of 2048 bytes and exposed it (-fw_cfg test,file=file with a >> modified read_raw enabling DMA) >> >> # perf stat -r 100 cat /sys/firmware/qemu_fw_cfg/by_name/test/raw >/dev/null >> - with DMA: >> 0.636037 task-clock (msec) #0.141 CPUs utilized >> ( +- 1.19% ) >> - without DMA: >> 6.430128 task-clock (msec) #0.622 CPUs utilized >> ( +- 0.22% ) >> >> That's a few msec saved during boot by enabling DMA read (the gain >> would be more substantial if other & bigger fw-cfg entries are read by >> others from sysfs, unfortunately, it's not clear if we can always >> enable DMA there) >> >> Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> >> --- >> drivers/firmware/qemu_fw_cfg.c | 61 >> ++ >> 1 file changed, 50 insertions(+), 11 deletions(-) >> >> diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c >> index 3015e77aebca..94df57e9be66 100644 >> --- a/drivers/firmware/qemu_fw_cfg.c >> +++ b/drivers/firmware/qemu_fw_cfg.c >> @@ -124,12 +124,47 @@ static ssize_t fw_cfg_dma_transfer(void *address, u32 >> length, u32 control) >> return ret; >> } >> >> +/* with acpi & dev locks taken */ >> +static ssize_t fw_cfg_read_blob_dma(u16 key, >> + void *buf, loff_t pos, size_t count) >> +{ >> + ssize_t ret; >> + >> + if (pos == 0) { >> + ret = fw_cfg_dma_transfer(buf, count, key << 16 >> + | FW_CFG_DMA_CTL_SELECT >> + | FW_CFG_DMA_CTL_READ); >> + } else { >> + fw_cfg_sel_endianness(key); >> + ret = fw_cfg_dma_transfer(NULL, pos, FW_CFG_DMA_CTL_SKIP); >> + if (ret < 0) >> + return ret; >> + ret = fw_cfg_dma_transfer(buf, count, >> + FW_CFG_DMA_CTL_READ); >> + } >> + >> + return ret; >> +} >> + >> +/* with acpi & dev locks taken */ >> +static ssize_t fw_cfg_read_blob_io(u16 key, >> + void *buf, loff_t pos, size_t count) >> +{ >> + fw_cfg_sel_endianness(key); >> + while (pos-- > 0) >> + ioread8(fw_cfg_reg_data); >> + ioread8_rep(fw_cfg_reg_data, buf, count); >> + return count; >> +} >> + >> /* read chunk of given fw_cfg blob (caller responsible for sanity-check) */ >> static ssize_t fw_cfg_read_blob(u16 key, >> - void *buf, loff_t pos, size_t count) >> + void *buf, loff_t pos, size_t count, >> + bool dma) >> { >> u32 glk = -1U; >> acpi_status status; >> + ssize_t ret; >> >> /* If we have ACPI, ensure mutual exclusion against any potential >>* device access by the firmware, e.g. via AML methods: > > so this adds a dma flag to fw_cfg_read_blob. > > > >> @@
Re: [PATCH v15 11/11] RFC: fw_cfg: do DMA read operation
Hi On Tue, Feb 27, 2018 at 1:04 AM, Michael S. Tsirkin wrote: > On Thu, Feb 15, 2018 at 10:33:12PM +0100, Marc-André Lureau wrote: >> Modify fw_cfg_read_blob() to use DMA if the device supports it. >> Return errors, because the operation may fail. >> >> So far, only one call in fw_cfg_register_dir_entries() is using >> kmalloc'ed buf and is thus clearly eligible to DMA read. >> >> Initially, I didn't implement DMA read to speed up boot time, but as a >> first step before introducing DMA write (since read operations were >> already presents). Even more, I didn't realize fw-cfg entries were >> being read by the kernel during boot by default. But actally fw-cfg >> entries are being populated during module probe. I knew DMA improved a >> lot bios boot time (the main reason the DMA interface was added >> afaik). Let see the time it would take to read the whole ACPI >> tables (128kb allocated) >> >> # time cat /sys/firmware/qemu_fw_cfg/by_name/etc/acpi/tables/raw >> - with DMA: sys 0m0.003s >> - without DMA (-global fw_cfg.dma_enabled=off): sys 0m7.674s >> >> FW_CFG_FILE_DIR (0x19) is the only "file" that is read during kernel >> boot to populate sysfs qemu_fw_cfg directory, and it is quite >> small (1-2kb). Since it does not expose itself, in order to measure >> the time it takes to read such small file, I took a comparable sized >> file of 2048 bytes and exposed it (-fw_cfg test,file=file with a >> modified read_raw enabling DMA) >> >> # perf stat -r 100 cat /sys/firmware/qemu_fw_cfg/by_name/test/raw >/dev/null >> - with DMA: >> 0.636037 task-clock (msec) #0.141 CPUs utilized >> ( +- 1.19% ) >> - without DMA: >> 6.430128 task-clock (msec) #0.622 CPUs utilized >> ( +- 0.22% ) >> >> That's a few msec saved during boot by enabling DMA read (the gain >> would be more substantial if other & bigger fw-cfg entries are read by >> others from sysfs, unfortunately, it's not clear if we can always >> enable DMA there) >> >> Signed-off-by: Marc-André Lureau >> --- >> drivers/firmware/qemu_fw_cfg.c | 61 >> ++ >> 1 file changed, 50 insertions(+), 11 deletions(-) >> >> diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c >> index 3015e77aebca..94df57e9be66 100644 >> --- a/drivers/firmware/qemu_fw_cfg.c >> +++ b/drivers/firmware/qemu_fw_cfg.c >> @@ -124,12 +124,47 @@ static ssize_t fw_cfg_dma_transfer(void *address, u32 >> length, u32 control) >> return ret; >> } >> >> +/* with acpi & dev locks taken */ >> +static ssize_t fw_cfg_read_blob_dma(u16 key, >> + void *buf, loff_t pos, size_t count) >> +{ >> + ssize_t ret; >> + >> + if (pos == 0) { >> + ret = fw_cfg_dma_transfer(buf, count, key << 16 >> + | FW_CFG_DMA_CTL_SELECT >> + | FW_CFG_DMA_CTL_READ); >> + } else { >> + fw_cfg_sel_endianness(key); >> + ret = fw_cfg_dma_transfer(NULL, pos, FW_CFG_DMA_CTL_SKIP); >> + if (ret < 0) >> + return ret; >> + ret = fw_cfg_dma_transfer(buf, count, >> + FW_CFG_DMA_CTL_READ); >> + } >> + >> + return ret; >> +} >> + >> +/* with acpi & dev locks taken */ >> +static ssize_t fw_cfg_read_blob_io(u16 key, >> + void *buf, loff_t pos, size_t count) >> +{ >> + fw_cfg_sel_endianness(key); >> + while (pos-- > 0) >> + ioread8(fw_cfg_reg_data); >> + ioread8_rep(fw_cfg_reg_data, buf, count); >> + return count; >> +} >> + >> /* read chunk of given fw_cfg blob (caller responsible for sanity-check) */ >> static ssize_t fw_cfg_read_blob(u16 key, >> - void *buf, loff_t pos, size_t count) >> + void *buf, loff_t pos, size_t count, >> + bool dma) >> { >> u32 glk = -1U; >> acpi_status status; >> + ssize_t ret; >> >> /* If we have ACPI, ensure mutual exclusion against any potential >>* device access by the firmware, e.g. via AML methods: > > so this adds a dma flag to fw_cfg_read_blob. > > > >> @@ -143,14 +178,17 @@ static ssize_t fw_cfg_read_blob(u16 ke
Re: [PATCH v15 10/11] fw_cfg: write vmcoreinfo details
Hi On Tue, Feb 27, 2018 at 1:28 AM, Michael S. Tsirkin <m...@redhat.com> wrote: > On Thu, Feb 15, 2018 at 10:33:11PM +0100, Marc-André Lureau wrote: >> If the "etc/vmcoreinfo" fw_cfg file is present and we are not running >> the kdump kernel, write the addr/size of the vmcoreinfo ELF note. >> >> The DMA operation is expected to run synchronously with today qemu, >> but the specification states that it may become async, so we run >> "control" field check in a loop for eventual changes. >> >> Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> >> --- >> drivers/firmware/qemu_fw_cfg.c | 143 >> - >> include/uapi/linux/fw_cfg.h| 31 + >> 2 files changed, 171 insertions(+), 3 deletions(-) >> >> diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c >> index c28bec4b5663..3015e77aebca 100644 >> --- a/drivers/firmware/qemu_fw_cfg.c >> +++ b/drivers/firmware/qemu_fw_cfg.c >> @@ -34,11 +34,17 @@ >> #include >> #include >> #include >> +#include >> +#include >> +#include >> >> MODULE_AUTHOR("Gabriel L. Somlo <so...@cmu.edu>"); >> MODULE_DESCRIPTION("QEMU fw_cfg sysfs support"); >> MODULE_LICENSE("GPL"); >> >> +/* fw_cfg revision attribute, in /sys/firmware/qemu_fw_cfg top-level dir. */ >> +static u32 fw_cfg_rev; >> + >> /* fw_cfg device i/o register addresses */ >> static bool fw_cfg_is_mmio; >> static phys_addr_t fw_cfg_p_base; >> @@ -60,6 +66,64 @@ static void fw_cfg_sel_endianness(u16 key) >> iowrite16(key, fw_cfg_reg_ctrl); >> } >> >> +static inline bool fw_cfg_dma_enabled(void) >> +{ >> + return (fw_cfg_rev & FW_CFG_VERSION_DMA) && fw_cfg_reg_dma; >> +} >> + >> +/* qemu fw_cfg device is sync today, but spec says it may become async */ >> +static void fw_cfg_wait_for_control(struct fw_cfg_dma_access *d) >> +{ >> + for (;;) { >> + u32 ctrl = be32_to_cpu(READ_ONCE(d->control)); >> + >> + /* do not reorder the read to d->control */ >> + rmb(); >> + if ((ctrl & ~FW_CFG_DMA_CTL_ERROR) == 0) >> + return; >> + >> + cpu_relax(); >> + } >> +} >> + >> +static ssize_t fw_cfg_dma_transfer(void *address, u32 length, u32 control) >> +{ >> + phys_addr_t dma; >> + struct fw_cfg_dma_access *d = NULL; >> + ssize_t ret = length; >> + >> + d = kmalloc(sizeof(*d), GFP_KERNEL); >> + if (!d) { >> + ret = -ENOMEM; >> + goto end; >> + } >> + >> + /* fw_cfg device does not need IOMMU protection, so use physical >> addresses */ >> + *d = (struct fw_cfg_dma_access) { >> + .address = cpu_to_be64(address ? virt_to_phys(address) : 0), >> + .length = cpu_to_be32(length), >> + .control = cpu_to_be32(control) >> + }; >> + >> + dma = virt_to_phys(d); >> + >> + iowrite32be((u64)dma >> 32, fw_cfg_reg_dma); >> + /* force memory to sync before notifying device via MMIO */ >> + wmb(); >> + iowrite32be(dma, fw_cfg_reg_dma + 4); >> + >> + fw_cfg_wait_for_control(d); >> + >> + if (be32_to_cpu(READ_ONCE(d->control)) & FW_CFG_DMA_CTL_ERROR) { >> + ret = -EIO; >> + } >> + >> +end: >> + kfree(d); >> + >> + return ret; >> +} >> + >> /* read chunk of given fw_cfg blob (caller responsible for sanity-check) */ >> static ssize_t fw_cfg_read_blob(u16 key, >> void *buf, loff_t pos, size_t count) >> @@ -89,6 +153,47 @@ static ssize_t fw_cfg_read_blob(u16 key, >> return count; >> } >> >> +#ifdef CONFIG_CRASH_CORE >> +/* write chunk of given fw_cfg blob (caller responsible for sanity-check) */ >> +static ssize_t fw_cfg_write_blob(u16 key, >> + void *buf, loff_t pos, size_t count) >> +{ >> + u32 glk = -1U; >> + acpi_status status; >> + ssize_t ret = count; >> + >> + /* If we have ACPI, ensure mutual exclusion against any potential >> + * device access by the firmware, e.g. via AML methods: >> + */ >> + status = acpi_acquire_global_lock(ACPI_WAIT_FOREVER, ); >> + if (ACPI_FAILURE(status) && statu
Re: [PATCH v15 10/11] fw_cfg: write vmcoreinfo details
Hi On Tue, Feb 27, 2018 at 1:28 AM, Michael S. Tsirkin wrote: > On Thu, Feb 15, 2018 at 10:33:11PM +0100, Marc-André Lureau wrote: >> If the "etc/vmcoreinfo" fw_cfg file is present and we are not running >> the kdump kernel, write the addr/size of the vmcoreinfo ELF note. >> >> The DMA operation is expected to run synchronously with today qemu, >> but the specification states that it may become async, so we run >> "control" field check in a loop for eventual changes. >> >> Signed-off-by: Marc-André Lureau >> --- >> drivers/firmware/qemu_fw_cfg.c | 143 >> - >> include/uapi/linux/fw_cfg.h| 31 + >> 2 files changed, 171 insertions(+), 3 deletions(-) >> >> diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c >> index c28bec4b5663..3015e77aebca 100644 >> --- a/drivers/firmware/qemu_fw_cfg.c >> +++ b/drivers/firmware/qemu_fw_cfg.c >> @@ -34,11 +34,17 @@ >> #include >> #include >> #include >> +#include >> +#include >> +#include >> >> MODULE_AUTHOR("Gabriel L. Somlo "); >> MODULE_DESCRIPTION("QEMU fw_cfg sysfs support"); >> MODULE_LICENSE("GPL"); >> >> +/* fw_cfg revision attribute, in /sys/firmware/qemu_fw_cfg top-level dir. */ >> +static u32 fw_cfg_rev; >> + >> /* fw_cfg device i/o register addresses */ >> static bool fw_cfg_is_mmio; >> static phys_addr_t fw_cfg_p_base; >> @@ -60,6 +66,64 @@ static void fw_cfg_sel_endianness(u16 key) >> iowrite16(key, fw_cfg_reg_ctrl); >> } >> >> +static inline bool fw_cfg_dma_enabled(void) >> +{ >> + return (fw_cfg_rev & FW_CFG_VERSION_DMA) && fw_cfg_reg_dma; >> +} >> + >> +/* qemu fw_cfg device is sync today, but spec says it may become async */ >> +static void fw_cfg_wait_for_control(struct fw_cfg_dma_access *d) >> +{ >> + for (;;) { >> + u32 ctrl = be32_to_cpu(READ_ONCE(d->control)); >> + >> + /* do not reorder the read to d->control */ >> + rmb(); >> + if ((ctrl & ~FW_CFG_DMA_CTL_ERROR) == 0) >> + return; >> + >> + cpu_relax(); >> + } >> +} >> + >> +static ssize_t fw_cfg_dma_transfer(void *address, u32 length, u32 control) >> +{ >> + phys_addr_t dma; >> + struct fw_cfg_dma_access *d = NULL; >> + ssize_t ret = length; >> + >> + d = kmalloc(sizeof(*d), GFP_KERNEL); >> + if (!d) { >> + ret = -ENOMEM; >> + goto end; >> + } >> + >> + /* fw_cfg device does not need IOMMU protection, so use physical >> addresses */ >> + *d = (struct fw_cfg_dma_access) { >> + .address = cpu_to_be64(address ? virt_to_phys(address) : 0), >> + .length = cpu_to_be32(length), >> + .control = cpu_to_be32(control) >> + }; >> + >> + dma = virt_to_phys(d); >> + >> + iowrite32be((u64)dma >> 32, fw_cfg_reg_dma); >> + /* force memory to sync before notifying device via MMIO */ >> + wmb(); >> + iowrite32be(dma, fw_cfg_reg_dma + 4); >> + >> + fw_cfg_wait_for_control(d); >> + >> + if (be32_to_cpu(READ_ONCE(d->control)) & FW_CFG_DMA_CTL_ERROR) { >> + ret = -EIO; >> + } >> + >> +end: >> + kfree(d); >> + >> + return ret; >> +} >> + >> /* read chunk of given fw_cfg blob (caller responsible for sanity-check) */ >> static ssize_t fw_cfg_read_blob(u16 key, >> void *buf, loff_t pos, size_t count) >> @@ -89,6 +153,47 @@ static ssize_t fw_cfg_read_blob(u16 key, >> return count; >> } >> >> +#ifdef CONFIG_CRASH_CORE >> +/* write chunk of given fw_cfg blob (caller responsible for sanity-check) */ >> +static ssize_t fw_cfg_write_blob(u16 key, >> + void *buf, loff_t pos, size_t count) >> +{ >> + u32 glk = -1U; >> + acpi_status status; >> + ssize_t ret = count; >> + >> + /* If we have ACPI, ensure mutual exclusion against any potential >> + * device access by the firmware, e.g. via AML methods: >> + */ >> + status = acpi_acquire_global_lock(ACPI_WAIT_FOREVER, ); >> + if (ACPI_FAILURE(status) && status != AE_NOT_CONFIGURED) { >> + /* Should neve
Re: [PATCH v15 02/11] fw_cfg: add a public uapi header
On Tue, Feb 27, 2018 at 1:06 AM, Michael S. Tsirkin <m...@redhat.com> wrote: > On Thu, Feb 15, 2018 at 10:33:03PM +0100, Marc-André Lureau wrote: >> Create a common header file for well-known values and structures to be >> shared by the Linux kernel with qemu or other projects. >> >> It is based from qemu/docs/specs/fw_cfg.txt which references >> qemu/include/hw/nvram/fw_cfg_keys.h "for the most up-to-date and >> authoritative list" & vmcoreinfo.txt. Those files don't have an >> explicit license, but qemu/hw/nvram/fw_cfg.c is BSD-license, so >> Michael S. Tsirkin suggested to use the same license. >> >> The patch intentionally left out DMA & vmcoreinfo structures & >> defines, which are added in the commits making usage of it. >> >> Suggested-by: Michael S. Tsirkin <m...@redhat.com> >> Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> >> >> --- >> >> The related qemu patch making use of it, to be submitted: >> https://github.com/elmarco/qemu/commit/4884fc9e9c4c4467a371e5a40f3181239e1b70f5 >> --- >> MAINTAINERS| 1 + >> drivers/firmware/qemu_fw_cfg.c | 22 ++ >> include/uapi/linux/fw_cfg.h| 66 >> ++ >> 3 files changed, 69 insertions(+), 20 deletions(-) >> create mode 100644 include/uapi/linux/fw_cfg.h >> >> diff --git a/MAINTAINERS b/MAINTAINERS >> index 3bdc260e36b7..a66b65f62811 100644 >> --- a/MAINTAINERS >> +++ b/MAINTAINERS >> @@ -11352,6 +11352,7 @@ M:"Michael S. Tsirkin" <m...@redhat.com> >> L: qemu-de...@nongnu.org >> S: Maintained >> F: drivers/firmware/qemu_fw_cfg.c >> +F: include/uapi/linux/fw_cfg.h >> >> QIB DRIVER >> M: Dennis Dalessandro <dennis.dalessan...@intel.com> > > Why fw_cfg.h and not qemu_fw_cfg.h ? fw_cfg.h seems too generic. ok > >> diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c >> index a41b572eeeb1..42601a3eaed5 100644 >> --- a/drivers/firmware/qemu_fw_cfg.c >> +++ b/drivers/firmware/qemu_fw_cfg.c >> @@ -32,30 +32,12 @@ >> #include >> #include >> #include >> +#include >> >> MODULE_AUTHOR("Gabriel L. Somlo <so...@cmu.edu>"); >> MODULE_DESCRIPTION("QEMU fw_cfg sysfs support"); >> MODULE_LICENSE("GPL"); >> >> -/* selector key values for "well-known" fw_cfg entries */ >> -#define FW_CFG_SIGNATURE 0x00 >> -#define FW_CFG_ID 0x01 >> -#define FW_CFG_FILE_DIR 0x19 >> - >> -/* size in bytes of fw_cfg signature */ >> -#define FW_CFG_SIG_SIZE 4 >> - >> -/* fw_cfg "file name" is up to 56 characters (including terminating nul) */ >> -#define FW_CFG_MAX_FILE_PATH 56 >> - >> -/* fw_cfg file directory entry type */ >> -struct fw_cfg_file { >> - u32 size; >> - u16 select; >> - u16 reserved; >> - char name[FW_CFG_MAX_FILE_PATH]; >> -}; >> - >> /* fw_cfg device i/o register addresses */ >> static bool fw_cfg_is_mmio; >> static phys_addr_t fw_cfg_p_base; >> @@ -597,7 +579,7 @@ MODULE_DEVICE_TABLE(of, fw_cfg_sysfs_mmio_match); >> >> #ifdef CONFIG_ACPI >> static const struct acpi_device_id fw_cfg_sysfs_acpi_match[] = { >> - { "QEMU0002", }, >> + { FW_CFG_ACPI_DEVICE_ID, }, >> {}, >> }; >> MODULE_DEVICE_TABLE(acpi, fw_cfg_sysfs_acpi_match); >> diff --git a/include/uapi/linux/fw_cfg.h b/include/uapi/linux/fw_cfg.h >> new file mode 100644 >> index ..c698ac3812f6 >> --- /dev/null >> +++ b/include/uapi/linux/fw_cfg.h >> @@ -0,0 +1,66 @@ >> +/* SPDX-License-Identifier: BSD-3-Clause */ >> +#ifndef _LINUX_FW_CFG_H >> +#define _LINUX_FW_CFG_H >> + >> +#include >> + >> +#define FW_CFG_ACPI_DEVICE_ID"QEMU0002" >> + >> +/* selector key values for "well-known" fw_cfg entries */ >> +#define FW_CFG_SIGNATURE 0x00 >> +#define FW_CFG_ID0x01 >> +#define FW_CFG_UUID 0x02 >> +#define FW_CFG_RAM_SIZE 0x03 >> +#define FW_CFG_NOGRAPHIC 0x04 >> +#define FW_CFG_NB_CPUS 0x05 >> +#define FW_CFG_MACHINE_ID0x06 >> +#define FW_CFG_KERNEL_ADDR 0x07 >> +#define FW_CFG_KERNEL_SIZE 0x08 >> +#define FW_CFG_KERNEL_CMDLINE0x09 >> +#define FW_CFG_INITRD_ADDR 0x0a >> +#define FW_CFG_INITRD_SIZE 0x0b >> +#define FW_CFG_BO
Re: [PATCH v15 02/11] fw_cfg: add a public uapi header
On Tue, Feb 27, 2018 at 1:06 AM, Michael S. Tsirkin wrote: > On Thu, Feb 15, 2018 at 10:33:03PM +0100, Marc-André Lureau wrote: >> Create a common header file for well-known values and structures to be >> shared by the Linux kernel with qemu or other projects. >> >> It is based from qemu/docs/specs/fw_cfg.txt which references >> qemu/include/hw/nvram/fw_cfg_keys.h "for the most up-to-date and >> authoritative list" & vmcoreinfo.txt. Those files don't have an >> explicit license, but qemu/hw/nvram/fw_cfg.c is BSD-license, so >> Michael S. Tsirkin suggested to use the same license. >> >> The patch intentionally left out DMA & vmcoreinfo structures & >> defines, which are added in the commits making usage of it. >> >> Suggested-by: Michael S. Tsirkin >> Signed-off-by: Marc-André Lureau >> >> --- >> >> The related qemu patch making use of it, to be submitted: >> https://github.com/elmarco/qemu/commit/4884fc9e9c4c4467a371e5a40f3181239e1b70f5 >> --- >> MAINTAINERS| 1 + >> drivers/firmware/qemu_fw_cfg.c | 22 ++ >> include/uapi/linux/fw_cfg.h| 66 >> ++ >> 3 files changed, 69 insertions(+), 20 deletions(-) >> create mode 100644 include/uapi/linux/fw_cfg.h >> >> diff --git a/MAINTAINERS b/MAINTAINERS >> index 3bdc260e36b7..a66b65f62811 100644 >> --- a/MAINTAINERS >> +++ b/MAINTAINERS >> @@ -11352,6 +11352,7 @@ M:"Michael S. Tsirkin" >> L: qemu-de...@nongnu.org >> S: Maintained >> F: drivers/firmware/qemu_fw_cfg.c >> +F: include/uapi/linux/fw_cfg.h >> >> QIB DRIVER >> M: Dennis Dalessandro > > Why fw_cfg.h and not qemu_fw_cfg.h ? fw_cfg.h seems too generic. ok > >> diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c >> index a41b572eeeb1..42601a3eaed5 100644 >> --- a/drivers/firmware/qemu_fw_cfg.c >> +++ b/drivers/firmware/qemu_fw_cfg.c >> @@ -32,30 +32,12 @@ >> #include >> #include >> #include >> +#include >> >> MODULE_AUTHOR("Gabriel L. Somlo "); >> MODULE_DESCRIPTION("QEMU fw_cfg sysfs support"); >> MODULE_LICENSE("GPL"); >> >> -/* selector key values for "well-known" fw_cfg entries */ >> -#define FW_CFG_SIGNATURE 0x00 >> -#define FW_CFG_ID 0x01 >> -#define FW_CFG_FILE_DIR 0x19 >> - >> -/* size in bytes of fw_cfg signature */ >> -#define FW_CFG_SIG_SIZE 4 >> - >> -/* fw_cfg "file name" is up to 56 characters (including terminating nul) */ >> -#define FW_CFG_MAX_FILE_PATH 56 >> - >> -/* fw_cfg file directory entry type */ >> -struct fw_cfg_file { >> - u32 size; >> - u16 select; >> - u16 reserved; >> - char name[FW_CFG_MAX_FILE_PATH]; >> -}; >> - >> /* fw_cfg device i/o register addresses */ >> static bool fw_cfg_is_mmio; >> static phys_addr_t fw_cfg_p_base; >> @@ -597,7 +579,7 @@ MODULE_DEVICE_TABLE(of, fw_cfg_sysfs_mmio_match); >> >> #ifdef CONFIG_ACPI >> static const struct acpi_device_id fw_cfg_sysfs_acpi_match[] = { >> - { "QEMU0002", }, >> + { FW_CFG_ACPI_DEVICE_ID, }, >> {}, >> }; >> MODULE_DEVICE_TABLE(acpi, fw_cfg_sysfs_acpi_match); >> diff --git a/include/uapi/linux/fw_cfg.h b/include/uapi/linux/fw_cfg.h >> new file mode 100644 >> index ..c698ac3812f6 >> --- /dev/null >> +++ b/include/uapi/linux/fw_cfg.h >> @@ -0,0 +1,66 @@ >> +/* SPDX-License-Identifier: BSD-3-Clause */ >> +#ifndef _LINUX_FW_CFG_H >> +#define _LINUX_FW_CFG_H >> + >> +#include >> + >> +#define FW_CFG_ACPI_DEVICE_ID"QEMU0002" >> + >> +/* selector key values for "well-known" fw_cfg entries */ >> +#define FW_CFG_SIGNATURE 0x00 >> +#define FW_CFG_ID0x01 >> +#define FW_CFG_UUID 0x02 >> +#define FW_CFG_RAM_SIZE 0x03 >> +#define FW_CFG_NOGRAPHIC 0x04 >> +#define FW_CFG_NB_CPUS 0x05 >> +#define FW_CFG_MACHINE_ID0x06 >> +#define FW_CFG_KERNEL_ADDR 0x07 >> +#define FW_CFG_KERNEL_SIZE 0x08 >> +#define FW_CFG_KERNEL_CMDLINE0x09 >> +#define FW_CFG_INITRD_ADDR 0x0a >> +#define FW_CFG_INITRD_SIZE 0x0b >> +#define FW_CFG_BOOT_DEVICE 0x0c >> +#define FW_CFG_NUMA 0x0d >> +#define FW_CFG_BOOT_MENU 0x0e >> +#define FW_CFG_MAX_CPUS 0x0f >> +#d
Re: [PATCH v15 08/11] fw_cfg: handle fw_cfg_read_blob() error
Hi On Tue, Feb 27, 2018 at 1:20 AM, Michael S. Tsirkin <m...@redhat.com> wrote: > On Thu, Feb 15, 2018 at 10:33:09PM +0100, Marc-André Lureau wrote: >> fw_cfg_read_blob() may fail, but does not return error. This may lead >> to undefined behaviours, such as a memcmp(sig, "QEMU") on uninitilized >> memory. > > I don't think that's true - there's a memset there that > will initialize the memory. probe is likely the only > case where it returns a slightly incorrect data. Right, I'll update the commit message. >> Return an error if ACPI locking failed. Also, the following >> DMA read/write extension will add more error paths that should be >> handled appropriately. >> >> Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> >> --- >> drivers/firmware/qemu_fw_cfg.c | 32 ++-- >> 1 file changed, 22 insertions(+), 10 deletions(-) >> >> diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c >> index f6f90bef604c..5e6e5ac71dab 100644 >> --- a/drivers/firmware/qemu_fw_cfg.c >> +++ b/drivers/firmware/qemu_fw_cfg.c >> @@ -59,8 +59,8 @@ static void fw_cfg_sel_endianness(u16 key) >> } >> >> /* read chunk of given fw_cfg blob (caller responsible for sanity-check) */ >> -static void fw_cfg_read_blob(u16 key, >> - void *buf, loff_t pos, size_t count) >> +static ssize_t fw_cfg_read_blob(u16 key, >> + void *buf, loff_t pos, size_t count) >> { >> u32 glk = -1U; >> acpi_status status; >> @@ -73,7 +73,7 @@ static void fw_cfg_read_blob(u16 key, >> /* Should never get here */ >> WARN(1, "fw_cfg_read_blob: Failed to lock ACPI!\n"); >> memset(buf, 0, count); >> - return; >> + return -EINVAL; >> } >> >> mutex_lock(_cfg_dev_lock); > > Wouldn't something like -EBUSY be more appropriate? In theory, it would be a general failure right? I don't think we want the caller to retry. I think in EINVAL fits better, but I don't think it matters much this or EBUSY. >> @@ -84,6 +84,7 @@ static void fw_cfg_read_blob(u16 key, >> mutex_unlock(_cfg_dev_lock); >> >> acpi_release_global_lock(glk); >> + return count; >> } >> >> /* clean up fw_cfg device i/o */ >> @@ -165,8 +166,9 @@ static int fw_cfg_do_platform_probe(struct >> platform_device *pdev) >> } >> >> /* verify fw_cfg device signature */ >> - fw_cfg_read_blob(FW_CFG_SIGNATURE, sig, 0, FW_CFG_SIG_SIZE); >> - if (memcmp(sig, "QEMU", FW_CFG_SIG_SIZE) != 0) { >> + if (fw_cfg_read_blob(FW_CFG_SIGNATURE, sig, >> + 0, FW_CFG_SIG_SIZE) < 0 || >> + memcmp(sig, "QEMU", FW_CFG_SIG_SIZE) != 0) { >> fw_cfg_io_cleanup(); >> return -ENODEV; >> } >> @@ -326,8 +328,7 @@ static ssize_t fw_cfg_sysfs_read_raw(struct file *filp, >> struct kobject *kobj, >> if (count > entry->size - pos) >> count = entry->size - pos; >> >> - fw_cfg_read_blob(entry->select, buf, pos, count); >> - return count; >> + return fw_cfg_read_blob(entry->select, buf, pos, count); >> } >> >> static struct bin_attribute fw_cfg_sysfs_attr_raw = { >> @@ -483,7 +484,11 @@ static int fw_cfg_register_dir_entries(void) >> struct fw_cfg_file *dir; >> size_t dir_size; >> >> - fw_cfg_read_blob(FW_CFG_FILE_DIR, _count, 0, >> sizeof(files_count)); >> + ret = fw_cfg_read_blob(FW_CFG_FILE_DIR, _count, >> + 0, sizeof(files_count)); >> + if (ret < 0) >> + return ret; >> + >> count = be32_to_cpu(files_count); >> dir_size = count * sizeof(struct fw_cfg_file); >> >> @@ -491,7 +496,10 @@ static int fw_cfg_register_dir_entries(void) >> if (!dir) >> return -ENOMEM; >> >> - fw_cfg_read_blob(FW_CFG_FILE_DIR, dir, sizeof(files_count), dir_size); >> + ret = fw_cfg_read_blob(FW_CFG_FILE_DIR, dir, >> + sizeof(files_count), dir_size); >> + if (ret < 0) >> + goto end; >> >> for (i = 0; i < count; i++) { >> ret = fw_cfg_register_file([i]); >> @@ -499,6 +507,7 @@ static int fw_cfg_register_dir_entries(void) >> break; >> } >> >> +end: >> kfree(dir); >
Re: [PATCH v15 08/11] fw_cfg: handle fw_cfg_read_blob() error
Hi On Tue, Feb 27, 2018 at 1:20 AM, Michael S. Tsirkin wrote: > On Thu, Feb 15, 2018 at 10:33:09PM +0100, Marc-André Lureau wrote: >> fw_cfg_read_blob() may fail, but does not return error. This may lead >> to undefined behaviours, such as a memcmp(sig, "QEMU") on uninitilized >> memory. > > I don't think that's true - there's a memset there that > will initialize the memory. probe is likely the only > case where it returns a slightly incorrect data. Right, I'll update the commit message. >> Return an error if ACPI locking failed. Also, the following >> DMA read/write extension will add more error paths that should be >> handled appropriately. >> >> Signed-off-by: Marc-André Lureau >> --- >> drivers/firmware/qemu_fw_cfg.c | 32 ++-- >> 1 file changed, 22 insertions(+), 10 deletions(-) >> >> diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c >> index f6f90bef604c..5e6e5ac71dab 100644 >> --- a/drivers/firmware/qemu_fw_cfg.c >> +++ b/drivers/firmware/qemu_fw_cfg.c >> @@ -59,8 +59,8 @@ static void fw_cfg_sel_endianness(u16 key) >> } >> >> /* read chunk of given fw_cfg blob (caller responsible for sanity-check) */ >> -static void fw_cfg_read_blob(u16 key, >> - void *buf, loff_t pos, size_t count) >> +static ssize_t fw_cfg_read_blob(u16 key, >> + void *buf, loff_t pos, size_t count) >> { >> u32 glk = -1U; >> acpi_status status; >> @@ -73,7 +73,7 @@ static void fw_cfg_read_blob(u16 key, >> /* Should never get here */ >> WARN(1, "fw_cfg_read_blob: Failed to lock ACPI!\n"); >> memset(buf, 0, count); >> - return; >> + return -EINVAL; >> } >> >> mutex_lock(_cfg_dev_lock); > > Wouldn't something like -EBUSY be more appropriate? In theory, it would be a general failure right? I don't think we want the caller to retry. I think in EINVAL fits better, but I don't think it matters much this or EBUSY. >> @@ -84,6 +84,7 @@ static void fw_cfg_read_blob(u16 key, >> mutex_unlock(_cfg_dev_lock); >> >> acpi_release_global_lock(glk); >> + return count; >> } >> >> /* clean up fw_cfg device i/o */ >> @@ -165,8 +166,9 @@ static int fw_cfg_do_platform_probe(struct >> platform_device *pdev) >> } >> >> /* verify fw_cfg device signature */ >> - fw_cfg_read_blob(FW_CFG_SIGNATURE, sig, 0, FW_CFG_SIG_SIZE); >> - if (memcmp(sig, "QEMU", FW_CFG_SIG_SIZE) != 0) { >> + if (fw_cfg_read_blob(FW_CFG_SIGNATURE, sig, >> + 0, FW_CFG_SIG_SIZE) < 0 || >> + memcmp(sig, "QEMU", FW_CFG_SIG_SIZE) != 0) { >> fw_cfg_io_cleanup(); >> return -ENODEV; >> } >> @@ -326,8 +328,7 @@ static ssize_t fw_cfg_sysfs_read_raw(struct file *filp, >> struct kobject *kobj, >> if (count > entry->size - pos) >> count = entry->size - pos; >> >> - fw_cfg_read_blob(entry->select, buf, pos, count); >> - return count; >> + return fw_cfg_read_blob(entry->select, buf, pos, count); >> } >> >> static struct bin_attribute fw_cfg_sysfs_attr_raw = { >> @@ -483,7 +484,11 @@ static int fw_cfg_register_dir_entries(void) >> struct fw_cfg_file *dir; >> size_t dir_size; >> >> - fw_cfg_read_blob(FW_CFG_FILE_DIR, _count, 0, >> sizeof(files_count)); >> + ret = fw_cfg_read_blob(FW_CFG_FILE_DIR, _count, >> + 0, sizeof(files_count)); >> + if (ret < 0) >> + return ret; >> + >> count = be32_to_cpu(files_count); >> dir_size = count * sizeof(struct fw_cfg_file); >> >> @@ -491,7 +496,10 @@ static int fw_cfg_register_dir_entries(void) >> if (!dir) >> return -ENOMEM; >> >> - fw_cfg_read_blob(FW_CFG_FILE_DIR, dir, sizeof(files_count), dir_size); >> + ret = fw_cfg_read_blob(FW_CFG_FILE_DIR, dir, >> + sizeof(files_count), dir_size); >> + if (ret < 0) >> + goto end; >> >> for (i = 0; i < count; i++) { >> ret = fw_cfg_register_file([i]); >> @@ -499,6 +507,7 @@ static int fw_cfg_register_dir_entries(void) >> break; >> } >> >> +end: >> kfree(dir); >> return ret; >> } >> @@ -539
[PATCH v15 01/11] crash: export paddr_vmcoreinfo_note()
The following patch is going to use the symbol from the fw_cfg module, to call the function and write the note location details in the vmcoreinfo entry, so qemu can produce dumps with the vmcoreinfo note. CC: Andrew Morton <a...@linux-foundation.org> CC: Baoquan He <b...@redhat.com> CC: Dave Young <dyo...@redhat.com> CC: Dave Young <dyo...@redhat.com> CC: Hari Bathini <hbath...@linux.vnet.ibm.com> CC: Tony Luck <tony.l...@intel.com> CC: Vivek Goyal <vgo...@redhat.com> Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> Acked-by: Gabriel Somlo <so...@cmu.edu> --- kernel/crash_core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/crash_core.c b/kernel/crash_core.c index 4f63597c824d..a93590cdd9e1 100644 --- a/kernel/crash_core.c +++ b/kernel/crash_core.c @@ -376,6 +376,7 @@ phys_addr_t __weak paddr_vmcoreinfo_note(void) { return __pa(vmcoreinfo_note); } +EXPORT_SYMBOL(paddr_vmcoreinfo_note); static int __init crash_save_vmcoreinfo_init(void) { -- 2.16.1.73.g5832b7e9f2
[PATCH v15 02/11] fw_cfg: add a public uapi header
Create a common header file for well-known values and structures to be shared by the Linux kernel with qemu or other projects. It is based from qemu/docs/specs/fw_cfg.txt which references qemu/include/hw/nvram/fw_cfg_keys.h "for the most up-to-date and authoritative list" & vmcoreinfo.txt. Those files don't have an explicit license, but qemu/hw/nvram/fw_cfg.c is BSD-license, so Michael S. Tsirkin suggested to use the same license. The patch intentionally left out DMA & vmcoreinfo structures & defines, which are added in the commits making usage of it. Suggested-by: Michael S. Tsirkin <m...@redhat.com> Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> --- The related qemu patch making use of it, to be submitted: https://github.com/elmarco/qemu/commit/4884fc9e9c4c4467a371e5a40f3181239e1b70f5 --- MAINTAINERS| 1 + drivers/firmware/qemu_fw_cfg.c | 22 ++ include/uapi/linux/fw_cfg.h| 66 ++ 3 files changed, 69 insertions(+), 20 deletions(-) create mode 100644 include/uapi/linux/fw_cfg.h diff --git a/MAINTAINERS b/MAINTAINERS index 3bdc260e36b7..a66b65f62811 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11352,6 +11352,7 @@ M: "Michael S. Tsirkin" <m...@redhat.com> L: qemu-de...@nongnu.org S: Maintained F: drivers/firmware/qemu_fw_cfg.c +F: include/uapi/linux/fw_cfg.h QIB DRIVER M: Dennis Dalessandro <dennis.dalessan...@intel.com> diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index a41b572eeeb1..42601a3eaed5 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -32,30 +32,12 @@ #include #include #include +#include MODULE_AUTHOR("Gabriel L. Somlo <so...@cmu.edu>"); MODULE_DESCRIPTION("QEMU fw_cfg sysfs support"); MODULE_LICENSE("GPL"); -/* selector key values for "well-known" fw_cfg entries */ -#define FW_CFG_SIGNATURE 0x00 -#define FW_CFG_ID 0x01 -#define FW_CFG_FILE_DIR 0x19 - -/* size in bytes of fw_cfg signature */ -#define FW_CFG_SIG_SIZE 4 - -/* fw_cfg "file name" is up to 56 characters (including terminating nul) */ -#define FW_CFG_MAX_FILE_PATH 56 - -/* fw_cfg file directory entry type */ -struct fw_cfg_file { - u32 size; - u16 select; - u16 reserved; - char name[FW_CFG_MAX_FILE_PATH]; -}; - /* fw_cfg device i/o register addresses */ static bool fw_cfg_is_mmio; static phys_addr_t fw_cfg_p_base; @@ -597,7 +579,7 @@ MODULE_DEVICE_TABLE(of, fw_cfg_sysfs_mmio_match); #ifdef CONFIG_ACPI static const struct acpi_device_id fw_cfg_sysfs_acpi_match[] = { - { "QEMU0002", }, + { FW_CFG_ACPI_DEVICE_ID, }, {}, }; MODULE_DEVICE_TABLE(acpi, fw_cfg_sysfs_acpi_match); diff --git a/include/uapi/linux/fw_cfg.h b/include/uapi/linux/fw_cfg.h new file mode 100644 index ..c698ac3812f6 --- /dev/null +++ b/include/uapi/linux/fw_cfg.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +#ifndef _LINUX_FW_CFG_H +#define _LINUX_FW_CFG_H + +#include + +#define FW_CFG_ACPI_DEVICE_ID "QEMU0002" + +/* selector key values for "well-known" fw_cfg entries */ +#define FW_CFG_SIGNATURE 0x00 +#define FW_CFG_ID 0x01 +#define FW_CFG_UUID0x02 +#define FW_CFG_RAM_SIZE0x03 +#define FW_CFG_NOGRAPHIC 0x04 +#define FW_CFG_NB_CPUS 0x05 +#define FW_CFG_MACHINE_ID 0x06 +#define FW_CFG_KERNEL_ADDR 0x07 +#define FW_CFG_KERNEL_SIZE 0x08 +#define FW_CFG_KERNEL_CMDLINE 0x09 +#define FW_CFG_INITRD_ADDR 0x0a +#define FW_CFG_INITRD_SIZE 0x0b +#define FW_CFG_BOOT_DEVICE 0x0c +#define FW_CFG_NUMA0x0d +#define FW_CFG_BOOT_MENU 0x0e +#define FW_CFG_MAX_CPUS0x0f +#define FW_CFG_KERNEL_ENTRY0x10 +#define FW_CFG_KERNEL_DATA 0x11 +#define FW_CFG_INITRD_DATA 0x12 +#define FW_CFG_CMDLINE_ADDR0x13 +#define FW_CFG_CMDLINE_SIZE0x14 +#define FW_CFG_CMDLINE_DATA0x15 +#define FW_CFG_SETUP_ADDR 0x16 +#define FW_CFG_SETUP_SIZE 0x17 +#define FW_CFG_SETUP_DATA 0x18 +#define FW_CFG_FILE_DIR0x19 + +#define FW_CFG_FILE_FIRST 0x20 +#define FW_CFG_FILE_SLOTS_MIN 0x10 + +#define FW_CFG_WRITE_CHANNEL 0x4000 +#define FW_CFG_ARCH_LOCAL 0x8000 +#define FW_CFG_ENTRY_MASK (~(FW_CFG_WRITE_CHANNEL | FW_CFG_ARCH_LOCAL)) + +#define FW_CFG_INVALID 0x + +/* width in bytes of fw_cfg control register */ +#define FW_CFG_CTL_SIZE0x02 + +/* fw_cfg "file name" is up to 56 characters (including terminating nul) */ +#define FW_CFG_MAX_FILE_PATH 56 + +/* size in bytes of fw_cfg signature */ +#define FW_CFG_SIG_SIZE 4 + +/* FW_CFG_ID bits */ +#define FW_CFG_VERSION 0x01 + +/* fw_cfg file directory entry type */ +struct fw_cfg_file { + __be32 size; + __be16 select; + __u16 reserved; + char name[FW_CFG_MAX_FILE_PATH]; +}; + +#endif -- 2.16.1.73.g5832b7e9f2
[PATCH v15 01/11] crash: export paddr_vmcoreinfo_note()
The following patch is going to use the symbol from the fw_cfg module, to call the function and write the note location details in the vmcoreinfo entry, so qemu can produce dumps with the vmcoreinfo note. CC: Andrew Morton CC: Baoquan He CC: Dave Young CC: Dave Young CC: Hari Bathini CC: Tony Luck CC: Vivek Goyal Signed-off-by: Marc-André Lureau Acked-by: Gabriel Somlo --- kernel/crash_core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/crash_core.c b/kernel/crash_core.c index 4f63597c824d..a93590cdd9e1 100644 --- a/kernel/crash_core.c +++ b/kernel/crash_core.c @@ -376,6 +376,7 @@ phys_addr_t __weak paddr_vmcoreinfo_note(void) { return __pa(vmcoreinfo_note); } +EXPORT_SYMBOL(paddr_vmcoreinfo_note); static int __init crash_save_vmcoreinfo_init(void) { -- 2.16.1.73.g5832b7e9f2
[PATCH v15 02/11] fw_cfg: add a public uapi header
Create a common header file for well-known values and structures to be shared by the Linux kernel with qemu or other projects. It is based from qemu/docs/specs/fw_cfg.txt which references qemu/include/hw/nvram/fw_cfg_keys.h "for the most up-to-date and authoritative list" & vmcoreinfo.txt. Those files don't have an explicit license, but qemu/hw/nvram/fw_cfg.c is BSD-license, so Michael S. Tsirkin suggested to use the same license. The patch intentionally left out DMA & vmcoreinfo structures & defines, which are added in the commits making usage of it. Suggested-by: Michael S. Tsirkin Signed-off-by: Marc-André Lureau --- The related qemu patch making use of it, to be submitted: https://github.com/elmarco/qemu/commit/4884fc9e9c4c4467a371e5a40f3181239e1b70f5 --- MAINTAINERS| 1 + drivers/firmware/qemu_fw_cfg.c | 22 ++ include/uapi/linux/fw_cfg.h| 66 ++ 3 files changed, 69 insertions(+), 20 deletions(-) create mode 100644 include/uapi/linux/fw_cfg.h diff --git a/MAINTAINERS b/MAINTAINERS index 3bdc260e36b7..a66b65f62811 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11352,6 +11352,7 @@ M: "Michael S. Tsirkin" L: qemu-de...@nongnu.org S: Maintained F: drivers/firmware/qemu_fw_cfg.c +F: include/uapi/linux/fw_cfg.h QIB DRIVER M: Dennis Dalessandro diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index a41b572eeeb1..42601a3eaed5 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -32,30 +32,12 @@ #include #include #include +#include MODULE_AUTHOR("Gabriel L. Somlo "); MODULE_DESCRIPTION("QEMU fw_cfg sysfs support"); MODULE_LICENSE("GPL"); -/* selector key values for "well-known" fw_cfg entries */ -#define FW_CFG_SIGNATURE 0x00 -#define FW_CFG_ID 0x01 -#define FW_CFG_FILE_DIR 0x19 - -/* size in bytes of fw_cfg signature */ -#define FW_CFG_SIG_SIZE 4 - -/* fw_cfg "file name" is up to 56 characters (including terminating nul) */ -#define FW_CFG_MAX_FILE_PATH 56 - -/* fw_cfg file directory entry type */ -struct fw_cfg_file { - u32 size; - u16 select; - u16 reserved; - char name[FW_CFG_MAX_FILE_PATH]; -}; - /* fw_cfg device i/o register addresses */ static bool fw_cfg_is_mmio; static phys_addr_t fw_cfg_p_base; @@ -597,7 +579,7 @@ MODULE_DEVICE_TABLE(of, fw_cfg_sysfs_mmio_match); #ifdef CONFIG_ACPI static const struct acpi_device_id fw_cfg_sysfs_acpi_match[] = { - { "QEMU0002", }, + { FW_CFG_ACPI_DEVICE_ID, }, {}, }; MODULE_DEVICE_TABLE(acpi, fw_cfg_sysfs_acpi_match); diff --git a/include/uapi/linux/fw_cfg.h b/include/uapi/linux/fw_cfg.h new file mode 100644 index ..c698ac3812f6 --- /dev/null +++ b/include/uapi/linux/fw_cfg.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +#ifndef _LINUX_FW_CFG_H +#define _LINUX_FW_CFG_H + +#include + +#define FW_CFG_ACPI_DEVICE_ID "QEMU0002" + +/* selector key values for "well-known" fw_cfg entries */ +#define FW_CFG_SIGNATURE 0x00 +#define FW_CFG_ID 0x01 +#define FW_CFG_UUID0x02 +#define FW_CFG_RAM_SIZE0x03 +#define FW_CFG_NOGRAPHIC 0x04 +#define FW_CFG_NB_CPUS 0x05 +#define FW_CFG_MACHINE_ID 0x06 +#define FW_CFG_KERNEL_ADDR 0x07 +#define FW_CFG_KERNEL_SIZE 0x08 +#define FW_CFG_KERNEL_CMDLINE 0x09 +#define FW_CFG_INITRD_ADDR 0x0a +#define FW_CFG_INITRD_SIZE 0x0b +#define FW_CFG_BOOT_DEVICE 0x0c +#define FW_CFG_NUMA0x0d +#define FW_CFG_BOOT_MENU 0x0e +#define FW_CFG_MAX_CPUS0x0f +#define FW_CFG_KERNEL_ENTRY0x10 +#define FW_CFG_KERNEL_DATA 0x11 +#define FW_CFG_INITRD_DATA 0x12 +#define FW_CFG_CMDLINE_ADDR0x13 +#define FW_CFG_CMDLINE_SIZE0x14 +#define FW_CFG_CMDLINE_DATA0x15 +#define FW_CFG_SETUP_ADDR 0x16 +#define FW_CFG_SETUP_SIZE 0x17 +#define FW_CFG_SETUP_DATA 0x18 +#define FW_CFG_FILE_DIR0x19 + +#define FW_CFG_FILE_FIRST 0x20 +#define FW_CFG_FILE_SLOTS_MIN 0x10 + +#define FW_CFG_WRITE_CHANNEL 0x4000 +#define FW_CFG_ARCH_LOCAL 0x8000 +#define FW_CFG_ENTRY_MASK (~(FW_CFG_WRITE_CHANNEL | FW_CFG_ARCH_LOCAL)) + +#define FW_CFG_INVALID 0x + +/* width in bytes of fw_cfg control register */ +#define FW_CFG_CTL_SIZE0x02 + +/* fw_cfg "file name" is up to 56 characters (including terminating nul) */ +#define FW_CFG_MAX_FILE_PATH 56 + +/* size in bytes of fw_cfg signature */ +#define FW_CFG_SIG_SIZE 4 + +/* FW_CFG_ID bits */ +#define FW_CFG_VERSION 0x01 + +/* fw_cfg file directory entry type */ +struct fw_cfg_file { + __be32 size; + __be16 select; + __u16 reserved; + char name[FW_CFG_MAX_FILE_PATH]; +}; + +#endif -- 2.16.1.73.g5832b7e9f2
[PATCH v15 05/11] fw_cfg: fix sparse warning reading FW_CFG_ID
Use a restricted type for reading FW_CFG_ID, fixing sparse warning: drivers/firmware/qemu_fw_cfg.c:540:22: warning: cast to restricted __le32 Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> --- drivers/firmware/qemu_fw_cfg.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 6ee12c9e079a..71672cb8c427 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -512,6 +512,7 @@ static inline void fw_cfg_kobj_cleanup(struct kobject *kobj) static int fw_cfg_sysfs_probe(struct platform_device *pdev) { int err; + __le32 rev; /* NOTE: If we supported multiple fw_cfg devices, we'd first create * a subdirectory named after e.g. pdev->id, then hang per-device @@ -537,8 +538,8 @@ static int fw_cfg_sysfs_probe(struct platform_device *pdev) goto err_probe; /* get revision number, add matching top-level attribute */ - fw_cfg_read_blob(FW_CFG_ID, _cfg_rev, 0, sizeof(fw_cfg_rev)); - fw_cfg_rev = le32_to_cpu(fw_cfg_rev); + fw_cfg_read_blob(FW_CFG_ID, , 0, sizeof(rev)); + fw_cfg_rev = le32_to_cpu(rev); err = sysfs_create_file(fw_cfg_top_ko, _cfg_rev_attr.attr); if (err) goto err_rev; -- 2.16.1.73.g5832b7e9f2
[PATCH v15 05/11] fw_cfg: fix sparse warning reading FW_CFG_ID
Use a restricted type for reading FW_CFG_ID, fixing sparse warning: drivers/firmware/qemu_fw_cfg.c:540:22: warning: cast to restricted __le32 Signed-off-by: Marc-André Lureau --- drivers/firmware/qemu_fw_cfg.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 6ee12c9e079a..71672cb8c427 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -512,6 +512,7 @@ static inline void fw_cfg_kobj_cleanup(struct kobject *kobj) static int fw_cfg_sysfs_probe(struct platform_device *pdev) { int err; + __le32 rev; /* NOTE: If we supported multiple fw_cfg devices, we'd first create * a subdirectory named after e.g. pdev->id, then hang per-device @@ -537,8 +538,8 @@ static int fw_cfg_sysfs_probe(struct platform_device *pdev) goto err_probe; /* get revision number, add matching top-level attribute */ - fw_cfg_read_blob(FW_CFG_ID, _cfg_rev, 0, sizeof(fw_cfg_rev)); - fw_cfg_rev = le32_to_cpu(fw_cfg_rev); + fw_cfg_read_blob(FW_CFG_ID, , 0, sizeof(rev)); + fw_cfg_rev = le32_to_cpu(rev); err = sysfs_create_file(fw_cfg_top_ko, _cfg_rev_attr.attr); if (err) goto err_rev; -- 2.16.1.73.g5832b7e9f2
[PATCH v15 04/11] fw_cfg: fix sparse warnings with fw_cfg_file
Modify fw_cfg_sysfs_entry to store entry values, instead of reusing the restricted types. Fixes warnings such as: $ make C=1 CF=-D__CHECK_ENDIAN__ drivers/firmware/qemu_fw_cfg.o drivers/firmware/qemu_fw_cfg.c:491:29: warning: incorrect type in assignment (different base types) drivers/firmware/qemu_fw_cfg.c:491:29:expected restricted __be32 [usertype] size drivers/firmware/qemu_fw_cfg.c:491:29:got unsigned int drivers/firmware/qemu_fw_cfg.c:492:31: warning: incorrect type in assignment (different base types) drivers/firmware/qemu_fw_cfg.c:492:31:expected restricted __be16 [usertype] select drivers/firmware/qemu_fw_cfg.c:492:31:got int Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> --- drivers/firmware/qemu_fw_cfg.c | 28 +++- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 6164731a3c35..6ee12c9e079a 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -193,7 +193,9 @@ static const struct { /* fw_cfg_sysfs_entry type */ struct fw_cfg_sysfs_entry { struct kobject kobj; - struct fw_cfg_file f; + u32 size; + u16 select; + char name[FW_CFG_MAX_FILE_PATH]; struct list_head list; }; @@ -257,17 +259,17 @@ struct fw_cfg_sysfs_attribute fw_cfg_sysfs_attr_##_attr = { \ static ssize_t fw_cfg_sysfs_show_size(struct fw_cfg_sysfs_entry *e, char *buf) { - return sprintf(buf, "%u\n", e->f.size); + return sprintf(buf, "%u\n", e->size); } static ssize_t fw_cfg_sysfs_show_key(struct fw_cfg_sysfs_entry *e, char *buf) { - return sprintf(buf, "%u\n", e->f.select); + return sprintf(buf, "%u\n", e->select); } static ssize_t fw_cfg_sysfs_show_name(struct fw_cfg_sysfs_entry *e, char *buf) { - return sprintf(buf, "%s\n", e->f.name); + return sprintf(buf, "%s\n", e->name); } static FW_CFG_SYSFS_ATTR(size); @@ -318,13 +320,13 @@ static ssize_t fw_cfg_sysfs_read_raw(struct file *filp, struct kobject *kobj, { struct fw_cfg_sysfs_entry *entry = to_entry(kobj); - if (pos > entry->f.size) + if (pos > entry->size) return -EINVAL; - if (count > entry->f.size - pos) - count = entry->f.size - pos; + if (count > entry->size - pos) + count = entry->size - pos; - fw_cfg_read_blob(entry->f.select, buf, pos, count); + fw_cfg_read_blob(entry->select, buf, pos, count); return count; } @@ -443,11 +445,13 @@ static int fw_cfg_register_file(const struct fw_cfg_file *f) return -ENOMEM; /* set file entry information */ - memcpy(>f, f, sizeof(struct fw_cfg_file)); + entry->size = be32_to_cpu(f->size); + entry->select = be16_to_cpu(f->select); + memcpy(entry->name, f->name, FW_CFG_MAX_FILE_PATH); /* register entry under "/sys/firmware/qemu_fw_cfg/by_key/" */ err = kobject_init_and_add(>kobj, _cfg_sysfs_entry_ktype, - fw_cfg_sel_ko, "%d", entry->f.select); + fw_cfg_sel_ko, "%d", entry->select); if (err) goto err_register; @@ -457,7 +461,7 @@ static int fw_cfg_register_file(const struct fw_cfg_file *f) goto err_add_raw; /* try adding "/sys/firmware/qemu_fw_cfg/by_name/" symlink */ - fw_cfg_build_symlink(fw_cfg_fname_kset, >kobj, entry->f.name); + fw_cfg_build_symlink(fw_cfg_fname_kset, >kobj, entry->name); /* success, add entry to global cache */ fw_cfg_sysfs_cache_enlist(entry); @@ -489,8 +493,6 @@ static int fw_cfg_register_dir_entries(void) fw_cfg_read_blob(FW_CFG_FILE_DIR, dir, sizeof(count), dir_size); for (i = 0; i < count; i++) { - dir[i].size = be32_to_cpu(dir[i].size); - dir[i].select = be16_to_cpu(dir[i].select); ret = fw_cfg_register_file([i]); if (ret) break; -- 2.16.1.73.g5832b7e9f2
[PATCH v15 06/11] fw_cfg: fix sparse warnings around FW_CFG_FILE_DIR read
Use struct fw_cfg_files to read the directory size, fixing the sparse warnings: drivers/firmware/qemu_fw_cfg.c:485:17: warning: cast to restricted __be32 Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> --- drivers/firmware/qemu_fw_cfg.c | 7 --- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 71672cb8c427..805372e8e50d 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -478,19 +478,20 @@ static int fw_cfg_register_file(const struct fw_cfg_file *f) static int fw_cfg_register_dir_entries(void) { int ret = 0; + __be32 files_count; u32 count, i; struct fw_cfg_file *dir; size_t dir_size; - fw_cfg_read_blob(FW_CFG_FILE_DIR, , 0, sizeof(count)); - count = be32_to_cpu(count); + fw_cfg_read_blob(FW_CFG_FILE_DIR, _count, 0, sizeof(files_count)); + count = be32_to_cpu(files_count); dir_size = count * sizeof(struct fw_cfg_file); dir = kmalloc(dir_size, GFP_KERNEL); if (!dir) return -ENOMEM; - fw_cfg_read_blob(FW_CFG_FILE_DIR, dir, sizeof(count), dir_size); + fw_cfg_read_blob(FW_CFG_FILE_DIR, dir, sizeof(files_count), dir_size); for (i = 0; i < count; i++) { ret = fw_cfg_register_file([i]); -- 2.16.1.73.g5832b7e9f2
[PATCH v15 04/11] fw_cfg: fix sparse warnings with fw_cfg_file
Modify fw_cfg_sysfs_entry to store entry values, instead of reusing the restricted types. Fixes warnings such as: $ make C=1 CF=-D__CHECK_ENDIAN__ drivers/firmware/qemu_fw_cfg.o drivers/firmware/qemu_fw_cfg.c:491:29: warning: incorrect type in assignment (different base types) drivers/firmware/qemu_fw_cfg.c:491:29:expected restricted __be32 [usertype] size drivers/firmware/qemu_fw_cfg.c:491:29:got unsigned int drivers/firmware/qemu_fw_cfg.c:492:31: warning: incorrect type in assignment (different base types) drivers/firmware/qemu_fw_cfg.c:492:31:expected restricted __be16 [usertype] select drivers/firmware/qemu_fw_cfg.c:492:31:got int Signed-off-by: Marc-André Lureau --- drivers/firmware/qemu_fw_cfg.c | 28 +++- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 6164731a3c35..6ee12c9e079a 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -193,7 +193,9 @@ static const struct { /* fw_cfg_sysfs_entry type */ struct fw_cfg_sysfs_entry { struct kobject kobj; - struct fw_cfg_file f; + u32 size; + u16 select; + char name[FW_CFG_MAX_FILE_PATH]; struct list_head list; }; @@ -257,17 +259,17 @@ struct fw_cfg_sysfs_attribute fw_cfg_sysfs_attr_##_attr = { \ static ssize_t fw_cfg_sysfs_show_size(struct fw_cfg_sysfs_entry *e, char *buf) { - return sprintf(buf, "%u\n", e->f.size); + return sprintf(buf, "%u\n", e->size); } static ssize_t fw_cfg_sysfs_show_key(struct fw_cfg_sysfs_entry *e, char *buf) { - return sprintf(buf, "%u\n", e->f.select); + return sprintf(buf, "%u\n", e->select); } static ssize_t fw_cfg_sysfs_show_name(struct fw_cfg_sysfs_entry *e, char *buf) { - return sprintf(buf, "%s\n", e->f.name); + return sprintf(buf, "%s\n", e->name); } static FW_CFG_SYSFS_ATTR(size); @@ -318,13 +320,13 @@ static ssize_t fw_cfg_sysfs_read_raw(struct file *filp, struct kobject *kobj, { struct fw_cfg_sysfs_entry *entry = to_entry(kobj); - if (pos > entry->f.size) + if (pos > entry->size) return -EINVAL; - if (count > entry->f.size - pos) - count = entry->f.size - pos; + if (count > entry->size - pos) + count = entry->size - pos; - fw_cfg_read_blob(entry->f.select, buf, pos, count); + fw_cfg_read_blob(entry->select, buf, pos, count); return count; } @@ -443,11 +445,13 @@ static int fw_cfg_register_file(const struct fw_cfg_file *f) return -ENOMEM; /* set file entry information */ - memcpy(>f, f, sizeof(struct fw_cfg_file)); + entry->size = be32_to_cpu(f->size); + entry->select = be16_to_cpu(f->select); + memcpy(entry->name, f->name, FW_CFG_MAX_FILE_PATH); /* register entry under "/sys/firmware/qemu_fw_cfg/by_key/" */ err = kobject_init_and_add(>kobj, _cfg_sysfs_entry_ktype, - fw_cfg_sel_ko, "%d", entry->f.select); + fw_cfg_sel_ko, "%d", entry->select); if (err) goto err_register; @@ -457,7 +461,7 @@ static int fw_cfg_register_file(const struct fw_cfg_file *f) goto err_add_raw; /* try adding "/sys/firmware/qemu_fw_cfg/by_name/" symlink */ - fw_cfg_build_symlink(fw_cfg_fname_kset, >kobj, entry->f.name); + fw_cfg_build_symlink(fw_cfg_fname_kset, >kobj, entry->name); /* success, add entry to global cache */ fw_cfg_sysfs_cache_enlist(entry); @@ -489,8 +493,6 @@ static int fw_cfg_register_dir_entries(void) fw_cfg_read_blob(FW_CFG_FILE_DIR, dir, sizeof(count), dir_size); for (i = 0; i < count; i++) { - dir[i].size = be32_to_cpu(dir[i].size); - dir[i].select = be16_to_cpu(dir[i].select); ret = fw_cfg_register_file([i]); if (ret) break; -- 2.16.1.73.g5832b7e9f2
[PATCH v15 06/11] fw_cfg: fix sparse warnings around FW_CFG_FILE_DIR read
Use struct fw_cfg_files to read the directory size, fixing the sparse warnings: drivers/firmware/qemu_fw_cfg.c:485:17: warning: cast to restricted __be32 Signed-off-by: Marc-André Lureau --- drivers/firmware/qemu_fw_cfg.c | 7 --- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 71672cb8c427..805372e8e50d 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -478,19 +478,20 @@ static int fw_cfg_register_file(const struct fw_cfg_file *f) static int fw_cfg_register_dir_entries(void) { int ret = 0; + __be32 files_count; u32 count, i; struct fw_cfg_file *dir; size_t dir_size; - fw_cfg_read_blob(FW_CFG_FILE_DIR, , 0, sizeof(count)); - count = be32_to_cpu(count); + fw_cfg_read_blob(FW_CFG_FILE_DIR, _count, 0, sizeof(files_count)); + count = be32_to_cpu(files_count); dir_size = count * sizeof(struct fw_cfg_file); dir = kmalloc(dir_size, GFP_KERNEL); if (!dir) return -ENOMEM; - fw_cfg_read_blob(FW_CFG_FILE_DIR, dir, sizeof(count), dir_size); + fw_cfg_read_blob(FW_CFG_FILE_DIR, dir, sizeof(files_count), dir_size); for (i = 0; i < count; i++) { ret = fw_cfg_register_file([i]); -- 2.16.1.73.g5832b7e9f2
[PATCH v15 08/11] fw_cfg: handle fw_cfg_read_blob() error
fw_cfg_read_blob() may fail, but does not return error. This may lead to undefined behaviours, such as a memcmp(sig, "QEMU") on uninitilized memory. Return an error if ACPI locking failed. Also, the following DMA read/write extension will add more error paths that should be handled appropriately. Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> --- drivers/firmware/qemu_fw_cfg.c | 32 ++-- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index f6f90bef604c..5e6e5ac71dab 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -59,8 +59,8 @@ static void fw_cfg_sel_endianness(u16 key) } /* read chunk of given fw_cfg blob (caller responsible for sanity-check) */ -static void fw_cfg_read_blob(u16 key, - void *buf, loff_t pos, size_t count) +static ssize_t fw_cfg_read_blob(u16 key, + void *buf, loff_t pos, size_t count) { u32 glk = -1U; acpi_status status; @@ -73,7 +73,7 @@ static void fw_cfg_read_blob(u16 key, /* Should never get here */ WARN(1, "fw_cfg_read_blob: Failed to lock ACPI!\n"); memset(buf, 0, count); - return; + return -EINVAL; } mutex_lock(_cfg_dev_lock); @@ -84,6 +84,7 @@ static void fw_cfg_read_blob(u16 key, mutex_unlock(_cfg_dev_lock); acpi_release_global_lock(glk); + return count; } /* clean up fw_cfg device i/o */ @@ -165,8 +166,9 @@ static int fw_cfg_do_platform_probe(struct platform_device *pdev) } /* verify fw_cfg device signature */ - fw_cfg_read_blob(FW_CFG_SIGNATURE, sig, 0, FW_CFG_SIG_SIZE); - if (memcmp(sig, "QEMU", FW_CFG_SIG_SIZE) != 0) { + if (fw_cfg_read_blob(FW_CFG_SIGNATURE, sig, + 0, FW_CFG_SIG_SIZE) < 0 || + memcmp(sig, "QEMU", FW_CFG_SIG_SIZE) != 0) { fw_cfg_io_cleanup(); return -ENODEV; } @@ -326,8 +328,7 @@ static ssize_t fw_cfg_sysfs_read_raw(struct file *filp, struct kobject *kobj, if (count > entry->size - pos) count = entry->size - pos; - fw_cfg_read_blob(entry->select, buf, pos, count); - return count; + return fw_cfg_read_blob(entry->select, buf, pos, count); } static struct bin_attribute fw_cfg_sysfs_attr_raw = { @@ -483,7 +484,11 @@ static int fw_cfg_register_dir_entries(void) struct fw_cfg_file *dir; size_t dir_size; - fw_cfg_read_blob(FW_CFG_FILE_DIR, _count, 0, sizeof(files_count)); + ret = fw_cfg_read_blob(FW_CFG_FILE_DIR, _count, + 0, sizeof(files_count)); + if (ret < 0) + return ret; + count = be32_to_cpu(files_count); dir_size = count * sizeof(struct fw_cfg_file); @@ -491,7 +496,10 @@ static int fw_cfg_register_dir_entries(void) if (!dir) return -ENOMEM; - fw_cfg_read_blob(FW_CFG_FILE_DIR, dir, sizeof(files_count), dir_size); + ret = fw_cfg_read_blob(FW_CFG_FILE_DIR, dir, + sizeof(files_count), dir_size); + if (ret < 0) + goto end; for (i = 0; i < count; i++) { ret = fw_cfg_register_file([i]); @@ -499,6 +507,7 @@ static int fw_cfg_register_dir_entries(void) break; } +end: kfree(dir); return ret; } @@ -539,7 +548,10 @@ static int fw_cfg_sysfs_probe(struct platform_device *pdev) goto err_probe; /* get revision number, add matching top-level attribute */ - fw_cfg_read_blob(FW_CFG_ID, , 0, sizeof(rev)); + err = fw_cfg_read_blob(FW_CFG_ID, , 0, sizeof(rev)); + if (err < 0) + goto err_probe; + fw_cfg_rev = le32_to_cpu(rev); err = sysfs_create_file(fw_cfg_top_ko, _cfg_rev_attr.attr); if (err) -- 2.16.1.73.g5832b7e9f2
[PATCH v15 08/11] fw_cfg: handle fw_cfg_read_blob() error
fw_cfg_read_blob() may fail, but does not return error. This may lead to undefined behaviours, such as a memcmp(sig, "QEMU") on uninitilized memory. Return an error if ACPI locking failed. Also, the following DMA read/write extension will add more error paths that should be handled appropriately. Signed-off-by: Marc-André Lureau --- drivers/firmware/qemu_fw_cfg.c | 32 ++-- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index f6f90bef604c..5e6e5ac71dab 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -59,8 +59,8 @@ static void fw_cfg_sel_endianness(u16 key) } /* read chunk of given fw_cfg blob (caller responsible for sanity-check) */ -static void fw_cfg_read_blob(u16 key, - void *buf, loff_t pos, size_t count) +static ssize_t fw_cfg_read_blob(u16 key, + void *buf, loff_t pos, size_t count) { u32 glk = -1U; acpi_status status; @@ -73,7 +73,7 @@ static void fw_cfg_read_blob(u16 key, /* Should never get here */ WARN(1, "fw_cfg_read_blob: Failed to lock ACPI!\n"); memset(buf, 0, count); - return; + return -EINVAL; } mutex_lock(_cfg_dev_lock); @@ -84,6 +84,7 @@ static void fw_cfg_read_blob(u16 key, mutex_unlock(_cfg_dev_lock); acpi_release_global_lock(glk); + return count; } /* clean up fw_cfg device i/o */ @@ -165,8 +166,9 @@ static int fw_cfg_do_platform_probe(struct platform_device *pdev) } /* verify fw_cfg device signature */ - fw_cfg_read_blob(FW_CFG_SIGNATURE, sig, 0, FW_CFG_SIG_SIZE); - if (memcmp(sig, "QEMU", FW_CFG_SIG_SIZE) != 0) { + if (fw_cfg_read_blob(FW_CFG_SIGNATURE, sig, + 0, FW_CFG_SIG_SIZE) < 0 || + memcmp(sig, "QEMU", FW_CFG_SIG_SIZE) != 0) { fw_cfg_io_cleanup(); return -ENODEV; } @@ -326,8 +328,7 @@ static ssize_t fw_cfg_sysfs_read_raw(struct file *filp, struct kobject *kobj, if (count > entry->size - pos) count = entry->size - pos; - fw_cfg_read_blob(entry->select, buf, pos, count); - return count; + return fw_cfg_read_blob(entry->select, buf, pos, count); } static struct bin_attribute fw_cfg_sysfs_attr_raw = { @@ -483,7 +484,11 @@ static int fw_cfg_register_dir_entries(void) struct fw_cfg_file *dir; size_t dir_size; - fw_cfg_read_blob(FW_CFG_FILE_DIR, _count, 0, sizeof(files_count)); + ret = fw_cfg_read_blob(FW_CFG_FILE_DIR, _count, + 0, sizeof(files_count)); + if (ret < 0) + return ret; + count = be32_to_cpu(files_count); dir_size = count * sizeof(struct fw_cfg_file); @@ -491,7 +496,10 @@ static int fw_cfg_register_dir_entries(void) if (!dir) return -ENOMEM; - fw_cfg_read_blob(FW_CFG_FILE_DIR, dir, sizeof(files_count), dir_size); + ret = fw_cfg_read_blob(FW_CFG_FILE_DIR, dir, + sizeof(files_count), dir_size); + if (ret < 0) + goto end; for (i = 0; i < count; i++) { ret = fw_cfg_register_file([i]); @@ -499,6 +507,7 @@ static int fw_cfg_register_dir_entries(void) break; } +end: kfree(dir); return ret; } @@ -539,7 +548,10 @@ static int fw_cfg_sysfs_probe(struct platform_device *pdev) goto err_probe; /* get revision number, add matching top-level attribute */ - fw_cfg_read_blob(FW_CFG_ID, , 0, sizeof(rev)); + err = fw_cfg_read_blob(FW_CFG_ID, , 0, sizeof(rev)); + if (err < 0) + goto err_probe; + fw_cfg_rev = le32_to_cpu(rev); err = sysfs_create_file(fw_cfg_top_ko, _cfg_rev_attr.attr); if (err) -- 2.16.1.73.g5832b7e9f2
[PATCH v15 10/11] fw_cfg: write vmcoreinfo details
If the "etc/vmcoreinfo" fw_cfg file is present and we are not running the kdump kernel, write the addr/size of the vmcoreinfo ELF note. The DMA operation is expected to run synchronously with today qemu, but the specification states that it may become async, so we run "control" field check in a loop for eventual changes. Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> --- drivers/firmware/qemu_fw_cfg.c | 143 - include/uapi/linux/fw_cfg.h| 31 + 2 files changed, 171 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index c28bec4b5663..3015e77aebca 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -34,11 +34,17 @@ #include #include #include +#include +#include +#include MODULE_AUTHOR("Gabriel L. Somlo <so...@cmu.edu>"); MODULE_DESCRIPTION("QEMU fw_cfg sysfs support"); MODULE_LICENSE("GPL"); +/* fw_cfg revision attribute, in /sys/firmware/qemu_fw_cfg top-level dir. */ +static u32 fw_cfg_rev; + /* fw_cfg device i/o register addresses */ static bool fw_cfg_is_mmio; static phys_addr_t fw_cfg_p_base; @@ -60,6 +66,64 @@ static void fw_cfg_sel_endianness(u16 key) iowrite16(key, fw_cfg_reg_ctrl); } +static inline bool fw_cfg_dma_enabled(void) +{ + return (fw_cfg_rev & FW_CFG_VERSION_DMA) && fw_cfg_reg_dma; +} + +/* qemu fw_cfg device is sync today, but spec says it may become async */ +static void fw_cfg_wait_for_control(struct fw_cfg_dma_access *d) +{ + for (;;) { + u32 ctrl = be32_to_cpu(READ_ONCE(d->control)); + + /* do not reorder the read to d->control */ + rmb(); + if ((ctrl & ~FW_CFG_DMA_CTL_ERROR) == 0) + return; + + cpu_relax(); + } +} + +static ssize_t fw_cfg_dma_transfer(void *address, u32 length, u32 control) +{ + phys_addr_t dma; + struct fw_cfg_dma_access *d = NULL; + ssize_t ret = length; + + d = kmalloc(sizeof(*d), GFP_KERNEL); + if (!d) { + ret = -ENOMEM; + goto end; + } + + /* fw_cfg device does not need IOMMU protection, so use physical addresses */ + *d = (struct fw_cfg_dma_access) { + .address = cpu_to_be64(address ? virt_to_phys(address) : 0), + .length = cpu_to_be32(length), + .control = cpu_to_be32(control) + }; + + dma = virt_to_phys(d); + + iowrite32be((u64)dma >> 32, fw_cfg_reg_dma); + /* force memory to sync before notifying device via MMIO */ + wmb(); + iowrite32be(dma, fw_cfg_reg_dma + 4); + + fw_cfg_wait_for_control(d); + + if (be32_to_cpu(READ_ONCE(d->control)) & FW_CFG_DMA_CTL_ERROR) { + ret = -EIO; + } + +end: + kfree(d); + + return ret; +} + /* read chunk of given fw_cfg blob (caller responsible for sanity-check) */ static ssize_t fw_cfg_read_blob(u16 key, void *buf, loff_t pos, size_t count) @@ -89,6 +153,47 @@ static ssize_t fw_cfg_read_blob(u16 key, return count; } +#ifdef CONFIG_CRASH_CORE +/* write chunk of given fw_cfg blob (caller responsible for sanity-check) */ +static ssize_t fw_cfg_write_blob(u16 key, +void *buf, loff_t pos, size_t count) +{ + u32 glk = -1U; + acpi_status status; + ssize_t ret = count; + + /* If we have ACPI, ensure mutual exclusion against any potential +* device access by the firmware, e.g. via AML methods: +*/ + status = acpi_acquire_global_lock(ACPI_WAIT_FOREVER, ); + if (ACPI_FAILURE(status) && status != AE_NOT_CONFIGURED) { + /* Should never get here */ + WARN(1, "%s: Failed to lock ACPI!\n", __func__); + return -EINVAL; + } + + mutex_lock(_cfg_dev_lock); + if (pos == 0) { + ret = fw_cfg_dma_transfer(buf, count, key << 16 + | FW_CFG_DMA_CTL_SELECT + | FW_CFG_DMA_CTL_WRITE); + } else { + fw_cfg_sel_endianness(key); + ret = fw_cfg_dma_transfer(NULL, pos, FW_CFG_DMA_CTL_SKIP); + if (ret < 0) + goto end; + ret = fw_cfg_dma_transfer(buf, count, FW_CFG_DMA_CTL_WRITE); + } + +end: + mutex_unlock(_cfg_dev_lock); + + acpi_release_global_lock(glk); + + return ret; +} +#endif /* CONFIG_CRASH_CORE */ + /* clean up fw_cfg device i/o */ static void fw_cfg_io_cleanup(void) { @@ -188,9 +293,6 @@ static int fw_cfg_do_platform_probe(struct platform_device *pdev) return 0; } -/* fw_cfg revision attribute, in /sys/firmware/qemu_fw_cfg t
[PATCH v15 10/11] fw_cfg: write vmcoreinfo details
If the "etc/vmcoreinfo" fw_cfg file is present and we are not running the kdump kernel, write the addr/size of the vmcoreinfo ELF note. The DMA operation is expected to run synchronously with today qemu, but the specification states that it may become async, so we run "control" field check in a loop for eventual changes. Signed-off-by: Marc-André Lureau --- drivers/firmware/qemu_fw_cfg.c | 143 - include/uapi/linux/fw_cfg.h| 31 + 2 files changed, 171 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index c28bec4b5663..3015e77aebca 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -34,11 +34,17 @@ #include #include #include +#include +#include +#include MODULE_AUTHOR("Gabriel L. Somlo "); MODULE_DESCRIPTION("QEMU fw_cfg sysfs support"); MODULE_LICENSE("GPL"); +/* fw_cfg revision attribute, in /sys/firmware/qemu_fw_cfg top-level dir. */ +static u32 fw_cfg_rev; + /* fw_cfg device i/o register addresses */ static bool fw_cfg_is_mmio; static phys_addr_t fw_cfg_p_base; @@ -60,6 +66,64 @@ static void fw_cfg_sel_endianness(u16 key) iowrite16(key, fw_cfg_reg_ctrl); } +static inline bool fw_cfg_dma_enabled(void) +{ + return (fw_cfg_rev & FW_CFG_VERSION_DMA) && fw_cfg_reg_dma; +} + +/* qemu fw_cfg device is sync today, but spec says it may become async */ +static void fw_cfg_wait_for_control(struct fw_cfg_dma_access *d) +{ + for (;;) { + u32 ctrl = be32_to_cpu(READ_ONCE(d->control)); + + /* do not reorder the read to d->control */ + rmb(); + if ((ctrl & ~FW_CFG_DMA_CTL_ERROR) == 0) + return; + + cpu_relax(); + } +} + +static ssize_t fw_cfg_dma_transfer(void *address, u32 length, u32 control) +{ + phys_addr_t dma; + struct fw_cfg_dma_access *d = NULL; + ssize_t ret = length; + + d = kmalloc(sizeof(*d), GFP_KERNEL); + if (!d) { + ret = -ENOMEM; + goto end; + } + + /* fw_cfg device does not need IOMMU protection, so use physical addresses */ + *d = (struct fw_cfg_dma_access) { + .address = cpu_to_be64(address ? virt_to_phys(address) : 0), + .length = cpu_to_be32(length), + .control = cpu_to_be32(control) + }; + + dma = virt_to_phys(d); + + iowrite32be((u64)dma >> 32, fw_cfg_reg_dma); + /* force memory to sync before notifying device via MMIO */ + wmb(); + iowrite32be(dma, fw_cfg_reg_dma + 4); + + fw_cfg_wait_for_control(d); + + if (be32_to_cpu(READ_ONCE(d->control)) & FW_CFG_DMA_CTL_ERROR) { + ret = -EIO; + } + +end: + kfree(d); + + return ret; +} + /* read chunk of given fw_cfg blob (caller responsible for sanity-check) */ static ssize_t fw_cfg_read_blob(u16 key, void *buf, loff_t pos, size_t count) @@ -89,6 +153,47 @@ static ssize_t fw_cfg_read_blob(u16 key, return count; } +#ifdef CONFIG_CRASH_CORE +/* write chunk of given fw_cfg blob (caller responsible for sanity-check) */ +static ssize_t fw_cfg_write_blob(u16 key, +void *buf, loff_t pos, size_t count) +{ + u32 glk = -1U; + acpi_status status; + ssize_t ret = count; + + /* If we have ACPI, ensure mutual exclusion against any potential +* device access by the firmware, e.g. via AML methods: +*/ + status = acpi_acquire_global_lock(ACPI_WAIT_FOREVER, ); + if (ACPI_FAILURE(status) && status != AE_NOT_CONFIGURED) { + /* Should never get here */ + WARN(1, "%s: Failed to lock ACPI!\n", __func__); + return -EINVAL; + } + + mutex_lock(_cfg_dev_lock); + if (pos == 0) { + ret = fw_cfg_dma_transfer(buf, count, key << 16 + | FW_CFG_DMA_CTL_SELECT + | FW_CFG_DMA_CTL_WRITE); + } else { + fw_cfg_sel_endianness(key); + ret = fw_cfg_dma_transfer(NULL, pos, FW_CFG_DMA_CTL_SKIP); + if (ret < 0) + goto end; + ret = fw_cfg_dma_transfer(buf, count, FW_CFG_DMA_CTL_WRITE); + } + +end: + mutex_unlock(_cfg_dev_lock); + + acpi_release_global_lock(glk); + + return ret; +} +#endif /* CONFIG_CRASH_CORE */ + /* clean up fw_cfg device i/o */ static void fw_cfg_io_cleanup(void) { @@ -188,9 +293,6 @@ static int fw_cfg_do_platform_probe(struct platform_device *pdev) return 0; } -/* fw_cfg revision attribute, in /sys/firmware/qemu_fw_cfg top-level dir. */ -static u32 fw_cfg_rev; - static ssize_t fw_c
[PATCH v15 07/11] fw_cfg: remove inline from fw_cfg_read_blob()
The function is not small and getting bigger. Let the compiler decide instead. No profiling done, hopefully unnecessary. Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> --- drivers/firmware/qemu_fw_cfg.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 805372e8e50d..f6f90bef604c 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -59,8 +59,8 @@ static void fw_cfg_sel_endianness(u16 key) } /* read chunk of given fw_cfg blob (caller responsible for sanity-check) */ -static inline void fw_cfg_read_blob(u16 key, - void *buf, loff_t pos, size_t count) +static void fw_cfg_read_blob(u16 key, + void *buf, loff_t pos, size_t count) { u32 glk = -1U; acpi_status status; -- 2.16.1.73.g5832b7e9f2
[PATCH v15 07/11] fw_cfg: remove inline from fw_cfg_read_blob()
The function is not small and getting bigger. Let the compiler decide instead. No profiling done, hopefully unnecessary. Signed-off-by: Marc-André Lureau --- drivers/firmware/qemu_fw_cfg.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 805372e8e50d..f6f90bef604c 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -59,8 +59,8 @@ static void fw_cfg_sel_endianness(u16 key) } /* read chunk of given fw_cfg blob (caller responsible for sanity-check) */ -static inline void fw_cfg_read_blob(u16 key, - void *buf, loff_t pos, size_t count) +static void fw_cfg_read_blob(u16 key, + void *buf, loff_t pos, size_t count) { u32 glk = -1U; acpi_status status; -- 2.16.1.73.g5832b7e9f2
[PATCH v15 03/11] fw_cfg: fix sparse warnings in fw_cfg_sel_endianness()
Dispatch to the appropriate iowrite() instead of casting restricted type to u16. - if fw_cfg_is_mmio: before: iowrite16(cpu_to_be16(key)) after: iowrite16be(key) - if !fw_cfg_is_mmio: before: iowrite16(cpu_to_le16(key)) after: iowrite16(key) which is equivalent on little-endian systems, where fw_cfg IO is supported. Fixes: $ make C=1 CF=-D__CHECK_ENDIAN__ drivers/firmware/qemu_fw_cfg.o drivers/firmware/qemu_fw_cfg.c:55:33: warning: restricted __be16 degrades to integer drivers/firmware/qemu_fw_cfg.c:55:52: warning: restricted __le16 degrades to integer Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> --- drivers/firmware/qemu_fw_cfg.c | 9 ++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 42601a3eaed5..6164731a3c35 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -50,9 +50,12 @@ static void __iomem *fw_cfg_reg_data; static DEFINE_MUTEX(fw_cfg_dev_lock); /* pick appropriate endianness for selector key */ -static inline u16 fw_cfg_sel_endianness(u16 key) +static void fw_cfg_sel_endianness(u16 key) { - return fw_cfg_is_mmio ? cpu_to_be16(key) : cpu_to_le16(key); + if (fw_cfg_is_mmio) + iowrite16be(key, fw_cfg_reg_ctrl); + else + iowrite16(key, fw_cfg_reg_ctrl); } /* read chunk of given fw_cfg blob (caller responsible for sanity-check) */ @@ -74,7 +77,7 @@ static inline void fw_cfg_read_blob(u16 key, } mutex_lock(_cfg_dev_lock); - iowrite16(fw_cfg_sel_endianness(key), fw_cfg_reg_ctrl); + fw_cfg_sel_endianness(key); while (pos-- > 0) ioread8(fw_cfg_reg_data); ioread8_rep(fw_cfg_reg_data, buf, count); -- 2.16.1.73.g5832b7e9f2
[PATCH v15 03/11] fw_cfg: fix sparse warnings in fw_cfg_sel_endianness()
Dispatch to the appropriate iowrite() instead of casting restricted type to u16. - if fw_cfg_is_mmio: before: iowrite16(cpu_to_be16(key)) after: iowrite16be(key) - if !fw_cfg_is_mmio: before: iowrite16(cpu_to_le16(key)) after: iowrite16(key) which is equivalent on little-endian systems, where fw_cfg IO is supported. Fixes: $ make C=1 CF=-D__CHECK_ENDIAN__ drivers/firmware/qemu_fw_cfg.o drivers/firmware/qemu_fw_cfg.c:55:33: warning: restricted __be16 degrades to integer drivers/firmware/qemu_fw_cfg.c:55:52: warning: restricted __le16 degrades to integer Signed-off-by: Marc-André Lureau --- drivers/firmware/qemu_fw_cfg.c | 9 ++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 42601a3eaed5..6164731a3c35 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -50,9 +50,12 @@ static void __iomem *fw_cfg_reg_data; static DEFINE_MUTEX(fw_cfg_dev_lock); /* pick appropriate endianness for selector key */ -static inline u16 fw_cfg_sel_endianness(u16 key) +static void fw_cfg_sel_endianness(u16 key) { - return fw_cfg_is_mmio ? cpu_to_be16(key) : cpu_to_le16(key); + if (fw_cfg_is_mmio) + iowrite16be(key, fw_cfg_reg_ctrl); + else + iowrite16(key, fw_cfg_reg_ctrl); } /* read chunk of given fw_cfg blob (caller responsible for sanity-check) */ @@ -74,7 +77,7 @@ static inline void fw_cfg_read_blob(u16 key, } mutex_lock(_cfg_dev_lock); - iowrite16(fw_cfg_sel_endianness(key), fw_cfg_reg_ctrl); + fw_cfg_sel_endianness(key); while (pos-- > 0) ioread8(fw_cfg_reg_data); ioread8_rep(fw_cfg_reg_data, buf, count); -- 2.16.1.73.g5832b7e9f2
[PATCH v15 00/11] fw_cfg: add DMA operations & etc/vmcoreinfo support
Hi, This series adds DMA operations support to the qemu fw_cfg kernel module and populates "etc/vmcoreinfo" with vmcoreinfo location details (entry added since qemu 2.11 with -device vmcoreinfo). v15: - fix fw_cfg.h uapi header #include - use BSD license for fw_cfg.h uapi header - move the uapi defines/structs for DMA & vmcoreinfo in the corresponding patch - use cpu_relax() instead of usleep_range(50, 100); - replace do { } while(true) by for (;;) - fix the rmb() call location - add a preliminary patch to handle error from fw_cfg_write_blob() - rewrite fw_cfg_sel_endianness() to wrap iowrite() calls v14: - add "fw_cfg: add a public uapi header" - fix sparse warnings & don't introduce new warnings - add memory barriers to force IO ordering - split fw_cfg_read_blob() in fw_cfg_read_blob_io() and fw_cfg_read_blob_dma() - add error handling to fw_cfg_read_blob() callers - minor stylistic changes v13: - reorder patch series, introduce DMA write before DMA read - do some measurements of DMA read speed-ups v12: - fix virt_to_phys(NULL) panic with CONFIG_DEBUG_VIRTUAL=y - do not use DMA read, except for kmalloc() memory we allocated ourself (only fw_cfg_register_dir_entries() so far) v11: - add #include in last patch, fixing kbuild .config test Marc-André Lureau (11): crash: export paddr_vmcoreinfo_note() fw_cfg: add a public uapi header fw_cfg: fix sparse warnings in fw_cfg_sel_endianness() fw_cfg: fix sparse warnings with fw_cfg_file fw_cfg: fix sparse warning reading FW_CFG_ID fw_cfg: fix sparse warnings around FW_CFG_FILE_DIR read fw_cfg: remove inline from fw_cfg_read_blob() fw_cfg: handle fw_cfg_read_blob() error fw_cfg: add DMA register fw_cfg: write vmcoreinfo details RFC: fw_cfg: do DMA read operation MAINTAINERS| 1 + drivers/firmware/qemu_fw_cfg.c | 334 + include/uapi/linux/fw_cfg.h| 97 kernel/crash_core.c| 1 + 4 files changed, 369 insertions(+), 64 deletions(-) create mode 100644 include/uapi/linux/fw_cfg.h -- 2.16.1.73.g5832b7e9f2
[PATCH v15 00/11] fw_cfg: add DMA operations & etc/vmcoreinfo support
Hi, This series adds DMA operations support to the qemu fw_cfg kernel module and populates "etc/vmcoreinfo" with vmcoreinfo location details (entry added since qemu 2.11 with -device vmcoreinfo). v15: - fix fw_cfg.h uapi header #include - use BSD license for fw_cfg.h uapi header - move the uapi defines/structs for DMA & vmcoreinfo in the corresponding patch - use cpu_relax() instead of usleep_range(50, 100); - replace do { } while(true) by for (;;) - fix the rmb() call location - add a preliminary patch to handle error from fw_cfg_write_blob() - rewrite fw_cfg_sel_endianness() to wrap iowrite() calls v14: - add "fw_cfg: add a public uapi header" - fix sparse warnings & don't introduce new warnings - add memory barriers to force IO ordering - split fw_cfg_read_blob() in fw_cfg_read_blob_io() and fw_cfg_read_blob_dma() - add error handling to fw_cfg_read_blob() callers - minor stylistic changes v13: - reorder patch series, introduce DMA write before DMA read - do some measurements of DMA read speed-ups v12: - fix virt_to_phys(NULL) panic with CONFIG_DEBUG_VIRTUAL=y - do not use DMA read, except for kmalloc() memory we allocated ourself (only fw_cfg_register_dir_entries() so far) v11: - add #include in last patch, fixing kbuild .config test Marc-André Lureau (11): crash: export paddr_vmcoreinfo_note() fw_cfg: add a public uapi header fw_cfg: fix sparse warnings in fw_cfg_sel_endianness() fw_cfg: fix sparse warnings with fw_cfg_file fw_cfg: fix sparse warning reading FW_CFG_ID fw_cfg: fix sparse warnings around FW_CFG_FILE_DIR read fw_cfg: remove inline from fw_cfg_read_blob() fw_cfg: handle fw_cfg_read_blob() error fw_cfg: add DMA register fw_cfg: write vmcoreinfo details RFC: fw_cfg: do DMA read operation MAINTAINERS| 1 + drivers/firmware/qemu_fw_cfg.c | 334 + include/uapi/linux/fw_cfg.h| 97 kernel/crash_core.c| 1 + 4 files changed, 369 insertions(+), 64 deletions(-) create mode 100644 include/uapi/linux/fw_cfg.h -- 2.16.1.73.g5832b7e9f2
[PATCH v15 09/11] fw_cfg: add DMA register
Add an optional kernel module (or command line) parameter using the following syntax: [qemu_fw_cfg.]ioport=@[::[:]] or [qemu_fw_cfg.]mmio=@[::[:]] and initializes the register address using given or default offset. Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> Reviewed-by: Gabriel Somlo <so...@cmu.edu> --- drivers/firmware/qemu_fw_cfg.c | 53 -- 1 file changed, 41 insertions(+), 12 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 5e6e5ac71dab..c28bec4b5663 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -10,20 +10,21 @@ * and select subsets of aarch64), a Device Tree node (on arm), or using * a kernel module (or command line) parameter with the following syntax: * - * [qemu_fw_cfg.]ioport=@[::] + * [qemu_fw_cfg.]ioport=@[::[:]] * or - * [qemu_fw_cfg.]mmio=@[::] + * [qemu_fw_cfg.]mmio=@[::[:]] * * where: * := size of ioport or mmio range * := physical base address of ioport or mmio range * := (optional) offset of control register * := (optional) offset of data register + * := (optional) offset of dma register * * e.g.: - * qemu_fw_cfg.ioport=2@0x510:0:1 (the default on x86) + * qemu_fw_cfg.ioport=12@0x510:0:1:4 (the default on x86) * or - * qemu_fw_cfg.mmio=0xA@0x902:8:0 (the default on arm) + * qemu_fw_cfg.mmio=16@0x902:8:0:16 (the default on arm) */ #include @@ -45,6 +46,7 @@ static resource_size_t fw_cfg_p_size; static void __iomem *fw_cfg_dev_base; static void __iomem *fw_cfg_reg_ctrl; static void __iomem *fw_cfg_reg_data; +static void __iomem *fw_cfg_reg_dma; /* atomic access to fw_cfg device (potentially slow i/o, so using mutex) */ static DEFINE_MUTEX(fw_cfg_dev_lock); @@ -104,12 +106,14 @@ static void fw_cfg_io_cleanup(void) # if (defined(CONFIG_ARM) || defined(CONFIG_ARM64)) # define FW_CFG_CTRL_OFF 0x08 # define FW_CFG_DATA_OFF 0x00 +# define FW_CFG_DMA_OFF 0x10 # elif (defined(CONFIG_PPC_PMAC) || defined(CONFIG_SPARC32)) /* ppc/mac,sun4m */ # define FW_CFG_CTRL_OFF 0x00 # define FW_CFG_DATA_OFF 0x02 # elif (defined(CONFIG_X86) || defined(CONFIG_SPARC64)) /* x86, sun4u */ # define FW_CFG_CTRL_OFF 0x00 # define FW_CFG_DATA_OFF 0x01 +# define FW_CFG_DMA_OFF 0x04 # else # error "QEMU FW_CFG not available on this architecture!" # endif @@ -119,7 +123,7 @@ static void fw_cfg_io_cleanup(void) static int fw_cfg_do_platform_probe(struct platform_device *pdev) { char sig[FW_CFG_SIG_SIZE]; - struct resource *range, *ctrl, *data; + struct resource *range, *ctrl, *data, *dma; /* acquire i/o range details */ fw_cfg_is_mmio = false; @@ -156,6 +160,7 @@ static int fw_cfg_do_platform_probe(struct platform_device *pdev) /* were custom register offsets provided (e.g. on the command line)? */ ctrl = platform_get_resource_byname(pdev, IORESOURCE_REG, "ctrl"); data = platform_get_resource_byname(pdev, IORESOURCE_REG, "data"); + dma = platform_get_resource_byname(pdev, IORESOURCE_REG, "dma"); if (ctrl && data) { fw_cfg_reg_ctrl = fw_cfg_dev_base + ctrl->start; fw_cfg_reg_data = fw_cfg_dev_base + data->start; @@ -165,6 +170,13 @@ static int fw_cfg_do_platform_probe(struct platform_device *pdev) fw_cfg_reg_data = fw_cfg_dev_base + FW_CFG_DATA_OFF; } + if (dma) + fw_cfg_reg_dma = fw_cfg_dev_base + dma->start; +#ifdef FW_CFG_DMA_OFF + else + fw_cfg_reg_dma = fw_cfg_dev_base + FW_CFG_DMA_OFF; +#endif + /* verify fw_cfg device signature */ if (fw_cfg_read_blob(FW_CFG_SIGNATURE, sig, 0, FW_CFG_SIG_SIZE) < 0 || @@ -630,6 +642,7 @@ static struct platform_device *fw_cfg_cmdline_dev; /* use special scanf/printf modifier for phys_addr_t, resource_size_t */ #define PH_ADDR_SCAN_FMT "@%" __PHYS_ADDR_PREFIX "i%n" \ ":%" __PHYS_ADDR_PREFIX "i" \ +":%" __PHYS_ADDR_PREFIX "i%n" \ ":%" __PHYS_ADDR_PREFIX "i%n" #define PH_ADDR_PR_1_FMT "0x%" __PHYS_ADDR_PREFIX "x@" \ @@ -639,12 +652,15 @@ static struct platform_device *fw_cfg_cmdline_dev; ":%" __PHYS_ADDR_PREFIX "u" \ ":%" __PHYS_ADDR_PREFIX "u" +#define PH_ADDR_PR_4_FMT PH_ADDR_PR_3_FMT \ +":%" __PHYS_ADDR_PREFIX "u" + static int fw_cfg_cmdline_set(const char *arg, const struct kernel_param *kp) { - struct resource res[3] = {}; + struct resource res[4]
[PATCH v15 11/11] RFC: fw_cfg: do DMA read operation
Modify fw_cfg_read_blob() to use DMA if the device supports it. Return errors, because the operation may fail. So far, only one call in fw_cfg_register_dir_entries() is using kmalloc'ed buf and is thus clearly eligible to DMA read. Initially, I didn't implement DMA read to speed up boot time, but as a first step before introducing DMA write (since read operations were already presents). Even more, I didn't realize fw-cfg entries were being read by the kernel during boot by default. But actally fw-cfg entries are being populated during module probe. I knew DMA improved a lot bios boot time (the main reason the DMA interface was added afaik). Let see the time it would take to read the whole ACPI tables (128kb allocated) # time cat /sys/firmware/qemu_fw_cfg/by_name/etc/acpi/tables/raw - with DMA: sys 0m0.003s - without DMA (-global fw_cfg.dma_enabled=off): sys 0m7.674s FW_CFG_FILE_DIR (0x19) is the only "file" that is read during kernel boot to populate sysfs qemu_fw_cfg directory, and it is quite small (1-2kb). Since it does not expose itself, in order to measure the time it takes to read such small file, I took a comparable sized file of 2048 bytes and exposed it (-fw_cfg test,file=file with a modified read_raw enabling DMA) # perf stat -r 100 cat /sys/firmware/qemu_fw_cfg/by_name/test/raw >/dev/null - with DMA: 0.636037 task-clock (msec) #0.141 CPUs utilized ( +- 1.19% ) - without DMA: 6.430128 task-clock (msec) #0.622 CPUs utilized ( +- 0.22% ) That's a few msec saved during boot by enabling DMA read (the gain would be more substantial if other & bigger fw-cfg entries are read by others from sysfs, unfortunately, it's not clear if we can always enable DMA there) Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> --- drivers/firmware/qemu_fw_cfg.c | 61 ++ 1 file changed, 50 insertions(+), 11 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 3015e77aebca..94df57e9be66 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -124,12 +124,47 @@ static ssize_t fw_cfg_dma_transfer(void *address, u32 length, u32 control) return ret; } +/* with acpi & dev locks taken */ +static ssize_t fw_cfg_read_blob_dma(u16 key, + void *buf, loff_t pos, size_t count) +{ + ssize_t ret; + + if (pos == 0) { + ret = fw_cfg_dma_transfer(buf, count, key << 16 + | FW_CFG_DMA_CTL_SELECT + | FW_CFG_DMA_CTL_READ); + } else { + fw_cfg_sel_endianness(key); + ret = fw_cfg_dma_transfer(NULL, pos, FW_CFG_DMA_CTL_SKIP); + if (ret < 0) + return ret; + ret = fw_cfg_dma_transfer(buf, count, + FW_CFG_DMA_CTL_READ); + } + + return ret; +} + +/* with acpi & dev locks taken */ +static ssize_t fw_cfg_read_blob_io(u16 key, + void *buf, loff_t pos, size_t count) +{ + fw_cfg_sel_endianness(key); + while (pos-- > 0) + ioread8(fw_cfg_reg_data); + ioread8_rep(fw_cfg_reg_data, buf, count); + return count; +} + /* read chunk of given fw_cfg blob (caller responsible for sanity-check) */ static ssize_t fw_cfg_read_blob(u16 key, - void *buf, loff_t pos, size_t count) + void *buf, loff_t pos, size_t count, + bool dma) { u32 glk = -1U; acpi_status status; + ssize_t ret; /* If we have ACPI, ensure mutual exclusion against any potential * device access by the firmware, e.g. via AML methods: @@ -143,14 +178,17 @@ static ssize_t fw_cfg_read_blob(u16 key, } mutex_lock(_cfg_dev_lock); - fw_cfg_sel_endianness(key); - while (pos-- > 0) - ioread8(fw_cfg_reg_data); - ioread8_rep(fw_cfg_reg_data, buf, count); + if (dma && fw_cfg_dma_enabled()) { + ret = fw_cfg_read_blob_dma(key, buf, pos, count); + } else { + ret = fw_cfg_read_blob_io(key, buf, pos, count); + } + mutex_unlock(_cfg_dev_lock); acpi_release_global_lock(glk); - return count; + + return ret; } #ifdef CONFIG_CRASH_CORE @@ -284,7 +322,7 @@ static int fw_cfg_do_platform_probe(struct platform_device *pdev) /* verify fw_cfg device signature */ if (fw_cfg_read_blob(FW_CFG_SIGNATURE, sig, - 0, FW_CFG_SIG_SIZE) < 0 || + 0, FW_CFG_SIG_SIZE, false) < 0 || memcmp(sig, "QEMU", FW_CFG_SIG_SIZE) != 0) { fw_cfg_io_cleanup
[PATCH v15 11/11] RFC: fw_cfg: do DMA read operation
Modify fw_cfg_read_blob() to use DMA if the device supports it. Return errors, because the operation may fail. So far, only one call in fw_cfg_register_dir_entries() is using kmalloc'ed buf and is thus clearly eligible to DMA read. Initially, I didn't implement DMA read to speed up boot time, but as a first step before introducing DMA write (since read operations were already presents). Even more, I didn't realize fw-cfg entries were being read by the kernel during boot by default. But actally fw-cfg entries are being populated during module probe. I knew DMA improved a lot bios boot time (the main reason the DMA interface was added afaik). Let see the time it would take to read the whole ACPI tables (128kb allocated) # time cat /sys/firmware/qemu_fw_cfg/by_name/etc/acpi/tables/raw - with DMA: sys 0m0.003s - without DMA (-global fw_cfg.dma_enabled=off): sys 0m7.674s FW_CFG_FILE_DIR (0x19) is the only "file" that is read during kernel boot to populate sysfs qemu_fw_cfg directory, and it is quite small (1-2kb). Since it does not expose itself, in order to measure the time it takes to read such small file, I took a comparable sized file of 2048 bytes and exposed it (-fw_cfg test,file=file with a modified read_raw enabling DMA) # perf stat -r 100 cat /sys/firmware/qemu_fw_cfg/by_name/test/raw >/dev/null - with DMA: 0.636037 task-clock (msec) #0.141 CPUs utilized ( +- 1.19% ) - without DMA: 6.430128 task-clock (msec) #0.622 CPUs utilized ( +- 0.22% ) That's a few msec saved during boot by enabling DMA read (the gain would be more substantial if other & bigger fw-cfg entries are read by others from sysfs, unfortunately, it's not clear if we can always enable DMA there) Signed-off-by: Marc-André Lureau --- drivers/firmware/qemu_fw_cfg.c | 61 ++ 1 file changed, 50 insertions(+), 11 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 3015e77aebca..94df57e9be66 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -124,12 +124,47 @@ static ssize_t fw_cfg_dma_transfer(void *address, u32 length, u32 control) return ret; } +/* with acpi & dev locks taken */ +static ssize_t fw_cfg_read_blob_dma(u16 key, + void *buf, loff_t pos, size_t count) +{ + ssize_t ret; + + if (pos == 0) { + ret = fw_cfg_dma_transfer(buf, count, key << 16 + | FW_CFG_DMA_CTL_SELECT + | FW_CFG_DMA_CTL_READ); + } else { + fw_cfg_sel_endianness(key); + ret = fw_cfg_dma_transfer(NULL, pos, FW_CFG_DMA_CTL_SKIP); + if (ret < 0) + return ret; + ret = fw_cfg_dma_transfer(buf, count, + FW_CFG_DMA_CTL_READ); + } + + return ret; +} + +/* with acpi & dev locks taken */ +static ssize_t fw_cfg_read_blob_io(u16 key, + void *buf, loff_t pos, size_t count) +{ + fw_cfg_sel_endianness(key); + while (pos-- > 0) + ioread8(fw_cfg_reg_data); + ioread8_rep(fw_cfg_reg_data, buf, count); + return count; +} + /* read chunk of given fw_cfg blob (caller responsible for sanity-check) */ static ssize_t fw_cfg_read_blob(u16 key, - void *buf, loff_t pos, size_t count) + void *buf, loff_t pos, size_t count, + bool dma) { u32 glk = -1U; acpi_status status; + ssize_t ret; /* If we have ACPI, ensure mutual exclusion against any potential * device access by the firmware, e.g. via AML methods: @@ -143,14 +178,17 @@ static ssize_t fw_cfg_read_blob(u16 key, } mutex_lock(_cfg_dev_lock); - fw_cfg_sel_endianness(key); - while (pos-- > 0) - ioread8(fw_cfg_reg_data); - ioread8_rep(fw_cfg_reg_data, buf, count); + if (dma && fw_cfg_dma_enabled()) { + ret = fw_cfg_read_blob_dma(key, buf, pos, count); + } else { + ret = fw_cfg_read_blob_io(key, buf, pos, count); + } + mutex_unlock(_cfg_dev_lock); acpi_release_global_lock(glk); - return count; + + return ret; } #ifdef CONFIG_CRASH_CORE @@ -284,7 +322,7 @@ static int fw_cfg_do_platform_probe(struct platform_device *pdev) /* verify fw_cfg device signature */ if (fw_cfg_read_blob(FW_CFG_SIGNATURE, sig, - 0, FW_CFG_SIG_SIZE) < 0 || + 0, FW_CFG_SIG_SIZE, false) < 0 || memcmp(sig, "QEMU", FW_CFG_SIG_SIZE) != 0) { fw_cfg_io_cleanup(); return -ENODEV; @@ -
[PATCH v15 09/11] fw_cfg: add DMA register
Add an optional kernel module (or command line) parameter using the following syntax: [qemu_fw_cfg.]ioport=@[::[:]] or [qemu_fw_cfg.]mmio=@[::[:]] and initializes the register address using given or default offset. Signed-off-by: Marc-André Lureau Reviewed-by: Gabriel Somlo --- drivers/firmware/qemu_fw_cfg.c | 53 -- 1 file changed, 41 insertions(+), 12 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 5e6e5ac71dab..c28bec4b5663 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -10,20 +10,21 @@ * and select subsets of aarch64), a Device Tree node (on arm), or using * a kernel module (or command line) parameter with the following syntax: * - * [qemu_fw_cfg.]ioport=@[::] + * [qemu_fw_cfg.]ioport=@[::[:]] * or - * [qemu_fw_cfg.]mmio=@[::] + * [qemu_fw_cfg.]mmio=@[::[:]] * * where: * := size of ioport or mmio range * := physical base address of ioport or mmio range * := (optional) offset of control register * := (optional) offset of data register + * := (optional) offset of dma register * * e.g.: - * qemu_fw_cfg.ioport=2@0x510:0:1 (the default on x86) + * qemu_fw_cfg.ioport=12@0x510:0:1:4 (the default on x86) * or - * qemu_fw_cfg.mmio=0xA@0x902:8:0 (the default on arm) + * qemu_fw_cfg.mmio=16@0x902:8:0:16 (the default on arm) */ #include @@ -45,6 +46,7 @@ static resource_size_t fw_cfg_p_size; static void __iomem *fw_cfg_dev_base; static void __iomem *fw_cfg_reg_ctrl; static void __iomem *fw_cfg_reg_data; +static void __iomem *fw_cfg_reg_dma; /* atomic access to fw_cfg device (potentially slow i/o, so using mutex) */ static DEFINE_MUTEX(fw_cfg_dev_lock); @@ -104,12 +106,14 @@ static void fw_cfg_io_cleanup(void) # if (defined(CONFIG_ARM) || defined(CONFIG_ARM64)) # define FW_CFG_CTRL_OFF 0x08 # define FW_CFG_DATA_OFF 0x00 +# define FW_CFG_DMA_OFF 0x10 # elif (defined(CONFIG_PPC_PMAC) || defined(CONFIG_SPARC32)) /* ppc/mac,sun4m */ # define FW_CFG_CTRL_OFF 0x00 # define FW_CFG_DATA_OFF 0x02 # elif (defined(CONFIG_X86) || defined(CONFIG_SPARC64)) /* x86, sun4u */ # define FW_CFG_CTRL_OFF 0x00 # define FW_CFG_DATA_OFF 0x01 +# define FW_CFG_DMA_OFF 0x04 # else # error "QEMU FW_CFG not available on this architecture!" # endif @@ -119,7 +123,7 @@ static void fw_cfg_io_cleanup(void) static int fw_cfg_do_platform_probe(struct platform_device *pdev) { char sig[FW_CFG_SIG_SIZE]; - struct resource *range, *ctrl, *data; + struct resource *range, *ctrl, *data, *dma; /* acquire i/o range details */ fw_cfg_is_mmio = false; @@ -156,6 +160,7 @@ static int fw_cfg_do_platform_probe(struct platform_device *pdev) /* were custom register offsets provided (e.g. on the command line)? */ ctrl = platform_get_resource_byname(pdev, IORESOURCE_REG, "ctrl"); data = platform_get_resource_byname(pdev, IORESOURCE_REG, "data"); + dma = platform_get_resource_byname(pdev, IORESOURCE_REG, "dma"); if (ctrl && data) { fw_cfg_reg_ctrl = fw_cfg_dev_base + ctrl->start; fw_cfg_reg_data = fw_cfg_dev_base + data->start; @@ -165,6 +170,13 @@ static int fw_cfg_do_platform_probe(struct platform_device *pdev) fw_cfg_reg_data = fw_cfg_dev_base + FW_CFG_DATA_OFF; } + if (dma) + fw_cfg_reg_dma = fw_cfg_dev_base + dma->start; +#ifdef FW_CFG_DMA_OFF + else + fw_cfg_reg_dma = fw_cfg_dev_base + FW_CFG_DMA_OFF; +#endif + /* verify fw_cfg device signature */ if (fw_cfg_read_blob(FW_CFG_SIGNATURE, sig, 0, FW_CFG_SIG_SIZE) < 0 || @@ -630,6 +642,7 @@ static struct platform_device *fw_cfg_cmdline_dev; /* use special scanf/printf modifier for phys_addr_t, resource_size_t */ #define PH_ADDR_SCAN_FMT "@%" __PHYS_ADDR_PREFIX "i%n" \ ":%" __PHYS_ADDR_PREFIX "i" \ +":%" __PHYS_ADDR_PREFIX "i%n" \ ":%" __PHYS_ADDR_PREFIX "i%n" #define PH_ADDR_PR_1_FMT "0x%" __PHYS_ADDR_PREFIX "x@" \ @@ -639,12 +652,15 @@ static struct platform_device *fw_cfg_cmdline_dev; ":%" __PHYS_ADDR_PREFIX "u" \ ":%" __PHYS_ADDR_PREFIX "u" +#define PH_ADDR_PR_4_FMT PH_ADDR_PR_3_FMT \ +":%" __PHYS_ADDR_PREFIX "u" + static int fw_cfg_cmdline_set(const char *arg, const struct kernel_param *kp) { - struct resource res[3] = {}; + struct resource res[4] = {}; char *str; phys_addr_t base; -
Re: [PATCH v14 2/9] fw_cfg: add a public uapi header
Hi On Thu, Feb 15, 2018 at 7:20 PM, Michael S. Tsirkin <m...@redhat.com> wrote: > On Thu, Feb 15, 2018 at 10:25:27AM +0100, Marc-Andre Lureau wrote: >> Hi >> >> On Wed, Feb 14, 2018 at 9:41 PM, Michael S. Tsirkin <m...@redhat.com> wrote: >> > On Wed, Feb 14, 2018 at 03:18:43PM +0100, Marc-André Lureau wrote: >> >> Create a common header file for well-known values and structures to be >> >> shared by the Linux kernel with qemu or other projects. >> >> >> >> Suggested-by: Michael S. Tsirkin <m...@redhat.com> >> >> Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> >> >> >> >> --- >> >> >> >> The related qemu patch making use of it, to be submitted: >> >> https://github.com/elmarco/qemu/commit/4884fc9e9c4c4467a371e5a40f3181239e1b70f5 >> >> --- >> >> MAINTAINERS| 1 + >> >> drivers/firmware/qemu_fw_cfg.c | 22 + >> >> include/uapi/linux/fw_cfg.h| 102 >> >> + >> >> 3 files changed, 105 insertions(+), 20 deletions(-) >> >> create mode 100644 include/uapi/linux/fw_cfg.h >> >> >> >> diff --git a/MAINTAINERS b/MAINTAINERS >> >> index 3bdc260e36b7..a66b65f62811 100644 >> >> --- a/MAINTAINERS >> >> +++ b/MAINTAINERS >> >> @@ -11352,6 +11352,7 @@ M:"Michael S. Tsirkin" <m...@redhat.com> >> >> L: qemu-de...@nongnu.org >> >> S: Maintained >> >> F: drivers/firmware/qemu_fw_cfg.c >> >> +F: include/uapi/linux/fw_cfg.h >> >> >> >> QIB DRIVER >> >> M: Dennis Dalessandro <dennis.dalessan...@intel.com> >> >> diff --git a/drivers/firmware/qemu_fw_cfg.c >> >> b/drivers/firmware/qemu_fw_cfg.c >> >> index a41b572eeeb1..90f467232777 100644 >> >> --- a/drivers/firmware/qemu_fw_cfg.c >> >> +++ b/drivers/firmware/qemu_fw_cfg.c >> >> @@ -32,30 +32,12 @@ >> >> #include >> >> #include >> >> #include >> >> +#include >> >> >> >> MODULE_AUTHOR("Gabriel L. Somlo <so...@cmu.edu>"); >> >> MODULE_DESCRIPTION("QEMU fw_cfg sysfs support"); >> >> MODULE_LICENSE("GPL"); >> >> >> >> -/* selector key values for "well-known" fw_cfg entries */ >> >> -#define FW_CFG_SIGNATURE 0x00 >> >> -#define FW_CFG_ID 0x01 >> >> -#define FW_CFG_FILE_DIR 0x19 >> >> - >> >> -/* size in bytes of fw_cfg signature */ >> >> -#define FW_CFG_SIG_SIZE 4 >> >> - >> >> -/* fw_cfg "file name" is up to 56 characters (including terminating nul) >> >> */ >> >> -#define FW_CFG_MAX_FILE_PATH 56 >> >> - >> >> -/* fw_cfg file directory entry type */ >> >> -struct fw_cfg_file { >> >> - u32 size; >> >> - u16 select; >> >> - u16 reserved; >> >> - char name[FW_CFG_MAX_FILE_PATH]; >> >> -}; >> >> - >> >> /* fw_cfg device i/o register addresses */ >> >> static bool fw_cfg_is_mmio; >> >> static phys_addr_t fw_cfg_p_base; >> >> @@ -597,7 +579,7 @@ MODULE_DEVICE_TABLE(of, fw_cfg_sysfs_mmio_match); >> >> >> >> #ifdef CONFIG_ACPI >> >> static const struct acpi_device_id fw_cfg_sysfs_acpi_match[] = { >> >> - { "QEMU0002", }, >> >> + { FW_CFG_ACPI_DEVICE_ID, }, >> >> {}, >> >> }; >> >> MODULE_DEVICE_TABLE(acpi, fw_cfg_sysfs_acpi_match); >> >> diff --git a/include/uapi/linux/fw_cfg.h b/include/uapi/linux/fw_cfg.h >> >> new file mode 100644 >> >> index ..5b8136ce46ee >> >> --- /dev/null >> >> +++ b/include/uapi/linux/fw_cfg.h >> >> @@ -0,0 +1,102 @@ >> >> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ >> >> +#ifndef _LINUX_FW_CFG_H >> >> +#define _LINUX_FW_CFG_H >> >> + >> >> +#include >> >> + >> >> +#define FW_CFG_ACPI_DEVICE_ID"QEMU0002" >> >> + >> >> +/* selector key values for "well-known" fw_cfg entries */ >> >> +#define FW_CFG_SIGNATURE 0x00 >> >> +#define FW_CFG_ID0x01 >> >> +#define FW_CFG_UUI
Re: [PATCH v14 2/9] fw_cfg: add a public uapi header
Hi On Thu, Feb 15, 2018 at 7:20 PM, Michael S. Tsirkin wrote: > On Thu, Feb 15, 2018 at 10:25:27AM +0100, Marc-Andre Lureau wrote: >> Hi >> >> On Wed, Feb 14, 2018 at 9:41 PM, Michael S. Tsirkin wrote: >> > On Wed, Feb 14, 2018 at 03:18:43PM +0100, Marc-André Lureau wrote: >> >> Create a common header file for well-known values and structures to be >> >> shared by the Linux kernel with qemu or other projects. >> >> >> >> Suggested-by: Michael S. Tsirkin >> >> Signed-off-by: Marc-André Lureau >> >> >> >> --- >> >> >> >> The related qemu patch making use of it, to be submitted: >> >> https://github.com/elmarco/qemu/commit/4884fc9e9c4c4467a371e5a40f3181239e1b70f5 >> >> --- >> >> MAINTAINERS| 1 + >> >> drivers/firmware/qemu_fw_cfg.c | 22 + >> >> include/uapi/linux/fw_cfg.h| 102 >> >> + >> >> 3 files changed, 105 insertions(+), 20 deletions(-) >> >> create mode 100644 include/uapi/linux/fw_cfg.h >> >> >> >> diff --git a/MAINTAINERS b/MAINTAINERS >> >> index 3bdc260e36b7..a66b65f62811 100644 >> >> --- a/MAINTAINERS >> >> +++ b/MAINTAINERS >> >> @@ -11352,6 +11352,7 @@ M:"Michael S. Tsirkin" >> >> L: qemu-de...@nongnu.org >> >> S: Maintained >> >> F: drivers/firmware/qemu_fw_cfg.c >> >> +F: include/uapi/linux/fw_cfg.h >> >> >> >> QIB DRIVER >> >> M: Dennis Dalessandro >> >> diff --git a/drivers/firmware/qemu_fw_cfg.c >> >> b/drivers/firmware/qemu_fw_cfg.c >> >> index a41b572eeeb1..90f467232777 100644 >> >> --- a/drivers/firmware/qemu_fw_cfg.c >> >> +++ b/drivers/firmware/qemu_fw_cfg.c >> >> @@ -32,30 +32,12 @@ >> >> #include >> >> #include >> >> #include >> >> +#include >> >> >> >> MODULE_AUTHOR("Gabriel L. Somlo "); >> >> MODULE_DESCRIPTION("QEMU fw_cfg sysfs support"); >> >> MODULE_LICENSE("GPL"); >> >> >> >> -/* selector key values for "well-known" fw_cfg entries */ >> >> -#define FW_CFG_SIGNATURE 0x00 >> >> -#define FW_CFG_ID 0x01 >> >> -#define FW_CFG_FILE_DIR 0x19 >> >> - >> >> -/* size in bytes of fw_cfg signature */ >> >> -#define FW_CFG_SIG_SIZE 4 >> >> - >> >> -/* fw_cfg "file name" is up to 56 characters (including terminating nul) >> >> */ >> >> -#define FW_CFG_MAX_FILE_PATH 56 >> >> - >> >> -/* fw_cfg file directory entry type */ >> >> -struct fw_cfg_file { >> >> - u32 size; >> >> - u16 select; >> >> - u16 reserved; >> >> - char name[FW_CFG_MAX_FILE_PATH]; >> >> -}; >> >> - >> >> /* fw_cfg device i/o register addresses */ >> >> static bool fw_cfg_is_mmio; >> >> static phys_addr_t fw_cfg_p_base; >> >> @@ -597,7 +579,7 @@ MODULE_DEVICE_TABLE(of, fw_cfg_sysfs_mmio_match); >> >> >> >> #ifdef CONFIG_ACPI >> >> static const struct acpi_device_id fw_cfg_sysfs_acpi_match[] = { >> >> - { "QEMU0002", }, >> >> + { FW_CFG_ACPI_DEVICE_ID, }, >> >> {}, >> >> }; >> >> MODULE_DEVICE_TABLE(acpi, fw_cfg_sysfs_acpi_match); >> >> diff --git a/include/uapi/linux/fw_cfg.h b/include/uapi/linux/fw_cfg.h >> >> new file mode 100644 >> >> index ..5b8136ce46ee >> >> --- /dev/null >> >> +++ b/include/uapi/linux/fw_cfg.h >> >> @@ -0,0 +1,102 @@ >> >> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ >> >> +#ifndef _LINUX_FW_CFG_H >> >> +#define _LINUX_FW_CFG_H >> >> + >> >> +#include >> >> + >> >> +#define FW_CFG_ACPI_DEVICE_ID"QEMU0002" >> >> + >> >> +/* selector key values for "well-known" fw_cfg entries */ >> >> +#define FW_CFG_SIGNATURE 0x00 >> >> +#define FW_CFG_ID0x01 >> >> +#define FW_CFG_UUID 0x02 >> >> +#define FW_CFG_RAM_SIZE 0x03 >> >> +#define FW_CFG_NOGRAPHIC 0x04 >> >> +#define FW_CFG_NB_CPUS
Re: [PATCH v14 8/9] fw_cfg: write vmcoreinfo details
Hi On Thu, Feb 15, 2018 at 7:09 PM, Michael S. Tsirkin <m...@redhat.com> wrote: > On Wed, Feb 14, 2018 at 03:18:49PM +0100, Marc-André Lureau wrote: >> If the "etc/vmcoreinfo" fw_cfg file is present and we are not running >> the kdump kernel, write the addr/size of the vmcoreinfo ELF note. >> >> The DMA operation is expected to run synchronously with today qemu, >> but the specification states that it may become async, so we run >> "control" field check in a loop for eventual changes. >> >> Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> >> --- >> drivers/firmware/qemu_fw_cfg.c | 144 >> - >> 1 file changed, 141 insertions(+), 3 deletions(-) >> >> diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c >> index 37638b95cb45..69939e2529f2 100644 >> --- a/drivers/firmware/qemu_fw_cfg.c >> +++ b/drivers/firmware/qemu_fw_cfg.c >> @@ -34,11 +34,17 @@ >> #include >> #include >> #include >> +#include >> +#include >> +#include >> >> MODULE_AUTHOR("Gabriel L. Somlo <so...@cmu.edu>"); >> MODULE_DESCRIPTION("QEMU fw_cfg sysfs support"); >> MODULE_LICENSE("GPL"); >> >> +/* fw_cfg revision attribute, in /sys/firmware/qemu_fw_cfg top-level dir. */ >> +static u32 fw_cfg_rev; >> + >> /* fw_cfg device i/o register addresses */ >> static bool fw_cfg_is_mmio; >> static phys_addr_t fw_cfg_p_base; >> @@ -59,6 +65,65 @@ static inline u16 fw_cfg_sel_endianness(u16 key) >> (u16 __force)cpu_to_le16(key); >> } >> >> +static inline bool fw_cfg_dma_enabled(void) >> +{ >> + return (fw_cfg_rev & FW_CFG_VERSION_DMA) && fw_cfg_reg_dma; >> +} >> + >> +/* qemu fw_cfg device is sync today, but spec says it may become async */ >> +static void fw_cfg_wait_for_control(struct fw_cfg_dma_access *d) >> +{ >> + do { >> + u32 ctrl = be32_to_cpu(READ_ONCE(d->control)); >> + >> + if ((ctrl & ~FW_CFG_DMA_CTL_ERROR) == 0) >> + return; >> + >> + usleep_range(50, 100); > > I would just do cpu_relax() here. ok, I didn't know that one. > >> + } while (true); >> + >> + /* do not reorder the read to d->control */ >> + rmb(); > > Hmm. I don't really understand the comment. > Is this code ever reacheable? How does it help? I thought that's what you suggested in v13 review, but true, I should replace the return with a break to reach it. Is that what you expect too? (my understanding is to make sure the READ_ONCE(control) in wait_for_control happens before READ_ONCE(control) after in dma_transfer) > >> +} >> + >> +static ssize_t fw_cfg_dma_transfer(void *address, u32 length, u32 control) >> +{ >> + phys_addr_t dma; >> + struct fw_cfg_dma_access *d = NULL; >> + ssize_t ret = length; >> + >> + d = kmalloc(sizeof(*d), GFP_KERNEL); >> + if (!d) { >> + ret = -ENOMEM; >> + goto end; >> + } >> + >> + /* fw_cfg device does not need IOMMU protection, so use physical >> addresses */ >> + *d = (struct fw_cfg_dma_access) { >> + .address = cpu_to_be64(address ? virt_to_phys(address) : 0), >> + .length = cpu_to_be32(length), >> + .control = cpu_to_be32(control) >> + }; >> + >> + dma = virt_to_phys(d); >> + >> + iowrite32be((u64)dma >> 32, fw_cfg_reg_dma); >> + /* force memory to sync before notifying device via MMIO */ >> + wmb(); >> + iowrite32be(dma, fw_cfg_reg_dma + 4); >> + >> + fw_cfg_wait_for_control(d); >> + >> + if (be32_to_cpu(READ_ONCE(d->control)) & FW_CFG_DMA_CTL_ERROR) { >> + ret = -EIO; >> + } >> + >> +end: >> + kfree(d); >> + >> + return ret; >> +} >> + >> /* read chunk of given fw_cfg blob (caller responsible for sanity-check) */ >> static inline void fw_cfg_read_blob(u16 key, >> void *buf, loff_t pos, size_t count) >> @@ -87,6 +152,47 @@ static inline void fw_cfg_read_blob(u16 key, >> acpi_release_global_lock(glk); >> } >> >> +#ifdef CONFIG_CRASH_CORE >> +/* write chunk of given fw_cfg blob (caller responsible for sanity-check) */ >> +static ssize_t fw_cfg_write_blob(u16 key, >> +
Re: [PATCH v14 8/9] fw_cfg: write vmcoreinfo details
Hi On Thu, Feb 15, 2018 at 7:09 PM, Michael S. Tsirkin wrote: > On Wed, Feb 14, 2018 at 03:18:49PM +0100, Marc-André Lureau wrote: >> If the "etc/vmcoreinfo" fw_cfg file is present and we are not running >> the kdump kernel, write the addr/size of the vmcoreinfo ELF note. >> >> The DMA operation is expected to run synchronously with today qemu, >> but the specification states that it may become async, so we run >> "control" field check in a loop for eventual changes. >> >> Signed-off-by: Marc-André Lureau >> --- >> drivers/firmware/qemu_fw_cfg.c | 144 >> - >> 1 file changed, 141 insertions(+), 3 deletions(-) >> >> diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c >> index 37638b95cb45..69939e2529f2 100644 >> --- a/drivers/firmware/qemu_fw_cfg.c >> +++ b/drivers/firmware/qemu_fw_cfg.c >> @@ -34,11 +34,17 @@ >> #include >> #include >> #include >> +#include >> +#include >> +#include >> >> MODULE_AUTHOR("Gabriel L. Somlo "); >> MODULE_DESCRIPTION("QEMU fw_cfg sysfs support"); >> MODULE_LICENSE("GPL"); >> >> +/* fw_cfg revision attribute, in /sys/firmware/qemu_fw_cfg top-level dir. */ >> +static u32 fw_cfg_rev; >> + >> /* fw_cfg device i/o register addresses */ >> static bool fw_cfg_is_mmio; >> static phys_addr_t fw_cfg_p_base; >> @@ -59,6 +65,65 @@ static inline u16 fw_cfg_sel_endianness(u16 key) >> (u16 __force)cpu_to_le16(key); >> } >> >> +static inline bool fw_cfg_dma_enabled(void) >> +{ >> + return (fw_cfg_rev & FW_CFG_VERSION_DMA) && fw_cfg_reg_dma; >> +} >> + >> +/* qemu fw_cfg device is sync today, but spec says it may become async */ >> +static void fw_cfg_wait_for_control(struct fw_cfg_dma_access *d) >> +{ >> + do { >> + u32 ctrl = be32_to_cpu(READ_ONCE(d->control)); >> + >> + if ((ctrl & ~FW_CFG_DMA_CTL_ERROR) == 0) >> + return; >> + >> + usleep_range(50, 100); > > I would just do cpu_relax() here. ok, I didn't know that one. > >> + } while (true); >> + >> + /* do not reorder the read to d->control */ >> + rmb(); > > Hmm. I don't really understand the comment. > Is this code ever reacheable? How does it help? I thought that's what you suggested in v13 review, but true, I should replace the return with a break to reach it. Is that what you expect too? (my understanding is to make sure the READ_ONCE(control) in wait_for_control happens before READ_ONCE(control) after in dma_transfer) > >> +} >> + >> +static ssize_t fw_cfg_dma_transfer(void *address, u32 length, u32 control) >> +{ >> + phys_addr_t dma; >> + struct fw_cfg_dma_access *d = NULL; >> + ssize_t ret = length; >> + >> + d = kmalloc(sizeof(*d), GFP_KERNEL); >> + if (!d) { >> + ret = -ENOMEM; >> + goto end; >> + } >> + >> + /* fw_cfg device does not need IOMMU protection, so use physical >> addresses */ >> + *d = (struct fw_cfg_dma_access) { >> + .address = cpu_to_be64(address ? virt_to_phys(address) : 0), >> + .length = cpu_to_be32(length), >> + .control = cpu_to_be32(control) >> + }; >> + >> + dma = virt_to_phys(d); >> + >> + iowrite32be((u64)dma >> 32, fw_cfg_reg_dma); >> + /* force memory to sync before notifying device via MMIO */ >> + wmb(); >> + iowrite32be(dma, fw_cfg_reg_dma + 4); >> + >> + fw_cfg_wait_for_control(d); >> + >> + if (be32_to_cpu(READ_ONCE(d->control)) & FW_CFG_DMA_CTL_ERROR) { >> + ret = -EIO; >> + } >> + >> +end: >> + kfree(d); >> + >> + return ret; >> +} >> + >> /* read chunk of given fw_cfg blob (caller responsible for sanity-check) */ >> static inline void fw_cfg_read_blob(u16 key, >> void *buf, loff_t pos, size_t count) >> @@ -87,6 +152,47 @@ static inline void fw_cfg_read_blob(u16 key, >> acpi_release_global_lock(glk); >> } >> >> +#ifdef CONFIG_CRASH_CORE >> +/* write chunk of given fw_cfg blob (caller responsible for sanity-check) */ >> +static ssize_t fw_cfg_write_blob(u16 key, >> + void *buf, loff_t pos, size_t count) >> +{ >> + u3
[PATCH v14 1/9] crash: export paddr_vmcoreinfo_note()
The following patch is going to use the symbol from the fw_cfg module, to call the function and write the note location details in the vmcoreinfo entry, so qemu can produce dumps with the vmcoreinfo note. CC: Andrew Morton <a...@linux-foundation.org> CC: Baoquan He <b...@redhat.com> CC: Dave Young <dyo...@redhat.com> CC: Dave Young <dyo...@redhat.com> CC: Hari Bathini <hbath...@linux.vnet.ibm.com> CC: Tony Luck <tony.l...@intel.com> CC: Vivek Goyal <vgo...@redhat.com> Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> Acked-by: Gabriel Somlo <so...@cmu.edu> --- kernel/crash_core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/crash_core.c b/kernel/crash_core.c index 4f63597c824d..a93590cdd9e1 100644 --- a/kernel/crash_core.c +++ b/kernel/crash_core.c @@ -376,6 +376,7 @@ phys_addr_t __weak paddr_vmcoreinfo_note(void) { return __pa(vmcoreinfo_note); } +EXPORT_SYMBOL(paddr_vmcoreinfo_note); static int __init crash_save_vmcoreinfo_init(void) { -- 2.16.1.73.g5832b7e9f2
[PATCH v14 1/9] crash: export paddr_vmcoreinfo_note()
The following patch is going to use the symbol from the fw_cfg module, to call the function and write the note location details in the vmcoreinfo entry, so qemu can produce dumps with the vmcoreinfo note. CC: Andrew Morton CC: Baoquan He CC: Dave Young CC: Dave Young CC: Hari Bathini CC: Tony Luck CC: Vivek Goyal Signed-off-by: Marc-André Lureau Acked-by: Gabriel Somlo --- kernel/crash_core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/crash_core.c b/kernel/crash_core.c index 4f63597c824d..a93590cdd9e1 100644 --- a/kernel/crash_core.c +++ b/kernel/crash_core.c @@ -376,6 +376,7 @@ phys_addr_t __weak paddr_vmcoreinfo_note(void) { return __pa(vmcoreinfo_note); } +EXPORT_SYMBOL(paddr_vmcoreinfo_note); static int __init crash_save_vmcoreinfo_init(void) { -- 2.16.1.73.g5832b7e9f2
[PATCH v14 3/9] fw_cfg: fix sparse warnings in fw_cfg_sel_endianness()
The function is used for both LE & BE target type, use __force casting. Fixes: $ make C=1 CF=-D__CHECK_ENDIAN__ drivers/firmware/qemu_fw_cfg.o drivers/firmware/qemu_fw_cfg.c:55:33: warning: restricted __be16 degrades to integer drivers/firmware/qemu_fw_cfg.c:55:52: warning: restricted __le16 degrades to integer Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> --- drivers/firmware/qemu_fw_cfg.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 90f467232777..85e693287d87 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -52,7 +52,9 @@ static DEFINE_MUTEX(fw_cfg_dev_lock); /* pick appropriate endianness for selector key */ static inline u16 fw_cfg_sel_endianness(u16 key) { - return fw_cfg_is_mmio ? cpu_to_be16(key) : cpu_to_le16(key); + return fw_cfg_is_mmio ? + (u16 __force)cpu_to_be16(key) : + (u16 __force)cpu_to_le16(key); } /* read chunk of given fw_cfg blob (caller responsible for sanity-check) */ -- 2.16.1.73.g5832b7e9f2
[PATCH v14 3/9] fw_cfg: fix sparse warnings in fw_cfg_sel_endianness()
The function is used for both LE & BE target type, use __force casting. Fixes: $ make C=1 CF=-D__CHECK_ENDIAN__ drivers/firmware/qemu_fw_cfg.o drivers/firmware/qemu_fw_cfg.c:55:33: warning: restricted __be16 degrades to integer drivers/firmware/qemu_fw_cfg.c:55:52: warning: restricted __le16 degrades to integer Signed-off-by: Marc-André Lureau --- drivers/firmware/qemu_fw_cfg.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 90f467232777..85e693287d87 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -52,7 +52,9 @@ static DEFINE_MUTEX(fw_cfg_dev_lock); /* pick appropriate endianness for selector key */ static inline u16 fw_cfg_sel_endianness(u16 key) { - return fw_cfg_is_mmio ? cpu_to_be16(key) : cpu_to_le16(key); + return fw_cfg_is_mmio ? + (u16 __force)cpu_to_be16(key) : + (u16 __force)cpu_to_le16(key); } /* read chunk of given fw_cfg blob (caller responsible for sanity-check) */ -- 2.16.1.73.g5832b7e9f2
[PATCH v14 6/9] fw_cfg: fix sparse warnings around FW_CFG_FILE_DIR read
Use struct fw_cfg_files to read the directory size, fixing the sparse warnings: drivers/firmware/qemu_fw_cfg.c:485:17: warning: cast to restricted __be32 Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> --- drivers/firmware/qemu_fw_cfg.c | 7 --- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 4c4813409447..c4c726841ba7 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -478,18 +478,19 @@ static int fw_cfg_register_dir_entries(void) { int ret = 0; u32 count, i; + struct fw_cfg_files files; struct fw_cfg_file *dir; size_t dir_size; - fw_cfg_read_blob(FW_CFG_FILE_DIR, , 0, sizeof(count)); - count = be32_to_cpu(count); + fw_cfg_read_blob(FW_CFG_FILE_DIR, , 0, sizeof(files.count)); + count = be32_to_cpu(files.count); dir_size = count * sizeof(struct fw_cfg_file); dir = kmalloc(dir_size, GFP_KERNEL); if (!dir) return -ENOMEM; - fw_cfg_read_blob(FW_CFG_FILE_DIR, dir, sizeof(count), dir_size); + fw_cfg_read_blob(FW_CFG_FILE_DIR, dir, sizeof(files.count), dir_size); for (i = 0; i < count; i++) { ret = fw_cfg_register_file([i]); -- 2.16.1.73.g5832b7e9f2
[PATCH v14 6/9] fw_cfg: fix sparse warnings around FW_CFG_FILE_DIR read
Use struct fw_cfg_files to read the directory size, fixing the sparse warnings: drivers/firmware/qemu_fw_cfg.c:485:17: warning: cast to restricted __be32 Signed-off-by: Marc-André Lureau --- drivers/firmware/qemu_fw_cfg.c | 7 --- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 4c4813409447..c4c726841ba7 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -478,18 +478,19 @@ static int fw_cfg_register_dir_entries(void) { int ret = 0; u32 count, i; + struct fw_cfg_files files; struct fw_cfg_file *dir; size_t dir_size; - fw_cfg_read_blob(FW_CFG_FILE_DIR, , 0, sizeof(count)); - count = be32_to_cpu(count); + fw_cfg_read_blob(FW_CFG_FILE_DIR, , 0, sizeof(files.count)); + count = be32_to_cpu(files.count); dir_size = count * sizeof(struct fw_cfg_file); dir = kmalloc(dir_size, GFP_KERNEL); if (!dir) return -ENOMEM; - fw_cfg_read_blob(FW_CFG_FILE_DIR, dir, sizeof(count), dir_size); + fw_cfg_read_blob(FW_CFG_FILE_DIR, dir, sizeof(files.count), dir_size); for (i = 0; i < count; i++) { ret = fw_cfg_register_file([i]); -- 2.16.1.73.g5832b7e9f2
[PATCH v14 9/9] RFC: fw_cfg: do DMA read operation
Modify fw_cfg_read_blob() to use DMA if the device supports it. Return errors, because the operation may fail. So far, only one call in fw_cfg_register_dir_entries() is using kmalloc'ed buf and is thus clearly eligible to DMA read. Initially, I didn't implement DMA read to speed up boot time, but as a first step before introducing DMA write (since read operations were already presents). Even more, I didn't realize fw-cfg entries were being read by the kernel during boot by default. But actally fw-cfg entries are being populated during module probe. I knew DMA improved a lot bios boot time (the main reason the DMA interface was added afaik). Let see the time it would take to read the whole ACPI tables (128kb allocated) # time cat /sys/firmware/qemu_fw_cfg/by_name/etc/acpi/tables/raw - with DMA: sys 0m0.003s - without DMA (-global fw_cfg.dma_enabled=off): sys 0m7.674s FW_CFG_FILE_DIR (0x19) is the only "file" that is read during kernel boot to populate sysfs qemu_fw_cfg directory, and it is quite small (1-2kb). Since it does not expose itself, in order to measure the time it takes to read such small file, I took a comparable sized file of 2048 bytes and exposed it (-fw_cfg test,file=file with a modified read_raw enabling DMA) # perf stat -r 100 cat /sys/firmware/qemu_fw_cfg/by_name/test/raw >/dev/null - with DMA: 0.636037 task-clock (msec) #0.141 CPUs utilized ( +- 1.19% ) - without DMA: 6.430128 task-clock (msec) #0.622 CPUs utilized ( +- 0.22% ) That's a few msec saved during boot by enabling DMA read (the gain would be more substantial if other & bigger fw-cfg entries are read by others from sysfs, unfortunately, it's not clear if we can always enable DMA there) Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> --- drivers/firmware/qemu_fw_cfg.c | 80 ++ 1 file changed, 66 insertions(+), 14 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 69939e2529f2..ba9b907a4399 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -124,12 +124,46 @@ static ssize_t fw_cfg_dma_transfer(void *address, u32 length, u32 control) return ret; } +/* with acpi & dev locks taken */ +static ssize_t fw_cfg_read_blob_dma(u16 key, + void *buf, loff_t pos, size_t count) +{ + ssize_t ret; + + if (pos == 0) { + ret = fw_cfg_dma_transfer(buf, count, key << 16 + | FW_CFG_DMA_CTL_SELECT + | FW_CFG_DMA_CTL_READ); + } else { + iowrite16(fw_cfg_sel_endianness(key), fw_cfg_reg_ctrl); + ret = fw_cfg_dma_transfer(NULL, pos, FW_CFG_DMA_CTL_SKIP); + if (ret < 0) + return ret; + ret = fw_cfg_dma_transfer(buf, count, + FW_CFG_DMA_CTL_READ); + } + + return ret; +} + +/* with acpi & dev locks taken */ +static void fw_cfg_read_blob_io(u16 key, + void *buf, loff_t pos, size_t count) +{ + iowrite16(fw_cfg_sel_endianness(key), fw_cfg_reg_ctrl); + while (pos-- > 0) + ioread8(fw_cfg_reg_data); + ioread8_rep(fw_cfg_reg_data, buf, count); +} + /* read chunk of given fw_cfg blob (caller responsible for sanity-check) */ -static inline void fw_cfg_read_blob(u16 key, - void *buf, loff_t pos, size_t count) +static ssize_t fw_cfg_read_blob(u16 key, + void *buf, loff_t pos, size_t count, + bool dma) { u32 glk = -1U; acpi_status status; + ssize_t ret = count; /* If we have ACPI, ensure mutual exclusion against any potential * device access by the firmware, e.g. via AML methods: @@ -139,17 +173,21 @@ static inline void fw_cfg_read_blob(u16 key, /* Should never get here */ WARN(1, "fw_cfg_read_blob: Failed to lock ACPI!\n"); memset(buf, 0, count); - return; + return -EINVAL; } mutex_lock(_cfg_dev_lock); - iowrite16(fw_cfg_sel_endianness(key), fw_cfg_reg_ctrl); - while (pos-- > 0) - ioread8(fw_cfg_reg_data); - ioread8_rep(fw_cfg_reg_data, buf, count); + if (dma && fw_cfg_dma_enabled()) { + ret = fw_cfg_read_blob_dma(key, buf, pos, count); + } else { + fw_cfg_read_blob_io(key, buf, pos, count); + } + mutex_unlock(_cfg_dev_lock); acpi_release_global_lock(glk); + + return ret; } #ifdef CONFIG_CRASH_CORE @@ -282,8 +320,9 @@ static int fw_cfg_do_platform_probe(struct platform_device *pdev) #endif
[PATCH v14 8/9] fw_cfg: write vmcoreinfo details
If the "etc/vmcoreinfo" fw_cfg file is present and we are not running the kdump kernel, write the addr/size of the vmcoreinfo ELF note. The DMA operation is expected to run synchronously with today qemu, but the specification states that it may become async, so we run "control" field check in a loop for eventual changes. Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> --- drivers/firmware/qemu_fw_cfg.c | 144 - 1 file changed, 141 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 37638b95cb45..69939e2529f2 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -34,11 +34,17 @@ #include #include #include +#include +#include +#include MODULE_AUTHOR("Gabriel L. Somlo <so...@cmu.edu>"); MODULE_DESCRIPTION("QEMU fw_cfg sysfs support"); MODULE_LICENSE("GPL"); +/* fw_cfg revision attribute, in /sys/firmware/qemu_fw_cfg top-level dir. */ +static u32 fw_cfg_rev; + /* fw_cfg device i/o register addresses */ static bool fw_cfg_is_mmio; static phys_addr_t fw_cfg_p_base; @@ -59,6 +65,65 @@ static inline u16 fw_cfg_sel_endianness(u16 key) (u16 __force)cpu_to_le16(key); } +static inline bool fw_cfg_dma_enabled(void) +{ + return (fw_cfg_rev & FW_CFG_VERSION_DMA) && fw_cfg_reg_dma; +} + +/* qemu fw_cfg device is sync today, but spec says it may become async */ +static void fw_cfg_wait_for_control(struct fw_cfg_dma_access *d) +{ + do { + u32 ctrl = be32_to_cpu(READ_ONCE(d->control)); + + if ((ctrl & ~FW_CFG_DMA_CTL_ERROR) == 0) + return; + + usleep_range(50, 100); + } while (true); + + /* do not reorder the read to d->control */ + rmb(); +} + +static ssize_t fw_cfg_dma_transfer(void *address, u32 length, u32 control) +{ + phys_addr_t dma; + struct fw_cfg_dma_access *d = NULL; + ssize_t ret = length; + + d = kmalloc(sizeof(*d), GFP_KERNEL); + if (!d) { + ret = -ENOMEM; + goto end; + } + + /* fw_cfg device does not need IOMMU protection, so use physical addresses */ + *d = (struct fw_cfg_dma_access) { + .address = cpu_to_be64(address ? virt_to_phys(address) : 0), + .length = cpu_to_be32(length), + .control = cpu_to_be32(control) + }; + + dma = virt_to_phys(d); + + iowrite32be((u64)dma >> 32, fw_cfg_reg_dma); + /* force memory to sync before notifying device via MMIO */ + wmb(); + iowrite32be(dma, fw_cfg_reg_dma + 4); + + fw_cfg_wait_for_control(d); + + if (be32_to_cpu(READ_ONCE(d->control)) & FW_CFG_DMA_CTL_ERROR) { + ret = -EIO; + } + +end: + kfree(d); + + return ret; +} + /* read chunk of given fw_cfg blob (caller responsible for sanity-check) */ static inline void fw_cfg_read_blob(u16 key, void *buf, loff_t pos, size_t count) @@ -87,6 +152,47 @@ static inline void fw_cfg_read_blob(u16 key, acpi_release_global_lock(glk); } +#ifdef CONFIG_CRASH_CORE +/* write chunk of given fw_cfg blob (caller responsible for sanity-check) */ +static ssize_t fw_cfg_write_blob(u16 key, +void *buf, loff_t pos, size_t count) +{ + u32 glk = -1U; + acpi_status status; + ssize_t ret = count; + + /* If we have ACPI, ensure mutual exclusion against any potential +* device access by the firmware, e.g. via AML methods: +*/ + status = acpi_acquire_global_lock(ACPI_WAIT_FOREVER, ); + if (ACPI_FAILURE(status) && status != AE_NOT_CONFIGURED) { + /* Should never get here */ + WARN(1, "%s: Failed to lock ACPI!\n", __func__); + return -EINVAL; + } + + mutex_lock(_cfg_dev_lock); + if (pos == 0) { + ret = fw_cfg_dma_transfer(buf, count, key << 16 + | FW_CFG_DMA_CTL_SELECT + | FW_CFG_DMA_CTL_WRITE); + } else { + iowrite16(fw_cfg_sel_endianness(key), fw_cfg_reg_ctrl); + ret = fw_cfg_dma_transfer(NULL, pos, FW_CFG_DMA_CTL_SKIP); + if (ret < 0) + goto end; + ret = fw_cfg_dma_transfer(buf, count, FW_CFG_DMA_CTL_WRITE); + } + +end: + mutex_unlock(_cfg_dev_lock); + + acpi_release_global_lock(glk); + + return ret; +} +#endif /* CONFIG_CRASH_CORE */ + /* clean up fw_cfg device i/o */ static void fw_cfg_io_cleanup(void) { @@ -185,9 +291,6 @@ static int fw_cfg_do_platform_probe(struct platform_device *pdev) return 0; } -/* fw_cfg revision attribute, in /sys/firmware/qemu_fw
[PATCH v14 8/9] fw_cfg: write vmcoreinfo details
If the "etc/vmcoreinfo" fw_cfg file is present and we are not running the kdump kernel, write the addr/size of the vmcoreinfo ELF note. The DMA operation is expected to run synchronously with today qemu, but the specification states that it may become async, so we run "control" field check in a loop for eventual changes. Signed-off-by: Marc-André Lureau --- drivers/firmware/qemu_fw_cfg.c | 144 - 1 file changed, 141 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 37638b95cb45..69939e2529f2 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -34,11 +34,17 @@ #include #include #include +#include +#include +#include MODULE_AUTHOR("Gabriel L. Somlo "); MODULE_DESCRIPTION("QEMU fw_cfg sysfs support"); MODULE_LICENSE("GPL"); +/* fw_cfg revision attribute, in /sys/firmware/qemu_fw_cfg top-level dir. */ +static u32 fw_cfg_rev; + /* fw_cfg device i/o register addresses */ static bool fw_cfg_is_mmio; static phys_addr_t fw_cfg_p_base; @@ -59,6 +65,65 @@ static inline u16 fw_cfg_sel_endianness(u16 key) (u16 __force)cpu_to_le16(key); } +static inline bool fw_cfg_dma_enabled(void) +{ + return (fw_cfg_rev & FW_CFG_VERSION_DMA) && fw_cfg_reg_dma; +} + +/* qemu fw_cfg device is sync today, but spec says it may become async */ +static void fw_cfg_wait_for_control(struct fw_cfg_dma_access *d) +{ + do { + u32 ctrl = be32_to_cpu(READ_ONCE(d->control)); + + if ((ctrl & ~FW_CFG_DMA_CTL_ERROR) == 0) + return; + + usleep_range(50, 100); + } while (true); + + /* do not reorder the read to d->control */ + rmb(); +} + +static ssize_t fw_cfg_dma_transfer(void *address, u32 length, u32 control) +{ + phys_addr_t dma; + struct fw_cfg_dma_access *d = NULL; + ssize_t ret = length; + + d = kmalloc(sizeof(*d), GFP_KERNEL); + if (!d) { + ret = -ENOMEM; + goto end; + } + + /* fw_cfg device does not need IOMMU protection, so use physical addresses */ + *d = (struct fw_cfg_dma_access) { + .address = cpu_to_be64(address ? virt_to_phys(address) : 0), + .length = cpu_to_be32(length), + .control = cpu_to_be32(control) + }; + + dma = virt_to_phys(d); + + iowrite32be((u64)dma >> 32, fw_cfg_reg_dma); + /* force memory to sync before notifying device via MMIO */ + wmb(); + iowrite32be(dma, fw_cfg_reg_dma + 4); + + fw_cfg_wait_for_control(d); + + if (be32_to_cpu(READ_ONCE(d->control)) & FW_CFG_DMA_CTL_ERROR) { + ret = -EIO; + } + +end: + kfree(d); + + return ret; +} + /* read chunk of given fw_cfg blob (caller responsible for sanity-check) */ static inline void fw_cfg_read_blob(u16 key, void *buf, loff_t pos, size_t count) @@ -87,6 +152,47 @@ static inline void fw_cfg_read_blob(u16 key, acpi_release_global_lock(glk); } +#ifdef CONFIG_CRASH_CORE +/* write chunk of given fw_cfg blob (caller responsible for sanity-check) */ +static ssize_t fw_cfg_write_blob(u16 key, +void *buf, loff_t pos, size_t count) +{ + u32 glk = -1U; + acpi_status status; + ssize_t ret = count; + + /* If we have ACPI, ensure mutual exclusion against any potential +* device access by the firmware, e.g. via AML methods: +*/ + status = acpi_acquire_global_lock(ACPI_WAIT_FOREVER, ); + if (ACPI_FAILURE(status) && status != AE_NOT_CONFIGURED) { + /* Should never get here */ + WARN(1, "%s: Failed to lock ACPI!\n", __func__); + return -EINVAL; + } + + mutex_lock(_cfg_dev_lock); + if (pos == 0) { + ret = fw_cfg_dma_transfer(buf, count, key << 16 + | FW_CFG_DMA_CTL_SELECT + | FW_CFG_DMA_CTL_WRITE); + } else { + iowrite16(fw_cfg_sel_endianness(key), fw_cfg_reg_ctrl); + ret = fw_cfg_dma_transfer(NULL, pos, FW_CFG_DMA_CTL_SKIP); + if (ret < 0) + goto end; + ret = fw_cfg_dma_transfer(buf, count, FW_CFG_DMA_CTL_WRITE); + } + +end: + mutex_unlock(_cfg_dev_lock); + + acpi_release_global_lock(glk); + + return ret; +} +#endif /* CONFIG_CRASH_CORE */ + /* clean up fw_cfg device i/o */ static void fw_cfg_io_cleanup(void) { @@ -185,9 +291,6 @@ static int fw_cfg_do_platform_probe(struct platform_device *pdev) return 0; } -/* fw_cfg revision attribute, in /sys/firmware/qemu_fw_cfg top-level dir. */ -static u32 fw_cfg_rev; - static
[PATCH v14 9/9] RFC: fw_cfg: do DMA read operation
Modify fw_cfg_read_blob() to use DMA if the device supports it. Return errors, because the operation may fail. So far, only one call in fw_cfg_register_dir_entries() is using kmalloc'ed buf and is thus clearly eligible to DMA read. Initially, I didn't implement DMA read to speed up boot time, but as a first step before introducing DMA write (since read operations were already presents). Even more, I didn't realize fw-cfg entries were being read by the kernel during boot by default. But actally fw-cfg entries are being populated during module probe. I knew DMA improved a lot bios boot time (the main reason the DMA interface was added afaik). Let see the time it would take to read the whole ACPI tables (128kb allocated) # time cat /sys/firmware/qemu_fw_cfg/by_name/etc/acpi/tables/raw - with DMA: sys 0m0.003s - without DMA (-global fw_cfg.dma_enabled=off): sys 0m7.674s FW_CFG_FILE_DIR (0x19) is the only "file" that is read during kernel boot to populate sysfs qemu_fw_cfg directory, and it is quite small (1-2kb). Since it does not expose itself, in order to measure the time it takes to read such small file, I took a comparable sized file of 2048 bytes and exposed it (-fw_cfg test,file=file with a modified read_raw enabling DMA) # perf stat -r 100 cat /sys/firmware/qemu_fw_cfg/by_name/test/raw >/dev/null - with DMA: 0.636037 task-clock (msec) #0.141 CPUs utilized ( +- 1.19% ) - without DMA: 6.430128 task-clock (msec) #0.622 CPUs utilized ( +- 0.22% ) That's a few msec saved during boot by enabling DMA read (the gain would be more substantial if other & bigger fw-cfg entries are read by others from sysfs, unfortunately, it's not clear if we can always enable DMA there) Signed-off-by: Marc-André Lureau --- drivers/firmware/qemu_fw_cfg.c | 80 ++ 1 file changed, 66 insertions(+), 14 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 69939e2529f2..ba9b907a4399 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -124,12 +124,46 @@ static ssize_t fw_cfg_dma_transfer(void *address, u32 length, u32 control) return ret; } +/* with acpi & dev locks taken */ +static ssize_t fw_cfg_read_blob_dma(u16 key, + void *buf, loff_t pos, size_t count) +{ + ssize_t ret; + + if (pos == 0) { + ret = fw_cfg_dma_transfer(buf, count, key << 16 + | FW_CFG_DMA_CTL_SELECT + | FW_CFG_DMA_CTL_READ); + } else { + iowrite16(fw_cfg_sel_endianness(key), fw_cfg_reg_ctrl); + ret = fw_cfg_dma_transfer(NULL, pos, FW_CFG_DMA_CTL_SKIP); + if (ret < 0) + return ret; + ret = fw_cfg_dma_transfer(buf, count, + FW_CFG_DMA_CTL_READ); + } + + return ret; +} + +/* with acpi & dev locks taken */ +static void fw_cfg_read_blob_io(u16 key, + void *buf, loff_t pos, size_t count) +{ + iowrite16(fw_cfg_sel_endianness(key), fw_cfg_reg_ctrl); + while (pos-- > 0) + ioread8(fw_cfg_reg_data); + ioread8_rep(fw_cfg_reg_data, buf, count); +} + /* read chunk of given fw_cfg blob (caller responsible for sanity-check) */ -static inline void fw_cfg_read_blob(u16 key, - void *buf, loff_t pos, size_t count) +static ssize_t fw_cfg_read_blob(u16 key, + void *buf, loff_t pos, size_t count, + bool dma) { u32 glk = -1U; acpi_status status; + ssize_t ret = count; /* If we have ACPI, ensure mutual exclusion against any potential * device access by the firmware, e.g. via AML methods: @@ -139,17 +173,21 @@ static inline void fw_cfg_read_blob(u16 key, /* Should never get here */ WARN(1, "fw_cfg_read_blob: Failed to lock ACPI!\n"); memset(buf, 0, count); - return; + return -EINVAL; } mutex_lock(_cfg_dev_lock); - iowrite16(fw_cfg_sel_endianness(key), fw_cfg_reg_ctrl); - while (pos-- > 0) - ioread8(fw_cfg_reg_data); - ioread8_rep(fw_cfg_reg_data, buf, count); + if (dma && fw_cfg_dma_enabled()) { + ret = fw_cfg_read_blob_dma(key, buf, pos, count); + } else { + fw_cfg_read_blob_io(key, buf, pos, count); + } + mutex_unlock(_cfg_dev_lock); acpi_release_global_lock(glk); + + return ret; } #ifdef CONFIG_CRASH_CORE @@ -282,8 +320,9 @@ static int fw_cfg_do_platform_probe(struct platform_device *pdev) #endif /* verify fw_cfg device signature */ -
[PATCH v14 7/9] fw_cfg: add DMA register
Add an optional kernel module (or command line) parameter using the following syntax: [qemu_fw_cfg.]ioport=@[::[:]] or [qemu_fw_cfg.]mmio=@[::[:]] and initializes the register address using given or default offset. Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> Reviewed-by: Gabriel Somlo <so...@cmu.edu> --- drivers/firmware/qemu_fw_cfg.c | 53 -- 1 file changed, 41 insertions(+), 12 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index c4c726841ba7..37638b95cb45 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -10,20 +10,21 @@ * and select subsets of aarch64), a Device Tree node (on arm), or using * a kernel module (or command line) parameter with the following syntax: * - * [qemu_fw_cfg.]ioport=@[::] + * [qemu_fw_cfg.]ioport=@[::[:]] * or - * [qemu_fw_cfg.]mmio=@[::] + * [qemu_fw_cfg.]mmio=@[::[:]] * * where: * := size of ioport or mmio range * := physical base address of ioport or mmio range * := (optional) offset of control register * := (optional) offset of data register + * := (optional) offset of dma register * * e.g.: - * qemu_fw_cfg.ioport=2@0x510:0:1 (the default on x86) + * qemu_fw_cfg.ioport=12@0x510:0:1:4 (the default on x86) * or - * qemu_fw_cfg.mmio=0xA@0x902:8:0 (the default on arm) + * qemu_fw_cfg.mmio=16@0x902:8:0:16 (the default on arm) */ #include @@ -45,6 +46,7 @@ static resource_size_t fw_cfg_p_size; static void __iomem *fw_cfg_dev_base; static void __iomem *fw_cfg_reg_ctrl; static void __iomem *fw_cfg_reg_data; +static void __iomem *fw_cfg_reg_dma; /* atomic access to fw_cfg device (potentially slow i/o, so using mutex) */ static DEFINE_MUTEX(fw_cfg_dev_lock); @@ -102,12 +104,14 @@ static void fw_cfg_io_cleanup(void) # if (defined(CONFIG_ARM) || defined(CONFIG_ARM64)) # define FW_CFG_CTRL_OFF 0x08 # define FW_CFG_DATA_OFF 0x00 +# define FW_CFG_DMA_OFF 0x10 # elif (defined(CONFIG_PPC_PMAC) || defined(CONFIG_SPARC32)) /* ppc/mac,sun4m */ # define FW_CFG_CTRL_OFF 0x00 # define FW_CFG_DATA_OFF 0x02 # elif (defined(CONFIG_X86) || defined(CONFIG_SPARC64)) /* x86, sun4u */ # define FW_CFG_CTRL_OFF 0x00 # define FW_CFG_DATA_OFF 0x01 +# define FW_CFG_DMA_OFF 0x04 # else # error "QEMU FW_CFG not available on this architecture!" # endif @@ -117,7 +121,7 @@ static void fw_cfg_io_cleanup(void) static int fw_cfg_do_platform_probe(struct platform_device *pdev) { char sig[FW_CFG_SIG_SIZE]; - struct resource *range, *ctrl, *data; + struct resource *range, *ctrl, *data, *dma; /* acquire i/o range details */ fw_cfg_is_mmio = false; @@ -154,6 +158,7 @@ static int fw_cfg_do_platform_probe(struct platform_device *pdev) /* were custom register offsets provided (e.g. on the command line)? */ ctrl = platform_get_resource_byname(pdev, IORESOURCE_REG, "ctrl"); data = platform_get_resource_byname(pdev, IORESOURCE_REG, "data"); + dma = platform_get_resource_byname(pdev, IORESOURCE_REG, "dma"); if (ctrl && data) { fw_cfg_reg_ctrl = fw_cfg_dev_base + ctrl->start; fw_cfg_reg_data = fw_cfg_dev_base + data->start; @@ -163,6 +168,13 @@ static int fw_cfg_do_platform_probe(struct platform_device *pdev) fw_cfg_reg_data = fw_cfg_dev_base + FW_CFG_DATA_OFF; } + if (dma) + fw_cfg_reg_dma = fw_cfg_dev_base + dma->start; +#ifdef FW_CFG_DMA_OFF + else + fw_cfg_reg_dma = fw_cfg_dev_base + FW_CFG_DMA_OFF; +#endif + /* verify fw_cfg device signature */ fw_cfg_read_blob(FW_CFG_SIGNATURE, sig, 0, FW_CFG_SIG_SIZE); if (memcmp(sig, "QEMU", FW_CFG_SIG_SIZE) != 0) { @@ -617,6 +629,7 @@ static struct platform_device *fw_cfg_cmdline_dev; /* use special scanf/printf modifier for phys_addr_t, resource_size_t */ #define PH_ADDR_SCAN_FMT "@%" __PHYS_ADDR_PREFIX "i%n" \ ":%" __PHYS_ADDR_PREFIX "i" \ +":%" __PHYS_ADDR_PREFIX "i%n" \ ":%" __PHYS_ADDR_PREFIX "i%n" #define PH_ADDR_PR_1_FMT "0x%" __PHYS_ADDR_PREFIX "x@" \ @@ -626,12 +639,15 @@ static struct platform_device *fw_cfg_cmdline_dev; ":%" __PHYS_ADDR_PREFIX "u" \ ":%" __PHYS_ADDR_PREFIX "u" +#define PH_ADDR_PR_4_FMT PH_ADDR_PR_3_FMT \ +":%" __PHYS_ADDR_PREFIX "u" + static int fw_cfg_cmdline_set(const char *arg, const struct kernel_param *kp) { - struct resource res[3] = {}; +
[PATCH v14 5/9] fw_cfg: fix sparse warning reading FW_CFG_ID
Use a restricted type for reading FW_CFG_ID, fixing sparse warning: drivers/firmware/qemu_fw_cfg.c:540:22: warning: cast to restricted __le32 Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> --- drivers/firmware/qemu_fw_cfg.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 8ad19086e5c5..4c4813409447 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -511,6 +511,7 @@ static inline void fw_cfg_kobj_cleanup(struct kobject *kobj) static int fw_cfg_sysfs_probe(struct platform_device *pdev) { int err; + __le32 rev; /* NOTE: If we supported multiple fw_cfg devices, we'd first create * a subdirectory named after e.g. pdev->id, then hang per-device @@ -536,8 +537,8 @@ static int fw_cfg_sysfs_probe(struct platform_device *pdev) goto err_probe; /* get revision number, add matching top-level attribute */ - fw_cfg_read_blob(FW_CFG_ID, _cfg_rev, 0, sizeof(fw_cfg_rev)); - fw_cfg_rev = le32_to_cpu(fw_cfg_rev); + fw_cfg_read_blob(FW_CFG_ID, , 0, sizeof(rev)); + fw_cfg_rev = le32_to_cpu(rev); err = sysfs_create_file(fw_cfg_top_ko, _cfg_rev_attr.attr); if (err) goto err_rev; -- 2.16.1.73.g5832b7e9f2
[PATCH v14 7/9] fw_cfg: add DMA register
Add an optional kernel module (or command line) parameter using the following syntax: [qemu_fw_cfg.]ioport=@[::[:]] or [qemu_fw_cfg.]mmio=@[::[:]] and initializes the register address using given or default offset. Signed-off-by: Marc-André Lureau Reviewed-by: Gabriel Somlo --- drivers/firmware/qemu_fw_cfg.c | 53 -- 1 file changed, 41 insertions(+), 12 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index c4c726841ba7..37638b95cb45 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -10,20 +10,21 @@ * and select subsets of aarch64), a Device Tree node (on arm), or using * a kernel module (or command line) parameter with the following syntax: * - * [qemu_fw_cfg.]ioport=@[::] + * [qemu_fw_cfg.]ioport=@[::[:]] * or - * [qemu_fw_cfg.]mmio=@[::] + * [qemu_fw_cfg.]mmio=@[::[:]] * * where: * := size of ioport or mmio range * := physical base address of ioport or mmio range * := (optional) offset of control register * := (optional) offset of data register + * := (optional) offset of dma register * * e.g.: - * qemu_fw_cfg.ioport=2@0x510:0:1 (the default on x86) + * qemu_fw_cfg.ioport=12@0x510:0:1:4 (the default on x86) * or - * qemu_fw_cfg.mmio=0xA@0x902:8:0 (the default on arm) + * qemu_fw_cfg.mmio=16@0x902:8:0:16 (the default on arm) */ #include @@ -45,6 +46,7 @@ static resource_size_t fw_cfg_p_size; static void __iomem *fw_cfg_dev_base; static void __iomem *fw_cfg_reg_ctrl; static void __iomem *fw_cfg_reg_data; +static void __iomem *fw_cfg_reg_dma; /* atomic access to fw_cfg device (potentially slow i/o, so using mutex) */ static DEFINE_MUTEX(fw_cfg_dev_lock); @@ -102,12 +104,14 @@ static void fw_cfg_io_cleanup(void) # if (defined(CONFIG_ARM) || defined(CONFIG_ARM64)) # define FW_CFG_CTRL_OFF 0x08 # define FW_CFG_DATA_OFF 0x00 +# define FW_CFG_DMA_OFF 0x10 # elif (defined(CONFIG_PPC_PMAC) || defined(CONFIG_SPARC32)) /* ppc/mac,sun4m */ # define FW_CFG_CTRL_OFF 0x00 # define FW_CFG_DATA_OFF 0x02 # elif (defined(CONFIG_X86) || defined(CONFIG_SPARC64)) /* x86, sun4u */ # define FW_CFG_CTRL_OFF 0x00 # define FW_CFG_DATA_OFF 0x01 +# define FW_CFG_DMA_OFF 0x04 # else # error "QEMU FW_CFG not available on this architecture!" # endif @@ -117,7 +121,7 @@ static void fw_cfg_io_cleanup(void) static int fw_cfg_do_platform_probe(struct platform_device *pdev) { char sig[FW_CFG_SIG_SIZE]; - struct resource *range, *ctrl, *data; + struct resource *range, *ctrl, *data, *dma; /* acquire i/o range details */ fw_cfg_is_mmio = false; @@ -154,6 +158,7 @@ static int fw_cfg_do_platform_probe(struct platform_device *pdev) /* were custom register offsets provided (e.g. on the command line)? */ ctrl = platform_get_resource_byname(pdev, IORESOURCE_REG, "ctrl"); data = platform_get_resource_byname(pdev, IORESOURCE_REG, "data"); + dma = platform_get_resource_byname(pdev, IORESOURCE_REG, "dma"); if (ctrl && data) { fw_cfg_reg_ctrl = fw_cfg_dev_base + ctrl->start; fw_cfg_reg_data = fw_cfg_dev_base + data->start; @@ -163,6 +168,13 @@ static int fw_cfg_do_platform_probe(struct platform_device *pdev) fw_cfg_reg_data = fw_cfg_dev_base + FW_CFG_DATA_OFF; } + if (dma) + fw_cfg_reg_dma = fw_cfg_dev_base + dma->start; +#ifdef FW_CFG_DMA_OFF + else + fw_cfg_reg_dma = fw_cfg_dev_base + FW_CFG_DMA_OFF; +#endif + /* verify fw_cfg device signature */ fw_cfg_read_blob(FW_CFG_SIGNATURE, sig, 0, FW_CFG_SIG_SIZE); if (memcmp(sig, "QEMU", FW_CFG_SIG_SIZE) != 0) { @@ -617,6 +629,7 @@ static struct platform_device *fw_cfg_cmdline_dev; /* use special scanf/printf modifier for phys_addr_t, resource_size_t */ #define PH_ADDR_SCAN_FMT "@%" __PHYS_ADDR_PREFIX "i%n" \ ":%" __PHYS_ADDR_PREFIX "i" \ +":%" __PHYS_ADDR_PREFIX "i%n" \ ":%" __PHYS_ADDR_PREFIX "i%n" #define PH_ADDR_PR_1_FMT "0x%" __PHYS_ADDR_PREFIX "x@" \ @@ -626,12 +639,15 @@ static struct platform_device *fw_cfg_cmdline_dev; ":%" __PHYS_ADDR_PREFIX "u" \ ":%" __PHYS_ADDR_PREFIX "u" +#define PH_ADDR_PR_4_FMT PH_ADDR_PR_3_FMT \ +":%" __PHYS_ADDR_PREFIX "u" + static int fw_cfg_cmdline_set(const char *arg, const struct kernel_param *kp) { - struct resource res[3] = {}; + struct resource res[4] = {}; char *str;
[PATCH v14 5/9] fw_cfg: fix sparse warning reading FW_CFG_ID
Use a restricted type for reading FW_CFG_ID, fixing sparse warning: drivers/firmware/qemu_fw_cfg.c:540:22: warning: cast to restricted __le32 Signed-off-by: Marc-André Lureau --- drivers/firmware/qemu_fw_cfg.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 8ad19086e5c5..4c4813409447 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -511,6 +511,7 @@ static inline void fw_cfg_kobj_cleanup(struct kobject *kobj) static int fw_cfg_sysfs_probe(struct platform_device *pdev) { int err; + __le32 rev; /* NOTE: If we supported multiple fw_cfg devices, we'd first create * a subdirectory named after e.g. pdev->id, then hang per-device @@ -536,8 +537,8 @@ static int fw_cfg_sysfs_probe(struct platform_device *pdev) goto err_probe; /* get revision number, add matching top-level attribute */ - fw_cfg_read_blob(FW_CFG_ID, _cfg_rev, 0, sizeof(fw_cfg_rev)); - fw_cfg_rev = le32_to_cpu(fw_cfg_rev); + fw_cfg_read_blob(FW_CFG_ID, , 0, sizeof(rev)); + fw_cfg_rev = le32_to_cpu(rev); err = sysfs_create_file(fw_cfg_top_ko, _cfg_rev_attr.attr); if (err) goto err_rev; -- 2.16.1.73.g5832b7e9f2
[PATCH v14 0/9] fw_cfg: add DMA operations & etc/vmcoreinfo support
Hi, This series adds DMA operations support to the qemu fw_cfg kernel module and populates "etc/vmcoreinfo" with vmcoreinfo location details (entry added since qemu 2.11 with -device vmcoreinfo). v14: - add "fw_cfg: add a public uapi header" - fix sparse warnings & don't introduce new warnings - add memory barriers to force IO ordering - split fw_cfg_read_blob() in fw_cfg_read_blob_io() and fw_cfg_read_blob_dma() - add error handling to fw_cfg_read_blob() callers - minor stylistic changes v13: - reorder patch series, introduce DMA write before DMA read - do some measurements of DMA read speed-ups v12: - fix virt_to_phys(NULL) panic with CONFIG_DEBUG_VIRTUAL=y - do not use DMA read, except for kmalloc() memory we allocated ourself (only fw_cfg_register_dir_entries() so far) v11: - add #include in last patch, fixing kbuild .config test Marc-André Lureau (9): crash: export paddr_vmcoreinfo_note() fw_cfg: add a public uapi header fw_cfg: fix sparse warnings in fw_cfg_sel_endianness() fw_cfg: fix sparse warnings with fw_cfg_file fw_cfg: fix sparse warning reading FW_CFG_ID fw_cfg: fix sparse warnings around FW_CFG_FILE_DIR read fw_cfg: add DMA register fw_cfg: write vmcoreinfo details RFC: fw_cfg: do DMA read operation MAINTAINERS| 1 + drivers/firmware/qemu_fw_cfg.c | 333 + include/uapi/linux/fw_cfg.h| 102 + kernel/crash_core.c| 1 + 4 files changed, 374 insertions(+), 63 deletions(-) create mode 100644 include/uapi/linux/fw_cfg.h -- 2.16.1.73.g5832b7e9f2
[PATCH v14 4/9] fw_cfg: fix sparse warnings with fw_cfg_file
Modify fw_cfg_sysfs_entry to store entry values, instead of reusing the restricted types. Fixes warnings such as: $ make C=1 CF=-D__CHECK_ENDIAN__ drivers/firmware/qemu_fw_cfg.o drivers/firmware/qemu_fw_cfg.c:491:29: warning: incorrect type in assignment (different base types) drivers/firmware/qemu_fw_cfg.c:491:29:expected restricted __be32 [usertype] size drivers/firmware/qemu_fw_cfg.c:491:29:got unsigned int drivers/firmware/qemu_fw_cfg.c:492:31: warning: incorrect type in assignment (different base types) drivers/firmware/qemu_fw_cfg.c:492:31:expected restricted __be16 [usertype] select drivers/firmware/qemu_fw_cfg.c:492:31:got int Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> --- drivers/firmware/qemu_fw_cfg.c | 28 +++- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 85e693287d87..8ad19086e5c5 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -192,7 +192,9 @@ static const struct { /* fw_cfg_sysfs_entry type */ struct fw_cfg_sysfs_entry { struct kobject kobj; - struct fw_cfg_file f; + u32 size; + u16 select; + char name[FW_CFG_MAX_FILE_PATH]; struct list_head list; }; @@ -256,17 +258,17 @@ struct fw_cfg_sysfs_attribute fw_cfg_sysfs_attr_##_attr = { \ static ssize_t fw_cfg_sysfs_show_size(struct fw_cfg_sysfs_entry *e, char *buf) { - return sprintf(buf, "%u\n", e->f.size); + return sprintf(buf, "%u\n", e->size); } static ssize_t fw_cfg_sysfs_show_key(struct fw_cfg_sysfs_entry *e, char *buf) { - return sprintf(buf, "%u\n", e->f.select); + return sprintf(buf, "%u\n", e->select); } static ssize_t fw_cfg_sysfs_show_name(struct fw_cfg_sysfs_entry *e, char *buf) { - return sprintf(buf, "%s\n", e->f.name); + return sprintf(buf, "%s\n", e->name); } static FW_CFG_SYSFS_ATTR(size); @@ -317,13 +319,13 @@ static ssize_t fw_cfg_sysfs_read_raw(struct file *filp, struct kobject *kobj, { struct fw_cfg_sysfs_entry *entry = to_entry(kobj); - if (pos > entry->f.size) + if (pos > entry->size) return -EINVAL; - if (count > entry->f.size - pos) - count = entry->f.size - pos; + if (count > entry->size - pos) + count = entry->size - pos; - fw_cfg_read_blob(entry->f.select, buf, pos, count); + fw_cfg_read_blob(entry->select, buf, pos, count); return count; } @@ -442,11 +444,13 @@ static int fw_cfg_register_file(const struct fw_cfg_file *f) return -ENOMEM; /* set file entry information */ - memcpy(>f, f, sizeof(struct fw_cfg_file)); + entry->size = be32_to_cpu(f->size); + entry->select = be16_to_cpu(f->select); + memcpy(entry->name, f->name, FW_CFG_MAX_FILE_PATH); /* register entry under "/sys/firmware/qemu_fw_cfg/by_key/" */ err = kobject_init_and_add(>kobj, _cfg_sysfs_entry_ktype, - fw_cfg_sel_ko, "%d", entry->f.select); + fw_cfg_sel_ko, "%d", entry->select); if (err) goto err_register; @@ -456,7 +460,7 @@ static int fw_cfg_register_file(const struct fw_cfg_file *f) goto err_add_raw; /* try adding "/sys/firmware/qemu_fw_cfg/by_name/" symlink */ - fw_cfg_build_symlink(fw_cfg_fname_kset, >kobj, entry->f.name); + fw_cfg_build_symlink(fw_cfg_fname_kset, >kobj, entry->name); /* success, add entry to global cache */ fw_cfg_sysfs_cache_enlist(entry); @@ -488,8 +492,6 @@ static int fw_cfg_register_dir_entries(void) fw_cfg_read_blob(FW_CFG_FILE_DIR, dir, sizeof(count), dir_size); for (i = 0; i < count; i++) { - dir[i].size = be32_to_cpu(dir[i].size); - dir[i].select = be16_to_cpu(dir[i].select); ret = fw_cfg_register_file([i]); if (ret) break; -- 2.16.1.73.g5832b7e9f2
[PATCH v14 0/9] fw_cfg: add DMA operations & etc/vmcoreinfo support
Hi, This series adds DMA operations support to the qemu fw_cfg kernel module and populates "etc/vmcoreinfo" with vmcoreinfo location details (entry added since qemu 2.11 with -device vmcoreinfo). v14: - add "fw_cfg: add a public uapi header" - fix sparse warnings & don't introduce new warnings - add memory barriers to force IO ordering - split fw_cfg_read_blob() in fw_cfg_read_blob_io() and fw_cfg_read_blob_dma() - add error handling to fw_cfg_read_blob() callers - minor stylistic changes v13: - reorder patch series, introduce DMA write before DMA read - do some measurements of DMA read speed-ups v12: - fix virt_to_phys(NULL) panic with CONFIG_DEBUG_VIRTUAL=y - do not use DMA read, except for kmalloc() memory we allocated ourself (only fw_cfg_register_dir_entries() so far) v11: - add #include in last patch, fixing kbuild .config test Marc-André Lureau (9): crash: export paddr_vmcoreinfo_note() fw_cfg: add a public uapi header fw_cfg: fix sparse warnings in fw_cfg_sel_endianness() fw_cfg: fix sparse warnings with fw_cfg_file fw_cfg: fix sparse warning reading FW_CFG_ID fw_cfg: fix sparse warnings around FW_CFG_FILE_DIR read fw_cfg: add DMA register fw_cfg: write vmcoreinfo details RFC: fw_cfg: do DMA read operation MAINTAINERS| 1 + drivers/firmware/qemu_fw_cfg.c | 333 + include/uapi/linux/fw_cfg.h| 102 + kernel/crash_core.c| 1 + 4 files changed, 374 insertions(+), 63 deletions(-) create mode 100644 include/uapi/linux/fw_cfg.h -- 2.16.1.73.g5832b7e9f2
[PATCH v14 4/9] fw_cfg: fix sparse warnings with fw_cfg_file
Modify fw_cfg_sysfs_entry to store entry values, instead of reusing the restricted types. Fixes warnings such as: $ make C=1 CF=-D__CHECK_ENDIAN__ drivers/firmware/qemu_fw_cfg.o drivers/firmware/qemu_fw_cfg.c:491:29: warning: incorrect type in assignment (different base types) drivers/firmware/qemu_fw_cfg.c:491:29:expected restricted __be32 [usertype] size drivers/firmware/qemu_fw_cfg.c:491:29:got unsigned int drivers/firmware/qemu_fw_cfg.c:492:31: warning: incorrect type in assignment (different base types) drivers/firmware/qemu_fw_cfg.c:492:31:expected restricted __be16 [usertype] select drivers/firmware/qemu_fw_cfg.c:492:31:got int Signed-off-by: Marc-André Lureau --- drivers/firmware/qemu_fw_cfg.c | 28 +++- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 85e693287d87..8ad19086e5c5 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -192,7 +192,9 @@ static const struct { /* fw_cfg_sysfs_entry type */ struct fw_cfg_sysfs_entry { struct kobject kobj; - struct fw_cfg_file f; + u32 size; + u16 select; + char name[FW_CFG_MAX_FILE_PATH]; struct list_head list; }; @@ -256,17 +258,17 @@ struct fw_cfg_sysfs_attribute fw_cfg_sysfs_attr_##_attr = { \ static ssize_t fw_cfg_sysfs_show_size(struct fw_cfg_sysfs_entry *e, char *buf) { - return sprintf(buf, "%u\n", e->f.size); + return sprintf(buf, "%u\n", e->size); } static ssize_t fw_cfg_sysfs_show_key(struct fw_cfg_sysfs_entry *e, char *buf) { - return sprintf(buf, "%u\n", e->f.select); + return sprintf(buf, "%u\n", e->select); } static ssize_t fw_cfg_sysfs_show_name(struct fw_cfg_sysfs_entry *e, char *buf) { - return sprintf(buf, "%s\n", e->f.name); + return sprintf(buf, "%s\n", e->name); } static FW_CFG_SYSFS_ATTR(size); @@ -317,13 +319,13 @@ static ssize_t fw_cfg_sysfs_read_raw(struct file *filp, struct kobject *kobj, { struct fw_cfg_sysfs_entry *entry = to_entry(kobj); - if (pos > entry->f.size) + if (pos > entry->size) return -EINVAL; - if (count > entry->f.size - pos) - count = entry->f.size - pos; + if (count > entry->size - pos) + count = entry->size - pos; - fw_cfg_read_blob(entry->f.select, buf, pos, count); + fw_cfg_read_blob(entry->select, buf, pos, count); return count; } @@ -442,11 +444,13 @@ static int fw_cfg_register_file(const struct fw_cfg_file *f) return -ENOMEM; /* set file entry information */ - memcpy(>f, f, sizeof(struct fw_cfg_file)); + entry->size = be32_to_cpu(f->size); + entry->select = be16_to_cpu(f->select); + memcpy(entry->name, f->name, FW_CFG_MAX_FILE_PATH); /* register entry under "/sys/firmware/qemu_fw_cfg/by_key/" */ err = kobject_init_and_add(>kobj, _cfg_sysfs_entry_ktype, - fw_cfg_sel_ko, "%d", entry->f.select); + fw_cfg_sel_ko, "%d", entry->select); if (err) goto err_register; @@ -456,7 +460,7 @@ static int fw_cfg_register_file(const struct fw_cfg_file *f) goto err_add_raw; /* try adding "/sys/firmware/qemu_fw_cfg/by_name/" symlink */ - fw_cfg_build_symlink(fw_cfg_fname_kset, >kobj, entry->f.name); + fw_cfg_build_symlink(fw_cfg_fname_kset, >kobj, entry->name); /* success, add entry to global cache */ fw_cfg_sysfs_cache_enlist(entry); @@ -488,8 +492,6 @@ static int fw_cfg_register_dir_entries(void) fw_cfg_read_blob(FW_CFG_FILE_DIR, dir, sizeof(count), dir_size); for (i = 0; i < count; i++) { - dir[i].size = be32_to_cpu(dir[i].size); - dir[i].select = be16_to_cpu(dir[i].select); ret = fw_cfg_register_file([i]); if (ret) break; -- 2.16.1.73.g5832b7e9f2
[PATCH v14 2/9] fw_cfg: add a public uapi header
Create a common header file for well-known values and structures to be shared by the Linux kernel with qemu or other projects. Suggested-by: Michael S. Tsirkin <m...@redhat.com> Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> --- The related qemu patch making use of it, to be submitted: https://github.com/elmarco/qemu/commit/4884fc9e9c4c4467a371e5a40f3181239e1b70f5 --- MAINTAINERS| 1 + drivers/firmware/qemu_fw_cfg.c | 22 + include/uapi/linux/fw_cfg.h| 102 + 3 files changed, 105 insertions(+), 20 deletions(-) create mode 100644 include/uapi/linux/fw_cfg.h diff --git a/MAINTAINERS b/MAINTAINERS index 3bdc260e36b7..a66b65f62811 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11352,6 +11352,7 @@ M: "Michael S. Tsirkin" <m...@redhat.com> L: qemu-de...@nongnu.org S: Maintained F: drivers/firmware/qemu_fw_cfg.c +F: include/uapi/linux/fw_cfg.h QIB DRIVER M: Dennis Dalessandro <dennis.dalessan...@intel.com> diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index a41b572eeeb1..90f467232777 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -32,30 +32,12 @@ #include #include #include +#include MODULE_AUTHOR("Gabriel L. Somlo <so...@cmu.edu>"); MODULE_DESCRIPTION("QEMU fw_cfg sysfs support"); MODULE_LICENSE("GPL"); -/* selector key values for "well-known" fw_cfg entries */ -#define FW_CFG_SIGNATURE 0x00 -#define FW_CFG_ID 0x01 -#define FW_CFG_FILE_DIR 0x19 - -/* size in bytes of fw_cfg signature */ -#define FW_CFG_SIG_SIZE 4 - -/* fw_cfg "file name" is up to 56 characters (including terminating nul) */ -#define FW_CFG_MAX_FILE_PATH 56 - -/* fw_cfg file directory entry type */ -struct fw_cfg_file { - u32 size; - u16 select; - u16 reserved; - char name[FW_CFG_MAX_FILE_PATH]; -}; - /* fw_cfg device i/o register addresses */ static bool fw_cfg_is_mmio; static phys_addr_t fw_cfg_p_base; @@ -597,7 +579,7 @@ MODULE_DEVICE_TABLE(of, fw_cfg_sysfs_mmio_match); #ifdef CONFIG_ACPI static const struct acpi_device_id fw_cfg_sysfs_acpi_match[] = { - { "QEMU0002", }, + { FW_CFG_ACPI_DEVICE_ID, }, {}, }; MODULE_DEVICE_TABLE(acpi, fw_cfg_sysfs_acpi_match); diff --git a/include/uapi/linux/fw_cfg.h b/include/uapi/linux/fw_cfg.h new file mode 100644 index ..5b8136ce46ee --- /dev/null +++ b/include/uapi/linux/fw_cfg.h @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_FW_CFG_H +#define _LINUX_FW_CFG_H + +#include + +#define FW_CFG_ACPI_DEVICE_ID "QEMU0002" + +/* selector key values for "well-known" fw_cfg entries */ +#define FW_CFG_SIGNATURE 0x00 +#define FW_CFG_ID 0x01 +#define FW_CFG_UUID0x02 +#define FW_CFG_RAM_SIZE0x03 +#define FW_CFG_NOGRAPHIC 0x04 +#define FW_CFG_NB_CPUS 0x05 +#define FW_CFG_MACHINE_ID 0x06 +#define FW_CFG_KERNEL_ADDR 0x07 +#define FW_CFG_KERNEL_SIZE 0x08 +#define FW_CFG_KERNEL_CMDLINE 0x09 +#define FW_CFG_INITRD_ADDR 0x0a +#define FW_CFG_INITRD_SIZE 0x0b +#define FW_CFG_BOOT_DEVICE 0x0c +#define FW_CFG_NUMA0x0d +#define FW_CFG_BOOT_MENU 0x0e +#define FW_CFG_MAX_CPUS0x0f +#define FW_CFG_KERNEL_ENTRY0x10 +#define FW_CFG_KERNEL_DATA 0x11 +#define FW_CFG_INITRD_DATA 0x12 +#define FW_CFG_CMDLINE_ADDR0x13 +#define FW_CFG_CMDLINE_SIZE0x14 +#define FW_CFG_CMDLINE_DATA0x15 +#define FW_CFG_SETUP_ADDR 0x16 +#define FW_CFG_SETUP_SIZE 0x17 +#define FW_CFG_SETUP_DATA 0x18 +#define FW_CFG_FILE_DIR0x19 + +#define FW_CFG_FILE_FIRST 0x20 +#define FW_CFG_FILE_SLOTS_MIN 0x10 + +#define FW_CFG_WRITE_CHANNEL 0x4000 +#define FW_CFG_ARCH_LOCAL 0x8000 +#define FW_CFG_ENTRY_MASK (~(FW_CFG_WRITE_CHANNEL | FW_CFG_ARCH_LOCAL)) + +#define FW_CFG_INVALID 0x + +/* width in bytes of fw_cfg control register */ +#define FW_CFG_CTL_SIZE0x02 + +/* fw_cfg "file name" is up to 56 characters (including terminating nul) */ +#define FW_CFG_MAX_FILE_PATH 56 + +/* size in bytes of fw_cfg signature */ +#define FW_CFG_SIG_SIZE 4 + +/* FW_CFG_ID bits */ +#define FW_CFG_VERSION 0x01 +#define FW_CFG_VERSION_DMA 0x02 + +/* fw_cfg file directory entry type */ +struct fw_cfg_file { + __be32 size;/* file size */ + __be16 select; /* write this to 0x510 to read it */ + __u16 reserved; + char name[FW_CFG_MAX_FILE_PATH]; +}; + +struct fw_cfg_files { + __be32 count; /* number of entries */ + struct fw_cfg_file f[]; +}; + +/* FW_CFG_DMA_CONTROL bits */ +#define FW_CFG_DMA_CTL_ERROR 0x01 +#define FW_CFG_DMA_CTL_READ
[PATCH v14 2/9] fw_cfg: add a public uapi header
Create a common header file for well-known values and structures to be shared by the Linux kernel with qemu or other projects. Suggested-by: Michael S. Tsirkin Signed-off-by: Marc-André Lureau --- The related qemu patch making use of it, to be submitted: https://github.com/elmarco/qemu/commit/4884fc9e9c4c4467a371e5a40f3181239e1b70f5 --- MAINTAINERS| 1 + drivers/firmware/qemu_fw_cfg.c | 22 + include/uapi/linux/fw_cfg.h| 102 + 3 files changed, 105 insertions(+), 20 deletions(-) create mode 100644 include/uapi/linux/fw_cfg.h diff --git a/MAINTAINERS b/MAINTAINERS index 3bdc260e36b7..a66b65f62811 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11352,6 +11352,7 @@ M: "Michael S. Tsirkin" L: qemu-de...@nongnu.org S: Maintained F: drivers/firmware/qemu_fw_cfg.c +F: include/uapi/linux/fw_cfg.h QIB DRIVER M: Dennis Dalessandro diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index a41b572eeeb1..90f467232777 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -32,30 +32,12 @@ #include #include #include +#include MODULE_AUTHOR("Gabriel L. Somlo "); MODULE_DESCRIPTION("QEMU fw_cfg sysfs support"); MODULE_LICENSE("GPL"); -/* selector key values for "well-known" fw_cfg entries */ -#define FW_CFG_SIGNATURE 0x00 -#define FW_CFG_ID 0x01 -#define FW_CFG_FILE_DIR 0x19 - -/* size in bytes of fw_cfg signature */ -#define FW_CFG_SIG_SIZE 4 - -/* fw_cfg "file name" is up to 56 characters (including terminating nul) */ -#define FW_CFG_MAX_FILE_PATH 56 - -/* fw_cfg file directory entry type */ -struct fw_cfg_file { - u32 size; - u16 select; - u16 reserved; - char name[FW_CFG_MAX_FILE_PATH]; -}; - /* fw_cfg device i/o register addresses */ static bool fw_cfg_is_mmio; static phys_addr_t fw_cfg_p_base; @@ -597,7 +579,7 @@ MODULE_DEVICE_TABLE(of, fw_cfg_sysfs_mmio_match); #ifdef CONFIG_ACPI static const struct acpi_device_id fw_cfg_sysfs_acpi_match[] = { - { "QEMU0002", }, + { FW_CFG_ACPI_DEVICE_ID, }, {}, }; MODULE_DEVICE_TABLE(acpi, fw_cfg_sysfs_acpi_match); diff --git a/include/uapi/linux/fw_cfg.h b/include/uapi/linux/fw_cfg.h new file mode 100644 index ..5b8136ce46ee --- /dev/null +++ b/include/uapi/linux/fw_cfg.h @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_FW_CFG_H +#define _LINUX_FW_CFG_H + +#include + +#define FW_CFG_ACPI_DEVICE_ID "QEMU0002" + +/* selector key values for "well-known" fw_cfg entries */ +#define FW_CFG_SIGNATURE 0x00 +#define FW_CFG_ID 0x01 +#define FW_CFG_UUID0x02 +#define FW_CFG_RAM_SIZE0x03 +#define FW_CFG_NOGRAPHIC 0x04 +#define FW_CFG_NB_CPUS 0x05 +#define FW_CFG_MACHINE_ID 0x06 +#define FW_CFG_KERNEL_ADDR 0x07 +#define FW_CFG_KERNEL_SIZE 0x08 +#define FW_CFG_KERNEL_CMDLINE 0x09 +#define FW_CFG_INITRD_ADDR 0x0a +#define FW_CFG_INITRD_SIZE 0x0b +#define FW_CFG_BOOT_DEVICE 0x0c +#define FW_CFG_NUMA0x0d +#define FW_CFG_BOOT_MENU 0x0e +#define FW_CFG_MAX_CPUS0x0f +#define FW_CFG_KERNEL_ENTRY0x10 +#define FW_CFG_KERNEL_DATA 0x11 +#define FW_CFG_INITRD_DATA 0x12 +#define FW_CFG_CMDLINE_ADDR0x13 +#define FW_CFG_CMDLINE_SIZE0x14 +#define FW_CFG_CMDLINE_DATA0x15 +#define FW_CFG_SETUP_ADDR 0x16 +#define FW_CFG_SETUP_SIZE 0x17 +#define FW_CFG_SETUP_DATA 0x18 +#define FW_CFG_FILE_DIR0x19 + +#define FW_CFG_FILE_FIRST 0x20 +#define FW_CFG_FILE_SLOTS_MIN 0x10 + +#define FW_CFG_WRITE_CHANNEL 0x4000 +#define FW_CFG_ARCH_LOCAL 0x8000 +#define FW_CFG_ENTRY_MASK (~(FW_CFG_WRITE_CHANNEL | FW_CFG_ARCH_LOCAL)) + +#define FW_CFG_INVALID 0x + +/* width in bytes of fw_cfg control register */ +#define FW_CFG_CTL_SIZE0x02 + +/* fw_cfg "file name" is up to 56 characters (including terminating nul) */ +#define FW_CFG_MAX_FILE_PATH 56 + +/* size in bytes of fw_cfg signature */ +#define FW_CFG_SIG_SIZE 4 + +/* FW_CFG_ID bits */ +#define FW_CFG_VERSION 0x01 +#define FW_CFG_VERSION_DMA 0x02 + +/* fw_cfg file directory entry type */ +struct fw_cfg_file { + __be32 size;/* file size */ + __be16 select; /* write this to 0x510 to read it */ + __u16 reserved; + char name[FW_CFG_MAX_FILE_PATH]; +}; + +struct fw_cfg_files { + __be32 count; /* number of entries */ + struct fw_cfg_file f[]; +}; + +/* FW_CFG_DMA_CONTROL bits */ +#define FW_CFG_DMA_CTL_ERROR 0x01 +#define FW_CFG_DMA_CTL_READ0x02 +#define FW_CFG_DMA_CTL_SKIP0x04 +#define FW_CFG_DMA_CTL_SELECT 0x08 +#define FW_CFG_DMA_CTL_WRITE 0x10 + +#defin
[PATCH v13 0/4] fw_cfg: add DMA operations & etc/vmcoreinfo support
Hi, This series adds DMA operations support to the qemu fw_cfg kernel module and populates "etc/vmcoreinfo" with vmcoreinfo location details (entry added since qemu 2.11 with -device vmcoreinfo). v13: - reorder patch series, introduce DMA write before DMA read - do some measurements of DMA read speed-ups v12: - fix virt_to_phys(NULL) panic with CONFIG_DEBUG_VIRTUAL=y - do not use DMA read, except for kmalloc() memory we allocated ourself (only fw_cfg_register_dir_entries() so far) v11: - add #include in last patch, fixing kbuild .config test Marc-André Lureau (4): crash: export paddr_vmcoreinfo_note() fw_cfg: add DMA register fw_cfg: write vmcoreinfo details RFC: fw_cfg: do DMA read operation drivers/firmware/qemu_fw_cfg.c | 257 - kernel/crash_core.c| 1 + 2 files changed, 230 insertions(+), 28 deletions(-) -- 2.16.1.73.g5832b7e9f2
[PATCH v13 0/4] fw_cfg: add DMA operations & etc/vmcoreinfo support
Hi, This series adds DMA operations support to the qemu fw_cfg kernel module and populates "etc/vmcoreinfo" with vmcoreinfo location details (entry added since qemu 2.11 with -device vmcoreinfo). v13: - reorder patch series, introduce DMA write before DMA read - do some measurements of DMA read speed-ups v12: - fix virt_to_phys(NULL) panic with CONFIG_DEBUG_VIRTUAL=y - do not use DMA read, except for kmalloc() memory we allocated ourself (only fw_cfg_register_dir_entries() so far) v11: - add #include in last patch, fixing kbuild .config test Marc-André Lureau (4): crash: export paddr_vmcoreinfo_note() fw_cfg: add DMA register fw_cfg: write vmcoreinfo details RFC: fw_cfg: do DMA read operation drivers/firmware/qemu_fw_cfg.c | 257 - kernel/crash_core.c| 1 + 2 files changed, 230 insertions(+), 28 deletions(-) -- 2.16.1.73.g5832b7e9f2
[PATCH v13 3/4] fw_cfg: write vmcoreinfo details
If the "etc/vmcoreinfo" fw_cfg file is present and we are not running the kdump kernel, write the addr/size of the vmcoreinfo ELF note. The DMA operation is expected to run synchronously with today qemu, but the specification states that it may become async, so we run "control" field check in a loop for eventual changes. Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> --- drivers/firmware/qemu_fw_cfg.c | 157 - 1 file changed, 154 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 740df0df2260..fd576ba7b337 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -33,6 +33,9 @@ #include #include #include +#include +#include +#include MODULE_AUTHOR("Gabriel L. Somlo <so...@cmu.edu>"); MODULE_DESCRIPTION("QEMU fw_cfg sysfs support"); @@ -43,12 +46,24 @@ MODULE_LICENSE("GPL"); #define FW_CFG_ID 0x01 #define FW_CFG_FILE_DIR 0x19 +#define FW_CFG_VERSION_DMA 0x02 +#define FW_CFG_DMA_CTL_ERROR 0x01 +#define FW_CFG_DMA_CTL_READ0x02 +#define FW_CFG_DMA_CTL_SKIP0x04 +#define FW_CFG_DMA_CTL_SELECT 0x08 +#define FW_CFG_DMA_CTL_WRITE 0x10 + /* size in bytes of fw_cfg signature */ #define FW_CFG_SIG_SIZE 4 /* fw_cfg "file name" is up to 56 characters (including terminating nul) */ #define FW_CFG_MAX_FILE_PATH 56 +#define VMCOREINFO_FORMAT_ELF 0x1 + +/* fw_cfg revision attribute, in /sys/firmware/qemu_fw_cfg top-level dir. */ +static u32 fw_cfg_rev; + /* fw_cfg file directory entry type */ struct fw_cfg_file { u32 size; @@ -57,6 +72,12 @@ struct fw_cfg_file { char name[FW_CFG_MAX_FILE_PATH]; }; +struct fw_cfg_dma { + u32 control; + u32 length; + u64 address; +} __packed; + /* fw_cfg device i/o register addresses */ static bool fw_cfg_is_mmio; static phys_addr_t fw_cfg_p_base; @@ -75,6 +96,59 @@ static inline u16 fw_cfg_sel_endianness(u16 key) return fw_cfg_is_mmio ? cpu_to_be16(key) : cpu_to_le16(key); } +static inline bool fw_cfg_dma_enabled(void) +{ + return fw_cfg_rev & FW_CFG_VERSION_DMA && fw_cfg_reg_dma; +} + +/* qemu fw_cfg device is sync today, but spec says it may become async */ +static void fw_cfg_wait_for_control(struct fw_cfg_dma *d) +{ + do { + u32 ctrl = be32_to_cpu(READ_ONCE(d->control)); + + if ((ctrl & ~FW_CFG_DMA_CTL_ERROR) == 0) + return; + + usleep_range(50, 100); + } while (true); +} + +static ssize_t fw_cfg_dma_transfer(void *address, u32 length, u32 control) +{ + phys_addr_t dma; + struct fw_cfg_dma *d = NULL; + ssize_t ret = length; + + d = kmalloc(sizeof(*d), GFP_KERNEL); + if (!d) { + ret = -ENOMEM; + goto end; + } + + *d = (struct fw_cfg_dma) { + .address = address ? cpu_to_be64(virt_to_phys(address)) : 0, + .length = cpu_to_be32(length), + .control = cpu_to_be32(control) + }; + + dma = virt_to_phys(d); + + iowrite32be((u64)dma >> 32, fw_cfg_reg_dma); + iowrite32be(dma, fw_cfg_reg_dma + 4); + + fw_cfg_wait_for_control(d); + + if (be32_to_cpu(READ_ONCE(d->control)) & FW_CFG_DMA_CTL_ERROR) { + ret = -EIO; + } + +end: + kfree(d); + + return ret; +} + /* read chunk of given fw_cfg blob (caller responsible for sanity-check) */ static inline void fw_cfg_read_blob(u16 key, void *buf, loff_t pos, size_t count) @@ -103,6 +177,47 @@ static inline void fw_cfg_read_blob(u16 key, acpi_release_global_lock(glk); } +#ifdef CONFIG_CRASH_CORE +/* write chunk of given fw_cfg blob (caller responsible for sanity-check) */ +static ssize_t fw_cfg_write_blob(u16 key, +void *buf, loff_t pos, size_t count) +{ + u32 glk = -1U; + acpi_status status; + ssize_t ret = count; + + /* If we have ACPI, ensure mutual exclusion against any potential +* device access by the firmware, e.g. via AML methods: +*/ + status = acpi_acquire_global_lock(ACPI_WAIT_FOREVER, ); + if (ACPI_FAILURE(status) && status != AE_NOT_CONFIGURED) { + /* Should never get here */ + WARN(1, "%s: Failed to lock ACPI!\n", __func__); + return -EINVAL; + } + + mutex_lock(_cfg_dev_lock); + if (pos == 0) { + ret = fw_cfg_dma_transfer(buf, count, key << 16 + | FW_CFG_DMA_CTL_SELECT + | FW_CFG_DMA_CTL_WRITE); + } else { + iowrite16(fw_cfg_sel_endianness(key), fw_cfg_reg_ctrl); + ret = fw_cfg_dma_transfer(NULL, pos, FW
[PATCH v13 3/4] fw_cfg: write vmcoreinfo details
If the "etc/vmcoreinfo" fw_cfg file is present and we are not running the kdump kernel, write the addr/size of the vmcoreinfo ELF note. The DMA operation is expected to run synchronously with today qemu, but the specification states that it may become async, so we run "control" field check in a loop for eventual changes. Signed-off-by: Marc-André Lureau --- drivers/firmware/qemu_fw_cfg.c | 157 - 1 file changed, 154 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 740df0df2260..fd576ba7b337 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -33,6 +33,9 @@ #include #include #include +#include +#include +#include MODULE_AUTHOR("Gabriel L. Somlo "); MODULE_DESCRIPTION("QEMU fw_cfg sysfs support"); @@ -43,12 +46,24 @@ MODULE_LICENSE("GPL"); #define FW_CFG_ID 0x01 #define FW_CFG_FILE_DIR 0x19 +#define FW_CFG_VERSION_DMA 0x02 +#define FW_CFG_DMA_CTL_ERROR 0x01 +#define FW_CFG_DMA_CTL_READ0x02 +#define FW_CFG_DMA_CTL_SKIP0x04 +#define FW_CFG_DMA_CTL_SELECT 0x08 +#define FW_CFG_DMA_CTL_WRITE 0x10 + /* size in bytes of fw_cfg signature */ #define FW_CFG_SIG_SIZE 4 /* fw_cfg "file name" is up to 56 characters (including terminating nul) */ #define FW_CFG_MAX_FILE_PATH 56 +#define VMCOREINFO_FORMAT_ELF 0x1 + +/* fw_cfg revision attribute, in /sys/firmware/qemu_fw_cfg top-level dir. */ +static u32 fw_cfg_rev; + /* fw_cfg file directory entry type */ struct fw_cfg_file { u32 size; @@ -57,6 +72,12 @@ struct fw_cfg_file { char name[FW_CFG_MAX_FILE_PATH]; }; +struct fw_cfg_dma { + u32 control; + u32 length; + u64 address; +} __packed; + /* fw_cfg device i/o register addresses */ static bool fw_cfg_is_mmio; static phys_addr_t fw_cfg_p_base; @@ -75,6 +96,59 @@ static inline u16 fw_cfg_sel_endianness(u16 key) return fw_cfg_is_mmio ? cpu_to_be16(key) : cpu_to_le16(key); } +static inline bool fw_cfg_dma_enabled(void) +{ + return fw_cfg_rev & FW_CFG_VERSION_DMA && fw_cfg_reg_dma; +} + +/* qemu fw_cfg device is sync today, but spec says it may become async */ +static void fw_cfg_wait_for_control(struct fw_cfg_dma *d) +{ + do { + u32 ctrl = be32_to_cpu(READ_ONCE(d->control)); + + if ((ctrl & ~FW_CFG_DMA_CTL_ERROR) == 0) + return; + + usleep_range(50, 100); + } while (true); +} + +static ssize_t fw_cfg_dma_transfer(void *address, u32 length, u32 control) +{ + phys_addr_t dma; + struct fw_cfg_dma *d = NULL; + ssize_t ret = length; + + d = kmalloc(sizeof(*d), GFP_KERNEL); + if (!d) { + ret = -ENOMEM; + goto end; + } + + *d = (struct fw_cfg_dma) { + .address = address ? cpu_to_be64(virt_to_phys(address)) : 0, + .length = cpu_to_be32(length), + .control = cpu_to_be32(control) + }; + + dma = virt_to_phys(d); + + iowrite32be((u64)dma >> 32, fw_cfg_reg_dma); + iowrite32be(dma, fw_cfg_reg_dma + 4); + + fw_cfg_wait_for_control(d); + + if (be32_to_cpu(READ_ONCE(d->control)) & FW_CFG_DMA_CTL_ERROR) { + ret = -EIO; + } + +end: + kfree(d); + + return ret; +} + /* read chunk of given fw_cfg blob (caller responsible for sanity-check) */ static inline void fw_cfg_read_blob(u16 key, void *buf, loff_t pos, size_t count) @@ -103,6 +177,47 @@ static inline void fw_cfg_read_blob(u16 key, acpi_release_global_lock(glk); } +#ifdef CONFIG_CRASH_CORE +/* write chunk of given fw_cfg blob (caller responsible for sanity-check) */ +static ssize_t fw_cfg_write_blob(u16 key, +void *buf, loff_t pos, size_t count) +{ + u32 glk = -1U; + acpi_status status; + ssize_t ret = count; + + /* If we have ACPI, ensure mutual exclusion against any potential +* device access by the firmware, e.g. via AML methods: +*/ + status = acpi_acquire_global_lock(ACPI_WAIT_FOREVER, ); + if (ACPI_FAILURE(status) && status != AE_NOT_CONFIGURED) { + /* Should never get here */ + WARN(1, "%s: Failed to lock ACPI!\n", __func__); + return -EINVAL; + } + + mutex_lock(_cfg_dev_lock); + if (pos == 0) { + ret = fw_cfg_dma_transfer(buf, count, key << 16 + | FW_CFG_DMA_CTL_SELECT + | FW_CFG_DMA_CTL_WRITE); + } else { + iowrite16(fw_cfg_sel_endianness(key), fw_cfg_reg_ctrl); + ret = fw_cfg_dma_transfer(NULL, pos, FW_CFG_DMA_CTL_SKIP); + if (r